Beispiel #1
0
bool CNPC_Infected::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
{
	// required movement direction
	float flMoveYaw = UTIL_VecToYaw( move.dir );

	// FIXME: move this up to navigator so that path goals can ignore these overrides.
	Vector dir;
	float flInfluence = GetFacingDirection( dir );
	dir = move.facing * (1 - flInfluence) + dir * flInfluence;
	VectorNormalize( dir );

	// ideal facing direction
	float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );
		
	// FIXME: facing has important max velocity issues
	GetMotor()->SetIdealYawAndUpdate( idealYaw );	

	// find movement direction to compensate for not being turned far enough
	float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y );

	// Setup the 9-way blend parameters based on our speed and direction.
	Vector2D vCurMovePose( 0, 0 );

	vCurMovePose.x = cos( DEG2RAD( flDiff ) ) * 1.0f; //flPlaybackRate;
	vCurMovePose.y = -sin( DEG2RAD( flDiff ) ) * 1.0f; //flPlaybackRate;

	SetPoseParameter( gm_nMoveXPoseParam, vCurMovePose.x );
	SetPoseParameter( gm_nMoveYPoseParam, vCurMovePose.y );

	// ==== Update Lean pose parameters
	if ( gm_nLeanYawPoseParam >= 0 )
	{
		float targetLean = GetPoseParameter( gm_nMoveYPoseParam ) * 30.0f;
		float curLean = GetPoseParameter( gm_nLeanYawPoseParam );
		if( curLean < targetLean )
			curLean += MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*12.0f); //was 15.0f
		else
			curLean -= MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*12.0f); //was 15.0f
		SetPoseParameter( gm_nLeanYawPoseParam, curLean );
	}

	if( gm_nLeanPitchPoseParam >= 0 )
	{
		float targetLean = GetPoseParameter( gm_nMoveXPoseParam ) * -20.0f; //was -30.0f
		float curLean = GetPoseParameter( gm_nLeanPitchPoseParam );
		if( curLean < targetLean )
			curLean += MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*10.0f); //was 15.0f
		else
			curLean -= MIN(fabs(targetLean-curLean), GetAnimTimeInterval()*10.0f); //was 15.0f
		SetPoseParameter( gm_nLeanPitchPoseParam, curLean );
	}

	return true;
}
//-----------------------------------------------------------------------------
// Purpose: Draw box oriented to a Vector direction
//-----------------------------------------------------------------------------
void NDebugOverlay::BoxDirection(const Vector &origin, const Vector &mins, const Vector &maxs, const Vector &orientation, int r, int g, int b, int a, float duration)
{
	// convert forward vector to angles
	QAngle f_angles = vec3_angle;
	f_angles.y = UTIL_VecToYaw( orientation );

	BoxAngles( origin, mins, maxs, f_angles, r, g, b, a, duration );
}
void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move )
{
	if ( GetOuter()->OverrideMoveFacing( move, GetMoveInterval() ) )
		return;

	// required movement direction
	float flMoveYaw = UTIL_VecToYaw( move.dir );

	int nSequence = GetSequence();
	float fSequenceMoveYaw = GetSequenceMoveYaw( nSequence );
	if ( fSequenceMoveYaw == NOMOTION )
	{
		fSequenceMoveYaw = 0;
	}

	if (!HasPoseParameter( nSequence, "move_yaw" ))
	{
		SetIdealYawAndUpdate( UTIL_AngleMod( flMoveYaw - fSequenceMoveYaw ) );
	}
	else
	{
		// FIXME: move this up to navigator so that path goals can ignore these overrides.
		Vector dir;
		float flInfluence = GetFacingDirection( dir );
		dir = move.facing * (1 - flInfluence) + dir * flInfluence;
		VectorNormalize( dir );

		// ideal facing direction
		float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );
		
		// FIXME: facing has important max velocity issues
		SetIdealYawAndUpdate( idealYaw );	

		// find movement direction to compensate for not being turned far enough
		float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y );
		SetPoseParameter( "move_yaw", flDiff );
		/*
		if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
		{
			Msg( "move %.1f : diff %.1f  : ideal %.1f\n", flMoveYaw, flDiff, m_IdealYaw );
		}
		*/
	}
}
float CASW_Sentry_Top::GetYawTo(CBaseEntity* pEnt)
{
	if (!pEnt)
		return m_fDeployYaw;
	Vector diff = pEnt->WorldSpaceCenter() - GetAbsOrigin();
	if (diff.x == 0 && diff.y == 0 && diff.z == 0)
		return m_fDeployYaw;

	return UTIL_VecToYaw(diff);
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &vecTarget - 
// Output : float
//-----------------------------------------------------------------------------
float CNPC_Bug_Warrior::CalcIdealYaw( const Vector &vecTarget )
{
	//If we can see our enemy but not reach them, face them always
	if ( ( GetEnemy() != NULL ) && ( HasCondition( COND_SEE_ENEMY ) && HasCondition( COND_ENEMY_UNREACHABLE ) ) )
	{
		return UTIL_VecToYaw ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
	}

	return BaseClass::CalcIdealYaw( vecTarget );
}
bool CASW_Jump_Trigger::ReasonableJump(CASW_Alien_Jumper *pJumper, int iJumpNum)
{
	if (!pJumper)
		return false;

	if (m_bOneJumpPerAlien && pJumper->m_bTriggerJumped)
		return false;

	if (!pJumper->GetEnemy())	// let him jump if he has no enemy
		return true;

	if (!m_bCheckEnemyDirection)		// let him jump if he doesn't care about the direction of his enemy
		return true;

	// if he does have an enemy, check it's in the direction of the jump
	float fEnemyYaw = UTIL_VecToYaw(pJumper->GetEnemy()->GetAbsOrigin() - pJumper->GetAbsOrigin());
	float fJumpYaw = UTIL_VecToYaw(m_vecJumpDestination[iJumpNum] - pJumper->GetAbsOrigin());

	return fabs(UTIL_AngleDiff(fEnemyYaw, fJumpYaw)) < 90;
}
void CASW_Parasite::InfestColonist(CASW_Colonist* pColonist)
{
	if (m_bDefanged || !pColonist)	// no infesting if we've been defanged
		return;

	if (!IsOnFire())	// don't actually infest if we're on fire, since we'll die very shortly
		pColonist->BecomeInfested(this);

	// attach
	int attachment = pColonist->LookupAttachment( "chest" );
	if ( attachment )
	{
		//SetAbsAngles( GetOwnerEntity()->GetAbsAngles() );
		SetSolid( SOLID_NONE );
		SetMoveType( MOVETYPE_NONE );
		QAngle current(0,0,0);

		Vector diff = pColonist->GetAbsOrigin() - GetAbsOrigin();
		float angle = UTIL_VecToYaw(diff);
		angle -= pColonist->GetAbsAngles()[YAW];	// get the diff between our angle from the marine and the marine's facing;
		
		current = GetAbsAngles();
		
		SetParent( pColonist, attachment );
				Vector vecPosition;
		float fRaise = random->RandomFloat(0,20);
		
		SetLocalOrigin( Vector( -fRaise * 0.2f, 0, fRaise ) );
		SetLocalAngles( QAngle( 0, angle + asw_infest_angle.GetFloat(), 0 ) );
		// play our infesting anim
		if ( asw_parasite_inside.GetBool() )
		{
			SetActivity(ACT_RANGE_ATTACK2);
		}
		else
		{
			int iInfestAttack = LookupSequence("Infest_attack");
			if (GetSequence() != iInfestAttack)
			{
				ResetSequence(iInfestAttack);
			}
		}
		// don't do anymore thinking - need to think still to animate?
		AddFlag( FL_NOTARGET );
		SetThink( &CASW_Parasite::InfestThink );
		SetTouch( NULL );
		m_bInfesting = true;		
	}
	else
	{
		FinishedInfesting();
	}		
}
float CASW_Simple_Alien::GetIdealYaw()
{
	if (m_bMoving)
	{
		if (m_iState == ASW_SIMPLE_ALIEN_ATTACKING && GetEnemy()
			&& GetEnemy()->GetAbsOrigin().DistTo(GetAbsOrigin()) < GetFaceEnemyDistance())
		{
			return UTIL_VecToYaw(GetEnemy()->GetAbsOrigin() - GetAbsOrigin());
		}
		else
		{
			if (m_hMoveTarget.Get())
			{
				m_vecMoveTarget = m_hMoveTarget->GetAbsOrigin();
			}
			return UTIL_VecToYaw(m_vecMoveTarget - GetAbsOrigin());
		}
	}

	return GetAbsAngles()[YAW];
}
void CPropVehicleManhack::UpdateHead( void )
{
	float yaw = GetPoseParameter( "head_yaw" );
	float pitch = GetPoseParameter( "head_pitch" );

	// If we should be watching our enemy, turn our head
	CNPC_Manhack *pManhack=GetManhack();
	
	if (pManhack != NULL) 
	{
		Vector vehicleEyeOrigin;
		QAngle vehicleEyeAngles;
		GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles );

		// FIXME: cache this
		Vector vBodyDir;
		AngleVectors( vehicleEyeAngles, &vBodyDir );

		Vector	manhackDir = pManhack->GetAbsOrigin() - vehicleEyeOrigin;
		VectorNormalize( manhackDir );
		
		float angle = UTIL_VecToYaw( vBodyDir );
		float angleDiff = UTIL_VecToYaw( manhackDir );
		angleDiff = UTIL_AngleDiff( angleDiff, angle + yaw );

		SetPoseParameter( "head_yaw", UTIL_Approach( yaw + angleDiff, yaw, 1 ) );

		angle = UTIL_VecToPitch( vBodyDir );
		angleDiff = UTIL_VecToPitch( manhackDir );
		angleDiff = UTIL_AngleDiff( angleDiff, angle + pitch );

		SetPoseParameter( "head_pitch", UTIL_Approach( pitch + angleDiff, pitch, 1 ) );
	}
	else
	{
		// Otherwise turn the head back to its normal position
		SetPoseParameter( "head_yaw",	UTIL_Approach( 0, yaw, 10 ) );
		SetPoseParameter( "head_pitch", UTIL_Approach( 0, pitch, 10 ) );
	}
}
Beispiel #10
0
bool CZombie::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult )
{
	if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) )
	{
		if  ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin )
		{
			m_hBlockingDoor = pDoor;
			m_flDoorBashYaw = UTIL_VecToYaw( pMoveGoal->directTrace.vHitNormal * -1 );	
		}
		return true;
	}

	return false;
}
//-----------------------------------------------------------------------------
// Purpose: 
//
// Input  : iSequence - 
//
// Output : float - 
//-----------------------------------------------------------------------------
float CAnimating::GetSequenceMoveYaw( int iSequence )
{
	Vector				vecReturn;
	
	Assert( GetModelPtr() );
	::GetSequenceLinearMotion( GetModelPtr(), iSequence, GetPoseParameterArray(), &vecReturn );

	if (vecReturn.Length() > 0)
	{
		return UTIL_VecToYaw( vecReturn );
	}

	return NOMOTION;
}
//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_RollerDozer::RunTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{
	case TASK_ROLLERDOZER_CLEAR_DEBRIS:
		if( gpGlobals->curtime > m_flWaitFinished )
		{
			m_hDebris = NULL;
			m_flTimeDebrisSearch = gpGlobals->curtime;
			TaskComplete();
		}
		else if( m_hDebris != NULL )
		{
			float yaw = UTIL_VecToYaw( m_hDebris->GetLocalOrigin() - GetLocalOrigin() );
			Vector vecRight, vecForward;

			AngleVectors( QAngle( 0, yaw, 0 ), &vecForward, &vecRight, NULL );

			//Stop pushing if I'm going to push this object sideways or back towards the center of the cleanup area.
			Vector vecCleanupDir = m_hDebris->GetLocalOrigin() - m_vecCleanupPoint;
			VectorNormalize( vecCleanupDir );
			if( DotProduct( vecForward, vecCleanupDir ) < -0.5 )
			{
				// HACKHACK !!!HACKHACK - right now forcing an unstick. Do this better (sjb)

				// Clear the debris, suspend the search for debris, trick base class into unsticking me.
				m_hDebris = NULL;
				m_flTimeDebrisSearch = gpGlobals->curtime + 4;
				m_iFail = 10;
				TaskFail("Pushing Wrong Way");
			}

			m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, ROLLERDOZER_FORWARD_SPEED * 2 );
		}
		else
		{
			TaskFail("No debris!!");
		}

		break;

	default:
		BaseClass::RunTask( pTask );
		break;
	}
}
float CASW_Sentry_Top_Flamer::GetYawTo(CBaseEntity* pEnt)
{
	if (!pEnt)
		return m_fDeployYaw;

	Vector vEnemyVel = GetEnemyVelocity( pEnt );
	// pEnt->GetVelocity( &vEnemyVel );

	Vector vIdealAim = ProjectileIntercept( GetFiringPosition(), 
		GetProjectileVelocity(),
		pEnt->WorldSpaceCenter(), vEnemyVel );

	if ( vIdealAim.IsZero() )
		return m_fDeployYaw;
	else
		return UTIL_VecToYaw(vIdealAim.Normalized());
}
Beispiel #14
0
void CPathCorner :: Touch( CBaseEntity *pOther )
{
	entvars_t*		pevToucher = pOther->pev;
		
	if ( FBitSet ( pevToucher->flags, FL_MONSTER ) )
	{// monsters don't navigate path corners based on touch anymore
		return;
	}

	// If OTHER isn't explicitly looking for this path_corner, bail out
	if ( pOther->m_pGoalEnt != this )
	{
		return;
	}

	// If OTHER has an enemy, this touch is incidental, ignore
	if ( !FNullEnt(pevToucher->enemy) )
	{
		return;		// fighting, not following a path
	}
	
	// UNDONE: support non-zero flWait
	/*
	if (m_flWait != 0)
		ALERT(at_warning, "Non-zero path-cornder waits NYI");
	*/

	// Find the next "stop" on the path, make it the goal of the "toucher".
	if (FStringNull(pev->target))
	{
		ALERT(at_warning, "PathCornerTouch: no next stop specified");
	}

	pOther->m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->target) ) );

	// If "next spot" was not found (does not exist - level design error)
	if ( !pOther->m_pGoalEnt )
	{
		ALERT(at_console, "PathCornerTouch--%s couldn't find next stop in path: %s", STRING(pev->classname), STRING(pev->target));
		return;
	}

	// Turn towards the next stop in the path.
	pevToucher->ideal_yaw = UTIL_VecToYaw ( pOther->m_pGoalEnt->pev->origin - pevToucher->origin );
}
Beispiel #15
0
Vector& CASW_Simple_Alien::GetChaseDestination(CBaseEntity *pEnt)
{
	static Vector vecDest = vec3_origin;
	vecDest = pEnt->GetAbsOrigin();

	if (!pEnt)
		return vecDest;

	Vector vecDiff = vecDest - GetAbsOrigin();
	vecDiff.z = 0;
	float dist = vecDiff.Length2D();
    if (dist > GetZigZagChaseDistance())	// do we need to zig zag?
	{
		QAngle angSideways(0, UTIL_VecToYaw(vecDiff), 0);
		Vector vecForward, vecRight, vecUp;
		AngleVectors(angSideways, &vecForward, &vecRight, &vecUp);
		
        vecDest = GetAbsOrigin() + vecForward * 92.0f + vecRight * (random->RandomFloat() * 144 - 72);
	}
	return vecDest;
}
//------------------------------------------------------------------------------
// Purpose: routine called to start when a task initially starts
// Input  : pTask - the task structure
//------------------------------------------------------------------------------
void CAI_ASW_MeleeBehavior::StartTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{
		case TASK_MELEE_FLIP_AROUND:
			{
				Assert( 0 ); // NOT DONE.  See example in shield behavior: TASK_SHIELD_MAINTAIN_FLIP
#if 0
				bool	bNeedFlip = false;

				if ( HaveSequenceForActivity( ACT_SPINAROUND ) == true )
				{
					CBaseEntity *pTarget = GetEnemy();
					if ( pTarget )
					{
						float flFacing = UTIL_VecToYaw( pTarget->GetAbsOrigin() - GetAbsOrigin() );
						float flDiff = UTIL_AngleDiff( GetAbsAngles()[ YAW ], flFacing );
					}
				}

				if ( bNeedFlip == false )
				{
					TaskComplete();
				}
				else
				{
					GetOuter()->SetIdealActivity( ACT_SPINAROUND );
				}
#endif
			}
			break;

		default:
			BaseClass::StartTask( pTask );
			break;
	}
}
AIMoveResult_t CAI_LocalNavigator::MoveCalc( AILocalMoveGoal_t *pMoveGoal, bool bPreviouslyValidated )
{
	bool bOnlyCurThink = ( bPreviouslyValidated && !HaveObstacles() );

	AIMoveResult_t result = MoveCalcRaw( pMoveGoal, bOnlyCurThink );

	if ( pMoveGoal->curExpectedDist > pMoveGoal->maxDist )
		pMoveGoal->curExpectedDist = pMoveGoal->maxDist;

	// If success, try to dampen really fast turning movement
	if ( result == AIMR_OK)
	{
		float interval = GetOuter()->GetMotor()->GetMoveInterval();
		float currentYaw = UTIL_AngleMod( GetLocalAngles().y );
		float goalYaw;
		float deltaYaw;
		float speed;
		float clampedYaw;

		// Clamp yaw
		goalYaw = UTIL_VecToYaw( pMoveGoal->facing );
		deltaYaw = fabs( UTIL_AngleDiff( goalYaw, currentYaw ) );
		if ( deltaYaw > 15 )
		{
			speed = deltaYaw * 4.0; // i.e., any maneuver takes a quarter a second
			clampedYaw = AI_ClampYaw( speed, currentYaw, goalYaw, interval );

			if ( clampedYaw != goalYaw )
			{
				pMoveGoal->facing = UTIL_YawToVector( clampedYaw );
			}
		}
	}
	
	return result;
}
//=========================================================
// Start task - selects the correct activity and performs
// any necessary calculations to start the next task on the
// schedule. 
//=========================================================
void CBaseMonster :: StartTask ( Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_TURN_RIGHT:
		{
			float flCurrentYaw;
			
			flCurrentYaw = UTIL_AngleMod( pev->angles.y );
			pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw - pTask->flData );
			SetTurnActivity();
			break;
		}
	case TASK_TURN_LEFT:
		{
			float flCurrentYaw;
			
			flCurrentYaw = UTIL_AngleMod( pev->angles.y );
			pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw + pTask->flData );
			SetTurnActivity();
			break;
		}
	case TASK_REMEMBER:
		{
			Remember ( (int)pTask->flData );
			TaskComplete();
			break;
		}
	case TASK_FORGET:
		{
			Forget ( (int)pTask->flData );
			TaskComplete();
			break;
		}
	case TASK_FIND_HINTNODE:
		{
			m_iHintNode = FindHintNode();

			if ( m_iHintNode != NO_NODE )
			{
				TaskComplete();
			}
			else
			{
				TaskFail();
			}
			break;
		}
	case TASK_STORE_LASTPOSITION:
		{
			m_vecLastPosition = pev->origin;
			TaskComplete();
			break;
		}
	case TASK_CLEAR_LASTPOSITION:
		{
			m_vecLastPosition = g_vecZero;
			TaskComplete();
			break;
		}
	case TASK_CLEAR_HINTNODE:
		{
			m_iHintNode = NO_NODE;
			TaskComplete();
			break;
		}
	case TASK_STOP_MOVING:
		{
			if ( m_IdealActivity == m_movementActivity )
			{
				m_IdealActivity = GetStoppedActivity();
			}

			RouteClear();
			TaskComplete();
			break;
		}
	case TASK_PLAY_SEQUENCE_FACE_ENEMY:
	case TASK_PLAY_SEQUENCE_FACE_TARGET:
	case TASK_PLAY_SEQUENCE:
		{
			m_IdealActivity = ( Activity )( int )pTask->flData;
			break;
		}
	case TASK_PLAY_ACTIVE_IDLE:
		{
			// monsters verify that they have a sequence for the node's activity BEFORE
			// moving towards the node, so it's ok to just set the activity without checking here.
			m_IdealActivity = ( Activity )WorldGraph.m_pNodes[ m_iHintNode ].m_sHintActivity;
			break;
		}
	case TASK_SET_SCHEDULE:
		{
			Schedule_t *pNewSchedule;

			pNewSchedule = GetScheduleOfType( (int)pTask->flData );
			
			if ( pNewSchedule )
			{
				ChangeSchedule( pNewSchedule );
			}
			else
			{
				TaskFail();
			}

			break;
		}
	case TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY:
		{
			if ( m_hEnemy == NULL )
			{
				TaskFail();
				return;
			}

			if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, 0, pTask->flData ) )
			{
				// try for cover farther than the FLData from the schedule.
				TaskComplete();
			}
			else
			{
				// no coverwhatsoever.
				TaskFail();
			}
			break;
		}
	case TASK_FIND_FAR_NODE_COVER_FROM_ENEMY:
		{
			if ( m_hEnemy == NULL )
			{
				TaskFail();
				return;
			}

			if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, pTask->flData, CoverRadius() ) )
			{
				// try for cover farther than the FLData from the schedule.
				TaskComplete();
			}
			else
			{
				// no coverwhatsoever.
				TaskFail();
			}
			break;
		}
	case TASK_FIND_NODE_COVER_FROM_ENEMY:
		{
			if ( m_hEnemy == NULL )
			{
				TaskFail();
				return;
			}

			if ( FindCover( m_hEnemy->pev->origin, m_hEnemy->pev->view_ofs, 0, CoverRadius() ) )
			{
				// try for cover farther than the FLData from the schedule.
				TaskComplete();
			}
			else
			{
				// no coverwhatsoever.
				TaskFail();
			}
			break;
		}
	case TASK_FIND_COVER_FROM_ENEMY:
		{
			entvars_t *pevCover;

			if ( m_hEnemy == NULL )
			{
				// Find cover from self if no enemy available
				pevCover = pev;
//				TaskFail();
//				return;
			}
			else
				pevCover = m_hEnemy->pev;

			if ( FindLateralCover( pevCover->origin, pevCover->view_ofs ) )
			{
				// try lateral first
				m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
				TaskComplete();
			}
			else if ( FindCover( pevCover->origin, pevCover->view_ofs, 0, CoverRadius() ) )
			{
				// then try for plain ole cover
				m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
				TaskComplete();
			}
			else
			{
				// no coverwhatsoever.
				TaskFail();
			}
			break;
		}
	case TASK_FIND_COVER_FROM_ORIGIN:
		{
			if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) )
			{
				// then try for plain ole cover
				m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
				TaskComplete();
			}
			else
			{
				// no cover!
				TaskFail();
			}
		}
		break;
	case TASK_FIND_COVER_FROM_BEST_SOUND:
		{
			CSound *pBestSound;

			pBestSound = PBestSound();

			ASSERT( pBestSound != NULL );
			/*
			if ( pBestSound && FindLateralCover( pBestSound->m_vecOrigin, g_vecZero ) )
			{
				// try lateral first
				m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
				TaskComplete();
			}
			*/

			if ( pBestSound && FindCover( pBestSound->m_vecOrigin, g_vecZero, pBestSound->m_iVolume, CoverRadius() ) )
			{
				// then try for plain ole cover
				m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
				TaskComplete();
			}
			else
			{
				// no coverwhatsoever. or no sound in list
				TaskFail();
			}
			break;
		}
	case TASK_FACE_HINTNODE:
		{
			pev->ideal_yaw = WorldGraph.m_pNodes[ m_iHintNode ].m_flHintYaw;
			SetTurnActivity();
			break;
		}
	
	case TASK_FACE_LASTPOSITION:
		MakeIdealYaw ( m_vecLastPosition );
		SetTurnActivity(); 
		break;

	case TASK_FACE_TARGET:
		if ( m_hTargetEnt != NULL )
		{
			MakeIdealYaw ( m_hTargetEnt->pev->origin );
			SetTurnActivity(); 
		}
		else
			TaskFail();
		break;
	case TASK_FACE_ENEMY:
		{
			MakeIdealYaw ( m_vecEnemyLKP );
			SetTurnActivity(); 
			break;
		}
	case TASK_FACE_IDEAL:
		{
			SetTurnActivity();
			break;
		}
	case TASK_FACE_ROUTE:
		{
			if (FRouteClear())
			{
				ALERT(at_aiconsole, "No route to face!\n");
				TaskFail();
			}
			else
			{
				MakeIdealYaw(m_Route[m_iRouteIndex].vecLocation);
				SetTurnActivity();
			}
			break;
		}
	case TASK_WAIT_PVS:
	case TASK_WAIT_INDEFINITE:
		{
			// don't do anything.
			break;
		}
	case TASK_WAIT:
	case TASK_WAIT_FACE_ENEMY:
		{// set a future time that tells us when the wait is over.
			m_flWaitFinished = gpGlobals->time + pTask->flData;	
			break;
		}
	case TASK_WAIT_RANDOM:
		{// set a future time that tells us when the wait is over.
			m_flWaitFinished = gpGlobals->time + RANDOM_FLOAT( 0.1, pTask->flData );
			break;
		}
	case TASK_MOVE_TO_TARGET_RANGE:
		{
			if ( (m_hTargetEnt->pev->origin - pev->origin).Length() < 1 )
				TaskComplete();
			else
			{
				m_vecMoveGoal = m_hTargetEnt->pev->origin;
				if ( !MoveToTarget( ACT_WALK, 2 ) )
					TaskFail();
			}
			break;
		}
	case TASK_RUN_TO_SCRIPT:
	case TASK_WALK_TO_SCRIPT:
		{
			Activity newActivity;

			if ( !m_pGoalEnt || (m_pGoalEnt->pev->origin - pev->origin).Length() < 1 )
				TaskComplete();
			else
			{
				if ( pTask->iTask == TASK_WALK_TO_SCRIPT )
					newActivity = ACT_WALK;
				else
					newActivity = ACT_RUN;
				// This monster can't do this!
				if ( LookupActivity( newActivity ) == ACTIVITY_NOT_AVAILABLE )
					TaskComplete();
				else 
				{
					if ( m_pGoalEnt != NULL )
					{
						Vector vecDest;
						vecDest = m_pGoalEnt->pev->origin;

						if ( !MoveToLocation( newActivity, 2, vecDest ) )
						{
							TaskFail();
							ALERT( at_aiconsole, "%s Failed to reach script!!!\n", STRING(pev->classname) );
							RouteClear();
						}
					}
					else
					{
						TaskFail();
						ALERT( at_aiconsole, "%s: MoveTarget is missing!?!\n", STRING(pev->classname) );
						RouteClear();
					}
				}
			}
			TaskComplete();
			break;
		}
	case TASK_CLEAR_MOVE_WAIT:
		{
			m_flMoveWaitFinished = gpGlobals->time;
			TaskComplete();
			break;
		}
	case TASK_MELEE_ATTACK1_NOTURN:
	case TASK_MELEE_ATTACK1:
		{
			m_IdealActivity = ACT_MELEE_ATTACK1;
			break;
		}
	case TASK_MELEE_ATTACK2_NOTURN:
	case TASK_MELEE_ATTACK2:
		{
			m_IdealActivity = ACT_MELEE_ATTACK2;
			break;
		}
	case TASK_RANGE_ATTACK1_NOTURN:
	case TASK_RANGE_ATTACK1:
		{
			m_IdealActivity = ACT_RANGE_ATTACK1;
			break;
		}
	case TASK_RANGE_ATTACK2_NOTURN:
	case TASK_RANGE_ATTACK2:
		{
			m_IdealActivity = ACT_RANGE_ATTACK2;
			break;
		}
	case TASK_RELOAD_NOTURN:
	case TASK_RELOAD:
		{
			m_IdealActivity = ACT_RELOAD;
			break;
		}
	case TASK_SPECIAL_ATTACK1:
		{
			m_IdealActivity = ACT_SPECIAL_ATTACK1;
			break;
		}
	case TASK_SPECIAL_ATTACK2:
		{
			m_IdealActivity = ACT_SPECIAL_ATTACK2;
			break;
		}
	case TASK_SET_ACTIVITY:
		{
			m_IdealActivity = (Activity)(int)pTask->flData;
			TaskComplete();
			break;
		}
	case TASK_GET_PATH_TO_ENEMY_LKP:
		{
			if ( BuildRoute ( m_vecEnemyLKP, bits_MF_TO_LOCATION, NULL ) )
			{
				TaskComplete();
			}
			else if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, 0, (m_vecEnemyLKP - pev->origin).Length() ))
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_ENEMY:
		{
			CBaseEntity *pEnemy = m_hEnemy;

			if ( pEnemy == NULL )
			{
				TaskFail();
				return;
			}

			if ( BuildRoute ( pEnemy->pev->origin, bits_MF_TO_ENEMY, pEnemy ) )
			{
				TaskComplete();
			}
			else if (BuildNearestRoute( pEnemy->pev->origin, pEnemy->pev->view_ofs, 0, (pEnemy->pev->origin - pev->origin).Length() ))
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_ENEMY_CORPSE:
		{
			UTIL_MakeVectors( pev->angles );
			if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 64, bits_MF_TO_LOCATION, NULL ) )
			{
				TaskComplete();
			}
			else
			{
				ALERT ( at_aiconsole, "GetPathToEnemyCorpse failed!!\n" );
				TaskFail();
			}
		}
		break;
	case TASK_GET_PATH_TO_SPOT:
		{
			CBaseEntity *pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
			if ( BuildRoute ( m_vecMoveGoal, bits_MF_TO_LOCATION, pPlayer ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" );
				TaskFail();
			}
			break;
		}

	case TASK_GET_PATH_TO_TARGET:
		{
			RouteClear();
			if ( m_hTargetEnt != NULL && MoveToTarget( m_movementActivity, 1 ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_SCRIPT:
		{
			RouteClear();
			if ( m_pCine != NULL && MoveToLocation( m_movementActivity, 1, m_pCine->pev->origin ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_HINTNODE:// for active idles!
		{
			if ( MoveToLocation( m_movementActivity, 2, WorldGraph.m_pNodes[ m_iHintNode ].m_vecOrigin ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToHintNode failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_LASTPOSITION:
		{
			m_vecMoveGoal = m_vecLastPosition;

			if ( MoveToLocation( m_movementActivity, 2, m_vecMoveGoal ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToLastPosition failed!!\n" );
				TaskFail();
			}
			break;
		}
	case TASK_GET_PATH_TO_BESTSOUND:
		{
			CSound *pSound;

			pSound = PBestSound();

			if ( pSound && MoveToLocation( m_movementActivity, 2, pSound->m_vecOrigin ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToBestSound failed!!\n" );
				TaskFail();
			}
			break;
		}
case TASK_GET_PATH_TO_BESTSCENT:
		{
			CSound *pScent;

			pScent = PBestScent();

			if ( pScent && MoveToLocation( m_movementActivity, 2, pScent->m_vecOrigin ) )
			{
				TaskComplete();
			}
			else
			{
				// no way to get there =(
				ALERT ( at_aiconsole, "GetPathToBestScent failed!!\n" );
				
				TaskFail();
			}
			break;
		}
	case TASK_RUN_PATH:
		{
			// UNDONE: This is in some default AI and some monsters can't run? -- walk instead?
			if ( LookupActivity( ACT_RUN ) != ACTIVITY_NOT_AVAILABLE )
			{
				m_movementActivity = ACT_RUN;
			}
			else
			{
				m_movementActivity = ACT_WALK;
			}
			TaskComplete();
			break;
		}
	case TASK_WALK_PATH:
		{
			if ( pev->movetype == MOVETYPE_FLY )
			{
				m_movementActivity = ACT_FLY;
			}
			if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE )
			{
				m_movementActivity = ACT_WALK;
			}
			else
			{
				m_movementActivity = ACT_RUN;
			}
			TaskComplete();
			break;
		}
	case TASK_STRAFE_PATH:
		{
			Vector2D	vec2DirToPoint; 
			Vector2D	vec2RightSide;

			// to start strafing, we have to first figure out if the target is on the left side or right side
			UTIL_MakeVectors ( pev->angles );

			vec2DirToPoint = ( m_Route[ 0 ].vecLocation - pev->origin ).Make2D().Normalize();
			vec2RightSide = gpGlobals->v_right.Make2D().Normalize();

			if ( DotProduct ( vec2DirToPoint, vec2RightSide ) > 0 )
			{
				// strafe right
				m_movementActivity = ACT_STRAFE_RIGHT;
			}
			else
			{
				// strafe left
				m_movementActivity = ACT_STRAFE_LEFT;
			}
			TaskComplete();
			break;
		}


	case TASK_WAIT_FOR_MOVEMENT:
		{
			if (FRouteClear())
			{
				TaskComplete();
			}
			break;
		}

	case TASK_EAT:
		{
			Eat( pTask->flData );
			TaskComplete();
			break;
		}
	case TASK_SMALL_FLINCH:
		{
			m_IdealActivity = GetSmallFlinchActivity();
			break;
		}
	case TASK_DIE:
		{
			RouteClear();	
			
			m_IdealActivity = GetDeathActivity();

			pev->deadflag = DEAD_DYING;
			break;
		}
	case TASK_SOUND_WAKE:
		{
			AlertSound();
			TaskComplete();
			break;
		}
	case TASK_SOUND_DIE:
		{
			DeathSound();
			TaskComplete();
			break;
		}
	case TASK_SOUND_IDLE:
		{
			IdleSound();
			TaskComplete();
			break;
		}
	case TASK_SOUND_PAIN:
		{
			PainSound();
			TaskComplete();
			break;
		}
	case TASK_SOUND_DEATH:
		{
			DeathSound();
			TaskComplete();
			break;
		}
	case TASK_SOUND_ANGRY:
		{
			// sounds are complete as soon as we get here, cause we've already played them.
			ALERT ( at_aiconsole, "SOUND\n" );			
			TaskComplete();
			break;
		}
	case TASK_WAIT_FOR_SCRIPT:
		{
			if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime )
			{
				TaskComplete(); //LRC - start playing immediately
			}
			else if (!m_pCine->IsAction() && m_pCine->m_iszIdle)
			{
				m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszIdle, FALSE );
				if (FStrEq( STRING(m_pCine->m_iszIdle), STRING(m_pCine->m_iszPlay)))
				{
					pev->framerate = 0;
				}
			}
			else
				m_IdealActivity = ACT_IDLE;

			break;
		}
	case TASK_PLAY_SCRIPT:
		{
			if (m_pCine->IsAction())
			{
				//ALERT(at_console,"PlayScript: setting idealactivity %d\n",m_pCine->m_fAction);
				switch(m_pCine->m_fAction)
				{
				case 0:
					m_IdealActivity = ACT_RANGE_ATTACK1; break;
				case 1:
					m_IdealActivity = ACT_RANGE_ATTACK2; break;
				case 2:
					m_IdealActivity = ACT_MELEE_ATTACK1; break;
				case 3:
					m_IdealActivity = ACT_MELEE_ATTACK2; break;
				case 4:
					m_IdealActivity = ACT_SPECIAL_ATTACK1; break;
				case 5:
					m_IdealActivity = ACT_SPECIAL_ATTACK2; break;
				case 6:
					m_IdealActivity = ACT_RELOAD; break;
				case 7:
					m_IdealActivity = ACT_HOP; break;
				}
				pev->framerate = 1.0; // shouldn't be needed, but just in case
				pev->movetype = MOVETYPE_FLY;
				ClearBits(pev->flags, FL_ONGROUND);
			}
			else
			{
				m_pCine->StartSequence( (CBaseMonster *)this, m_pCine->m_iszPlay, TRUE );
				if ( m_fSequenceFinished )
					ClearSchedule();
				pev->framerate = 1.0;
				//ALERT( at_aiconsole, "Script %s has begun for %s\n", STRING( m_pCine->m_iszPlay ), STRING(pev->classname) );
			}
			m_scriptState = SCRIPT_PLAYING;
			break;
		}
	case TASK_ENABLE_SCRIPT:
		{
			m_pCine->DelayStart( 0 );
			TaskComplete();
			break;
		}
//LRC
	case TASK_END_SCRIPT:
		{
			m_pCine->SequenceDone( this );
			TaskComplete();
			break;
		}
	case TASK_PLANT_ON_SCRIPT:
		{
			if ( m_pCine != NULL )
			{
				// Plant on script
				// LRC - if it's a teleport script, do the turn too
				if (m_pCine->m_fMoveTo == 4 || m_pCine->m_fMoveTo == 6)
				{
					if (m_pCine->m_fTurnType == 0) //LRC
						pev->angles.y = m_hTargetEnt->pev->angles.y;
					else if (m_pCine->m_fTurnType == 1)
						pev->angles.y = UTIL_VecToYaw(m_hTargetEnt->pev->origin - pev->origin);
					pev->ideal_yaw = pev->angles.y;
					pev->avelocity = Vector( 0, 0, 0 );
					pev->velocity = Vector( 0, 0, 0 );
					pev->effects |= EF_NOINTERP;
				}

				if (m_pCine->m_fMoveTo != 6)
					pev->origin = m_pGoalEnt->pev->origin;
			}

			TaskComplete();
			break;
		}
	case TASK_FACE_SCRIPT:
		{
			if ( m_pCine != NULL && m_pCine->m_fMoveTo != 0) // movetype "no move" makes us ignore turntype
			{
				switch (m_pCine->m_fTurnType)
				{
				case 0:
					pev->ideal_yaw = UTIL_AngleMod( m_pCine->pev->angles.y );
					break;
				case 1:
					// yes, this is inconsistent- turn to face uses the "target" and turn to angle uses the "cine".
					if (m_hTargetEnt)
						MakeIdealYaw ( m_hTargetEnt->pev->origin );
					else
						MakeIdealYaw ( m_pCine->pev->origin );
					break;
				// default: don't turn
				}
			}

			TaskComplete();
			m_IdealActivity = ACT_IDLE;
			RouteClear();
			break;
		}
	case TASK_SUGGEST_STATE:
		{
			m_IdealMonsterState = (MONSTERSTATE)(int)pTask->flData;
			TaskComplete();
			break;
		}

	case TASK_SET_FAIL_SCHEDULE:
		m_failSchedule = (int)pTask->flData;
		TaskComplete();
		break;

	case TASK_CLEAR_FAIL_SCHEDULE:
		m_failSchedule = SCHED_NONE;
		TaskComplete();
		break;

	default:
		{
			ALERT ( at_aiconsole, "No StartTask entry for %d\n", (SHARED_TASKS)pTask->iTask );
			break;
		}
	}
}
//=========================================================
// RunTask 
//=========================================================
void CBaseMonster :: RunTask ( Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_TURN_RIGHT:
	case TASK_TURN_LEFT:
		{
			ChangeYaw( pev->yaw_speed );

			if ( FacingIdeal() )
			{
				TaskComplete();
			}
			break;
		}

	case TASK_PLAY_SEQUENCE_FACE_ENEMY:
	case TASK_PLAY_SEQUENCE_FACE_TARGET:
		{
			CBaseEntity *pTarget;

			if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET )
				pTarget = m_hTargetEnt;
			else
				pTarget = m_hEnemy;
			if ( pTarget )
			{
				pev->ideal_yaw = UTIL_VecToYaw( pTarget->pev->origin - pev->origin );
				ChangeYaw( pev->yaw_speed );
			}
			if ( m_fSequenceFinished )
				TaskComplete();
		}
		break;

	case TASK_PLAY_SEQUENCE:
	case TASK_PLAY_ACTIVE_IDLE:
		{
			if ( m_fSequenceFinished )
			{
				TaskComplete();
			}
			break;
		}


	case TASK_FACE_ENEMY:
		{
			MakeIdealYaw( m_vecEnemyLKP );

			ChangeYaw( pev->yaw_speed );

			if ( FacingIdeal() )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_FACE_HINTNODE:
	case TASK_FACE_LASTPOSITION:
	case TASK_FACE_TARGET:
	case TASK_FACE_IDEAL:
	case TASK_FACE_ROUTE:
		{
			ChangeYaw( pev->yaw_speed );

			if ( FacingIdeal() )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_WAIT_PVS:
		{
			if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_WAIT_INDEFINITE:
		{
			// don't do anything.
			break;
		}
	case TASK_WAIT:
	case TASK_WAIT_RANDOM:
		{
			if ( gpGlobals->time >= m_flWaitFinished )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_WAIT_FACE_ENEMY:
		{
			MakeIdealYaw ( m_vecEnemyLKP );
			ChangeYaw( pev->yaw_speed ); 

			if ( gpGlobals->time >= m_flWaitFinished )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_MOVE_TO_TARGET_RANGE:
		{
			float distance;

			if ( m_hTargetEnt == NULL )
				TaskFail();
			else
			{
				distance = ( m_vecMoveGoal - pev->origin ).Length2D();
				// Re-evaluate when you think your finished, or the target has moved too far
				if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->pev->origin).Length() > pTask->flData * 0.5 )
				{
					m_vecMoveGoal = m_hTargetEnt->pev->origin;
					distance = ( m_vecMoveGoal - pev->origin ).Length2D();
					FRefreshRoute();
				}

				// Set the appropriate activity based on an overlapping range
				// overlap the range to prevent oscillation
				if ( distance < pTask->flData )
				{
					TaskComplete();
					RouteClear();		// Stop moving
				}
				else if ( distance < 190 && m_movementActivity != ACT_WALK )
					m_movementActivity = ACT_WALK;
				else if ( distance >= 270 && m_movementActivity != ACT_RUN )
					m_movementActivity = ACT_RUN;
			}

			break;
		}
	case TASK_WAIT_FOR_MOVEMENT:
		{
			if (MovementIsComplete())
			{
				TaskComplete();
				RouteClear();		// Stop moving
			}
			break;
		}
	case TASK_DIE:
		{
			if ( m_fSequenceFinished && pev->frame >= 255 )
			{
				pev->deadflag = DEAD_DEAD;
				
				SetThink ( NULL );
				StopAnimation();

				if ( !BBoxFlat() )
				{
					// a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will
					// block the player on a slope or stairs, the corpse is made nonsolid. 
//					pev->solid = SOLID_NOT;
					UTIL_SetSize ( pev, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) );
				}
				else // !!!HACKHACK - put monster in a thin, wide bounding box until we fix the solid type/bounding volume problem
					UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + 1 ) );

				if ( ShouldFadeOnDeath() )
				{
					// this monster was created by a monstermaker... fade the corpse out.
					SUB_StartFadeOut();
				}
				else
				{
					// body is gonna be around for a while, so have it stink for a bit.
					CSoundEnt::InsertSound ( bits_SOUND_CARCASS, pev->origin, 384, 30 );
				}
			}
			break;
		}
	case TASK_RANGE_ATTACK1_NOTURN:
	case TASK_MELEE_ATTACK1_NOTURN:
	case TASK_MELEE_ATTACK2_NOTURN:
	case TASK_RANGE_ATTACK2_NOTURN:
	case TASK_RELOAD_NOTURN:
		{
			if ( m_fSequenceFinished )
			{
				m_Activity = ACT_RESET;
				TaskComplete();
			}
			break;
		}
	case TASK_RANGE_ATTACK1:
	case TASK_MELEE_ATTACK1:
	case TASK_MELEE_ATTACK2:
	case TASK_RANGE_ATTACK2:
	case TASK_SPECIAL_ATTACK1:
	case TASK_SPECIAL_ATTACK2:
	case TASK_RELOAD:
		{
			MakeIdealYaw ( m_vecEnemyLKP );
			ChangeYaw ( pev->yaw_speed );

			if ( m_fSequenceFinished )
			{
				m_Activity = ACT_RESET;
				TaskComplete();
			}
			break;
		}
	case TASK_SMALL_FLINCH:
		{
			if ( m_fSequenceFinished )
			{
				TaskComplete();
			}
		}
		break;
	case TASK_WAIT_FOR_SCRIPT:
		{
			if ( m_pCine->m_iDelay <= 0 && gpGlobals->time >= m_pCine->m_startTime )
			{
				TaskComplete();
			}
			break;
		}
	case TASK_PLAY_SCRIPT:
		{
//			ALERT(at_console, "Play Script\n");
			if (m_fSequenceFinished)
			{
//				ALERT(at_console, "Anim Finished\n");
				if (m_pCine->m_iRepeatsLeft > 0)
				{
//					ALERT(at_console, "Frame %f; Repeat %d from %f\n", pev->frame, m_pCine->m_iRepeatsLeft, m_pCine->m_fRepeatFrame);
					m_pCine->m_iRepeatsLeft--;
					pev->frame = m_pCine->m_fRepeatFrame;
					ResetSequenceInfo( );
				}
				else
				{
					TaskComplete();
				}
			}
			break;
		}
	}
}
//-----------------------------------------------------------------------------
bool CAI_PlaneSolver::GenerateCircleObstacleSuggestions( const AILocalMoveGoal_t &moveGoal, float probeDist )
{
	bool result = false;
	Vector npcLoc = m_pNpc->WorldSpaceCenter();
	Vector mins, maxs;

	m_pNpc->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
	float radiusNpc = (mins.AsVector2D() - maxs.AsVector2D()).Length() * 0.5;
	
	for ( int i = 0; i < m_Obstacles.Count(); i++ )
	{
		CBaseEntity *pObstacleEntity = NULL;

		float zDistTooFar;
		if ( m_Obstacles[i].hEntity && m_Obstacles[i].hEntity->CollisionProp() )
		{
			pObstacleEntity = m_Obstacles[i].hEntity.Get();

			if( pObstacleEntity == moveGoal.pMoveTarget && (pObstacleEntity->IsNPC() || pObstacleEntity->IsPlayer()) )
			{
				// HEY! I'm trying to avoid the very thing I'm trying to get to. This will make we wobble like a drunk as I approach. Don't do it.
				continue;
			}

			pObstacleEntity->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
			zDistTooFar = ( maxs.z - mins.z ) * 0.5 + GetNpc()->GetHullHeight() * 0.5;
		}
		else
			zDistTooFar = GetNpc()->GetHullHeight();
			
		if ( fabs( m_Obstacles[i].center.z - npcLoc.z ) > zDistTooFar )
			continue;

		Vector vecToNpc 		= npcLoc - m_Obstacles[i].center;
		vecToNpc.z = 0;
		float distToObstacleSq 	= sq(vecToNpc.x) + sq(vecToNpc.y);
		float radius = m_Obstacles[i].radius + radiusNpc;

		if ( distToObstacleSq > 0.001 && distToObstacleSq < sq( radius + probeDist ) )
		{
			Vector vecToObstacle = vecToNpc * -1;
			float distToObstacle = VectorNormalize( vecToObstacle );
			float weight;
			float arc;
			float radiusSq = sq(radius);

			float flDot = DotProduct( vecToObstacle, moveGoal.dir );

			// Don't steer around to avoid obstacles we've already passed, unless we're right up against them.
			// That is, do this computation without the probeDist added in.
			if( flDot < 0.0f && distToObstacleSq > radiusSq )
			{
				continue;
			}

			if ( radiusSq < distToObstacleSq )
			{
				Vector vecTangent;
				float distToTangent = FastSqrt( distToObstacleSq - radiusSq );

				float oneOverDistToObstacleSq = 1 / distToObstacleSq;

				vecTangent.x = ( -distToTangent * vecToNpc.x + radius * vecToNpc.y ) * oneOverDistToObstacleSq;
				vecTangent.y = ( -distToTangent * vecToNpc.y - radius * vecToNpc.x ) * oneOverDistToObstacleSq;
				vecTangent.z = 0;

				float cosHalfArc = vecToObstacle.Dot( vecTangent );
				arc = RAD2DEG(acosf( cosHalfArc )) * 2.0;
				weight = 1.0 - (distToObstacle - radius) / probeDist;
				if ( weight > 0.75 )
					arc += (arc * 0.5) * (weight - 0.75) / 0.25;
				
				Assert( weight >= 0.0 && weight <= 1.0 );

#if DEBUG_OBSTACLES
				// -------------------------
				Msg( "Adding arc %f, w %f\n", arc, weight );

				Vector pointTangent = npcLoc + ( vecTangent * distToTangent );
					
				NDebugOverlay::Line( npcLoc - Vector( 0, 0, 64 ), npcLoc + Vector(0,0,64), 0,255,0, false, 0.1 );
				NDebugOverlay::Line( center - Vector( 0, 0, 64 ), center + Vector(0,0,64), 0,255,0, false, 0.1 );
				NDebugOverlay::Line( pointTangent - Vector( 0, 0, 64 ), pointTangent + Vector(0,0,64), 0,255,0, false, 0.1 );
				
				NDebugOverlay::Line( npcLoc + Vector(0,0,64), center + Vector(0,0,64), 0,0,255, false, 0.1 );
				NDebugOverlay::Line( center + Vector(0,0,64), pointTangent + Vector(0,0,64), 0,0,255, false, 0.1 );
				NDebugOverlay::Line( pointTangent + Vector(0,0,64), npcLoc + Vector(0,0,64), 0,0,255, false, 0.1 );
#endif
			}
			else
			{
				arc = 210;
				weight = 1.0;
			}

			if ( m_Obstacles[i].hEntity != NULL )
			{
				weight = AdjustRegulationWeight( m_Obstacles[i].hEntity, weight );
			}
			
			AI_MoveSuggestion_t suggestion( m_Obstacles[i].type, weight, UTIL_VecToYaw(vecToObstacle), arc );
			m_Solver.AddRegulation( suggestion );
			result = true;
		}
	}
	
	m_Obstacles.RemoveAll();
	return result;

}
bool CAI_PlaneSolver::RunMoveSolver( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, float degreesPositiveArc, 
									 bool fDeterOscillation, Vector *pResult )
{
	PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_RunMoveSolver );
		
	AI_MoveSolution_t solution;
	
	if ( m_Solver.HaveRegulations() )
	{
		// @TODO (toml 07-19-02): add a movement threshhold here (the target may be the same,
		// but the ai is nowhere near where the last solution was derived)
		bool fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target );
		
		// For debugging, visualize our regulations
		VisualizeRegulations();

		AI_MoveSuggestion_t moveSuggestions[2];
		int					nSuggestions = 1;

		moveSuggestions[0].Set( AIMST_MOVE, 1, UTIL_VecToYaw( goal.dir ), degreesPositiveArc );
		moveSuggestions[0].flags |= ComputeTurnBiasFlags( goal, directTrace );

		if ( fDeterOscillation && !fNewTarget )
		{
#ifndef TESTING_SUGGESTIONS
			moveSuggestions[nSuggestions++].Set( AIMST_OSCILLATION_DETERRANCE, 1, m_PrevSolution - 180, 180 );
#endif
		}

		if ( m_Solver.Solve( moveSuggestions, nSuggestions, &solution ) )
		{
			*pResult = UTIL_YawToVector( solution.dir );

			if (goal.navType == NAV_FLY)
			{
				// FIXME: Does the z component have to occur during the goal
				// setting because it's there & only there where MoveLimit
				// will report contact with the world if we move up?
				AdjustSolutionForFliers( goal, solution.dir, pResult );
			}
			// A crude attempt at oscillation detection: if we solved last time, and this time, and the same target is
			// involved, and we resulted in nearly a 180, we are probably oscillating
#ifndef TESTING_SUGGESTIONS
			if ( !fNewTarget )
			{
				float delta = solution.dir - m_PrevSolution;
				if ( delta < 0 )
					delta += 360;
				if ( delta > 165 && delta < 195 )
					return false;
			}
#endif
			m_PrevSolution = solution.dir;
			m_PrevSolutionVector = *pResult;

			Vector curVelocity = m_pNpc->GetSmoothedVelocity();
			if ( curVelocity != vec3_origin )
			{
				VectorNormalize( curVelocity );
				if ( !fNewTarget )
				{
					*pResult = curVelocity * 0.1 + m_PrevSolutionVector * 0.1 + *pResult * 0.8;
				}
				else
				{
					*pResult = curVelocity * 0.2 + *pResult * 0.8;
				}
			}

			return true;
		}
	}
	else
	{
		if (goal.navType != NAV_FLY)
		{
			*pResult = goal.dir;
		}
		else
		{
			VectorSubtract( goal.target, GetLocalOrigin(), *pResult );
			VectorNormalize( *pResult );
		}
		return true;
	}
	
	return false;
}
AI_SuggestorResult_t CAI_PlaneSolver::GenerateObstacleSuggestions( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, 
																   float distClear, float probeDist, float degreesToProbe, int nProbes )
{
	Assert( nProbes % 2 == 1 );
	
	PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_GenerateObstacleSuggestions );
	
	AI_SuggestorResult_t seekResult = SR_NONE;
	bool				 fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target );
	
	if ( fNewTarget )
		m_RefreshSamplesTimer.Force();

	if ( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] == 0.0 || m_RefreshSamplesTimer.Expired() )
	{
		m_Solver.ClearRegulations();
	
		if ( !ProbeForNpcs() )
			GenerateObstacleNpcs( goal, probeDist );
			
		if ( GenerateCircleObstacleSuggestions( goal, probeDist ) )
			seekResult = SR_OK;
		
		float spanPerProbe = degreesToProbe / nProbes;
		int   nSideProbes  = (nProbes - 1) / 2;
		float yawGoalDir   = UTIL_VecToYaw( goal.dir );
		
		Vector 		  probeTarget;
		AIMoveTrace_t moveTrace;
		int			  i;
		
		// Generate suggestion from direct trace, or probe if direct trace doesn't match
		if ( fabs( probeDist - ( distClear + directTrace.flDistObstructed ) ) < 0.1 && 
			 ( ProbeForNpcs() || directTrace.fStatus != AIMR_BLOCKED_NPC ) )
		{
			if ( directTrace.fStatus != AIMR_OK )
			{
				seekResult = SR_OK;
				GenerateSuggestionFromTrace( goal, directTrace, probeDist, yawGoalDir, spanPerProbe, 0 );
			}
		}
		else if ( GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, spanPerProbe, 0 ) == SR_OK )
		{
			seekResult = SR_OK;
		}
		
		// Scan left. Note that in the left and right scan, the algorithm stops as soon
		// as there is a clear path. This is an optimization in anticipation of the
		// behavior of the underlying solver. This will break more often the higher
		// PLANE_SOLVER_THINK_FREQUENCY becomes
		bool foundClear = false;

		for ( i = 1; i <= nSideProbes; i++ )
		{
			if ( !foundClear )
			{
				AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, 
																				 spanPerProbe, i );
				if ( curSeekResult == SR_OK )
				{
					seekResult = SR_OK;
				}
				else
					foundClear = true;
			}
			else
			{
				float ignored;
				float arcCenter;
				CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter );
				m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) );
			}
		}

		// Scan right
		foundClear = false;

		for ( i = -1; i >= -nSideProbes; i-- )
		{
			if ( !foundClear )
			{
				AI_SuggestorResult_t curSeekResult = GenerateObstacleSuggestion( goal, yawGoalDir, probeDist, 
																				 spanPerProbe, i );
				if ( curSeekResult == SR_OK )
				{
					seekResult = SR_OK;
				}
				else
					foundClear = true;
			}
			else
			{
				float ignored;
				float arcCenter;
				CalcYawsFromOffset( yawGoalDir, spanPerProbe, i, &ignored, &arcCenter );
				m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, spanPerProbe ) );
			}
		}

		if ( seekResult == SR_OK )
		{
			float arcCenter = yawGoalDir - 180;
			if ( arcCenter < 0 )
				arcCenter += 360;
				
			// Since these are not sampled every think, place a negative arc in all directions not sampled
			m_Solver.AddRegulation( AI_MoveSuggestion_t( AIMST_NO_KNOWLEDGE, 1, arcCenter, 360 - degreesToProbe ) );

		}

		m_RefreshSamplesTimer.Reset( PLANE_SOLVER_THINK_FREQUENCY[AIStrongOpt()] );
	}
	else if ( m_Solver.HaveRegulations() )
		seekResult = SR_OK;

	return seekResult;
}
void CASW_Parasite::InfestMarine(CASW_Marine* pMarine)
{
	if ( !pMarine )	
		return;

	pMarine->BecomeInfested(this);

	// attach
	int attachment = pMarine->LookupAttachment( "chest" );
	if ( attachment )
	{
		SetSolid( SOLID_NONE );
		SetMoveType( MOVETYPE_NONE );
		QAngle current(0,0,0);

		Vector diff = pMarine->GetAbsOrigin() - GetAbsOrigin();
		float angle = UTIL_VecToYaw(diff);
		angle -= pMarine->GetAbsAngles()[YAW];	// get the diff between our angle from the marine and the marine's facing;
		
		current = GetAbsAngles();

		Vector vAttachmentPos;
		pMarine->GetAttachment( attachment, vAttachmentPos );

		// Make sure it's near the chest attachement before parenting
		Teleport( &vAttachmentPos, &vec3_angle, &vec3_origin );
		
		SetParent( pMarine, attachment );

		float flRaise = RandomFloat( 15.0f, 18.0f );
		float flForward = RandomFloat( -3.0f, 0.0f );
		float flSide = RandomFloat( 1.75f, 3.0f ) * ( RandomInt( 0, 1 ) == 0 ? 1.0f : -1.0f );

		if ( asw_debug_alien_damage.GetBool() )
		{
			Msg( "INFEST: flRaise = %f flForward = %f flSide = %f yaw = %f\n", flRaise, flForward, flSide, angle + asw_infest_angle.GetFloat() );
		}
		SetLocalOrigin( Vector( flForward, flSide, flRaise ) );
		SetLocalAngles( QAngle( asw_infest_pitch.GetFloat(), angle + asw_infest_angle.GetFloat(), 0 ) );
		// play our infesting anim
		if ( asw_parasite_inside.GetBool() )
		{
			SetActivity(ACT_RANGE_ATTACK2);
		}
		else
		{
			int iInfestAttack = LookupSequence("Infest_attack");
			if (GetSequence() != iInfestAttack)
			{
				ResetSequence(iInfestAttack);
			}
		}
		
		AddFlag( FL_NOTARGET );
		SetThink( &CASW_Parasite::InfestThink );
		SetTouch( NULL );
		m_bInfesting = true;		
	}
	else
	{
		FinishedInfesting();
	}		
}
//---------------------------------------------------------
//---------------------------------------------------------
void CNPC_Dog::StartTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{

	case TASK_DOG_SETUP_THROW_TARGET:
		{
			SetupThrowTarget();
			TaskComplete();
		}
		break;
	case TASK_DOG_GET_PATH_TO_PHYSOBJ:
		{
			FindPhysicsObject( STRING( m_sObjectName ) );

			if ( m_hPhysicsEnt == NULL )
			{
				 FindPhysicsObject( NULL );
				 return;
			}

			IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject();
			
			Vector vecGoalPos;
			Vector vecDir;

			vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter();
			VectorNormalize(vecDir);
			vecDir.z = 0;
		
			if ( m_hPhysicsEnt->GetOwnerEntity() == NULL )
				 m_hPhysicsEnt->SetOwnerEntity( this );
		
			if ( pPhysicsObject )
				 pPhysicsObject->RecheckCollisionFilter();

			vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST );

			//If I'm near my goal, then just walk to it.
			Activity aActivity = ACT_RUN;

			if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 )
				 aActivity = ACT_WALK;

			if ( GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ) == false )
			{
				 if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 )
					  m_hUnreachableObjects.AddToTail( m_hPhysicsEnt );
					
				 FindPhysicsObject( NULL, m_hPhysicsEnt );

				 m_flTimeToCatch = gpGlobals->curtime + 0.1;
				 m_flNextRouteTime = gpGlobals->curtime + 0.3;
				 m_flNextSwat = gpGlobals->curtime + 0.1;

				 GetNavigator()->ClearGoal();
			}
			else
			{
				TaskComplete();
			}
		}
		break;

	case TASK_DOG_FACE_OBJECT:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}

			Vector vecDir;

			vecDir = m_hPhysicsEnt->WorldSpaceCenter() - GetLocalOrigin();
			VectorNormalize(vecDir);

			GetMotor()->SetIdealYaw( UTIL_VecToYaw( vecDir ) );
			TaskComplete();
		}
		break;
		
	case TASK_DOG_PICKUP_ITEM:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}
			else
			{
				SetIdealActivity( (Activity)ACT_DOG_PICKUP );
			}
		}

		break;
		
	case TASK_DOG_LAUNCH_ITEM:
		{
			if( m_hPhysicsEnt == NULL )
			{
				// Physics Object is gone! Probably was an explosive 
				// or something else broke it.
				TaskFail("Physics ent NULL");
				return;
			}
			else
			{
				if ( m_hPhysicsEnt == NULL || m_bHasObject == false )
				{
					 TaskFail( "Don't have the item!" );
					 return;
				}

				SetIdealActivity( (Activity)ACT_DOG_THROW );
			}
		}

		break;

	case TASK_DOG_WAIT_FOR_TARGET_TO_FACE:
		{
			if ( CanTargetSeeMe() )
				 TaskComplete();
		}
		break;

	case TASK_DOG_WAIT_FOR_OBJECT:
		{
			SetIdealActivity( (Activity)ACT_DOG_WAITING );
		}
		break;

	case TASK_DOG_CATCH_OBJECT:
	{
		SetIdealActivity( (Activity)ACT_DOG_CATCH  );
	}
	break;
			
	case TASK_DOG_DELAY_SWAT:
		m_flNextSwat = gpGlobals->curtime + pTask->flTaskData;

		if ( m_hThrowTarget == NULL )
		#ifdef SecobMod__Enable_Fixed_Multiplayer_AI
			m_hThrowTarget = UTIL_GetNearestVisiblePlayer(this); 
		#else
			m_hThrowTarget = AI_GetSinglePlayer();
		#endif //SecobMod__Enable_Fixed_Multiplayer_AI

		TaskComplete();
		break;

	default:
		BaseClass::StartTask( pTask );
	}
}
Beispiel #25
0
bool CAI_PlaneSolver::GenerateCircleObstacleSuggestions( float probeDist )
{
	bool result = false;
	Vector npcLoc = m_pNpc->WorldSpaceCenter();
	Vector mins, maxs;
				
	m_pNpc->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
	float radiusNpc = (mins.AsVector2D() - maxs.AsVector2D()).Length() * 0.5;
	
	for ( int i = 0; i < m_Obstacles.Count(); i++ )
	{
		float zDistTooFar;
		if ( m_Obstacles[i].hEntity && m_Obstacles[i].hEntity->CollisionProp() )
		{
			m_Obstacles[i].hEntity->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
			zDistTooFar = ( maxs.z - mins.z ) * 0.5 + GetNpc()->GetHullHeight() * 0.5;
		}
		else
			zDistTooFar = GetNpc()->GetHullHeight();
			
		if ( fabs( m_Obstacles[i].center.z - npcLoc.z ) > zDistTooFar )
			continue;

		Vector vecToNpc 		= npcLoc - m_Obstacles[i].center;
		vecToNpc.z = 0;
		float distToObstacleSq 	= sq(vecToNpc.x) + sq(vecToNpc.y);
		float radius = m_Obstacles[i].radius + radiusNpc;

		if ( distToObstacleSq > 0.001 && distToObstacleSq < sq(radius + probeDist))
		{
			Vector vecToObstacle = vecToNpc * -1;
			float distToObstacle = VectorNormalize( vecToObstacle );
			float weight;
			float arc;
			float radiusSq = sq(radius);
			if ( radiusSq < distToObstacleSq )
			{
				Vector vecTangent;
				float distToTangent = FastSqrt( distToObstacleSq - radiusSq );

				float oneOverDistToObstacleSq = 1 / distToObstacleSq;

				vecTangent.x = ( -distToTangent * vecToNpc.x + radius * vecToNpc.y ) * oneOverDistToObstacleSq;
				vecTangent.y = ( -distToTangent * vecToNpc.y - radius * vecToNpc.x ) * oneOverDistToObstacleSq;
				vecTangent.z = 0;

				float cosHalfArc = vecToObstacle.Dot( vecTangent );
				arc = RAD2DEG(acosf( cosHalfArc )) * 2.0;
				weight = 1.0 - (distToObstacle - radius) / probeDist;
				if ( weight > 0.75 )
					arc += (arc * 0.5) * (weight - 0.75) / 0.25;
				
				Assert( weight >= 0.0 && weight <= 1.0 );

#if DEBUG_OBSTACLES
				// -------------------------
				Msg( "Adding arc %f, w %f\n", arc, weight );

				Vector pointTangent = npcLoc + ( vecTangent * distToTangent );
					
				NDebugOverlay::Line( npcLoc - Vector( 0, 0, 64 ), npcLoc + Vector(0,0,64), 0,255,0, false, 0.1 );
				NDebugOverlay::Line( center - Vector( 0, 0, 64 ), center + Vector(0,0,64), 0,255,0, false, 0.1 );
				NDebugOverlay::Line( pointTangent - Vector( 0, 0, 64 ), pointTangent + Vector(0,0,64), 0,255,0, false, 0.1 );
				
				NDebugOverlay::Line( npcLoc + Vector(0,0,64), center + Vector(0,0,64), 0,0,255, false, 0.1 );
				NDebugOverlay::Line( center + Vector(0,0,64), pointTangent + Vector(0,0,64), 0,0,255, false, 0.1 );
				NDebugOverlay::Line( pointTangent + Vector(0,0,64), npcLoc + Vector(0,0,64), 0,0,255, false, 0.1 );
#endif
			}
			else
			{
				arc = 210;
				weight = 1.0;
			}

			if ( m_Obstacles[i].hEntity != NULL )
			{
				weight = AdjustRegulationWeight( m_Obstacles[i].hEntity, weight );
			}
			
			AI_MoveSuggestion_t suggestion( m_Obstacles[i].type, weight, UTIL_VecToYaw(vecToObstacle), arc );
			m_Solver.AddRegulation( suggestion );
			result = true;
		}
	}
	
	m_Obstacles.RemoveAll();
	return result;

}
// "Bend" our line of sight around corners until we can "see" the point.
bool CCSBot::BendLineOfSight(const Vector *eye, const Vector *point, Vector *bend) const
{
	// if we can directly see the point, use it
	TraceResult result;
	UTIL_TraceLine(*eye, *point + Vector(0, 0, HalfHumanHeight), ignore_monsters, ENT(pev), &result);

	if (result.flFraction == 1.0f && !result.fStartSolid)
	{
		// can directly see point, no bending needed
		*bend = *point;
		return true;
	}

	// "bend" our line of sight until we can see the approach point
	Vector v = *point - *eye;
	float startAngle = UTIL_VecToYaw(v);
	float length = v.Length2D();
	v.NormalizeInPlace();

	float angleInc = 10.0f;
	for (float angle = angleInc; angle <= 135.0f; angle += angleInc)
	{
		// check both sides at this angle offset
		for (int side = 0; side < 2; ++side)
		{
			float actualAngle = (side) ? (startAngle + angle) : (startAngle - angle);

			float dx = BotCOS(actualAngle);
			float dy = BotSIN(actualAngle);

			// compute rotated point ray endpoint
			Vector rotPoint(eye->x + length * dx, eye->y + length * dy, point->z);

			TraceResult result;
			UTIL_TraceLine(*eye, rotPoint + Vector(0, 0, HalfHumanHeight), ignore_monsters, ENT(pev), &result);

			// if this ray started in an obstacle, skip it
			if (result.fStartSolid)
			{
				continue;
			}

			Vector ray = rotPoint - *eye;
			float rayLength = ray.NormalizeInPlace();
			float visibleLength = rayLength * result.flFraction;

			// step along ray, checking if point is visible from ray point
			const float bendStepSize = 50.0f;
			for (float bendLength = bendStepSize; bendLength <= visibleLength; bendLength += bendStepSize)
			{
				// compute point along ray
				Vector rayPoint = *eye + bendLength * ray;

				// check if we can see approach point from this bend point
				UTIL_TraceLine(rayPoint, *point + Vector(0, 0, HalfHumanHeight), ignore_monsters, ENT(pev), &result);

				if (result.flFraction == 1.0f && !result.fStartSolid)
				{
					// target is visible from this bend point on the ray - use this point on the ray as our point

					// keep "bent" point at correct height along line of sight
					if (!GetGroundHeight(&rayPoint, &rayPoint.z))
					{
						rayPoint.z = point->z;
					}

					*bend = rayPoint;
					return true;
				}
			}
		}
	}

	*bend = *point;

	// bending rays didn't help - still can't see the point
	return false;
}
Beispiel #27
0
//
// TentacleThink
//
void CTentacle :: Cycle( void )
{
	// ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
	pev->nextthink = gpGlobals-> time + 0.1;

	// ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );

	if (m_MonsterState == MONSTERSTATE_SCRIPT || m_IdealMonsterState == MONSTERSTATE_SCRIPT)
	{
		pev->angles.y = m_flInitialYaw;
		pev->ideal_yaw = m_flInitialYaw;	
		ClearConditions( IgnoreConditions() );
		MonsterThink( );
		m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		return;
	}

	DispatchAnimEvents( );
	StudioFrameAdvance( );

	ChangeYaw( pev->yaw_speed );

	CSound *pSound;

	Listen( );

	// Listen will set this if there's something in my sound list
	if ( HasConditions( bits_COND_HEAR_SOUND ) )
		pSound = PBestSound();
	else
		pSound = NULL;

	if ( pSound )
	{
		Vector vecDir;
		if (gpGlobals->time - m_flPrevSoundTime < 0.5)
		{
			float dt = gpGlobals->time - m_flPrevSoundTime;
			vecDir = pSound->m_vecOrigin + (pSound->m_vecOrigin - m_vecPrevSound) / dt - pev->origin;
		}
		else
		{
			vecDir = pSound->m_vecOrigin - pev->origin;
		}
		m_flPrevSoundTime = gpGlobals->time;
		m_vecPrevSound = pSound->m_vecOrigin;

		m_flSoundYaw = UTIL_VecToYaw ( vecDir ) - m_flInitialYaw;
		m_iSoundLevel = Level( vecDir.z );

		if (m_flSoundYaw < -180)
			m_flSoundYaw += 360;
		if (m_flSoundYaw > 180)
			m_flSoundYaw -= 360;

		// ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw );
		if (m_flSoundTime < gpGlobals->time)
		{
			// play "I hear new something" sound
			char *sound;	

			switch( RANDOM_LONG(0,1) )
			{
			case 0: sound = "tentacle/te_alert1.wav"; break;
			case 1: sound = "tentacle/te_alert2.wav"; break;
			}

			// UTIL_EmitAmbientSound(ENT(pev), pev->origin + Vector( 0, 0, MyHeight()), sound, 1.0, ATTN_NORM, 0, 100);
		}
		m_flSoundTime = gpGlobals->time + RANDOM_FLOAT( 5.0, 10.0 );
	}

	// clip ideal_yaw
	float dy = m_flSoundYaw;
	switch( pev->sequence )
	{
	case TENTACLE_ANIM_Floor_Rear:
	case TENTACLE_ANIM_Floor_Rear_Idle:
	case TENTACLE_ANIM_Lev1_Rear:
	case TENTACLE_ANIM_Lev1_Rear_Idle:
	case TENTACLE_ANIM_Lev2_Rear:
	case TENTACLE_ANIM_Lev2_Rear_Idle:
	case TENTACLE_ANIM_Lev3_Rear:
	case TENTACLE_ANIM_Lev3_Rear_Idle:
		if (dy < 0 && dy > -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > 0 && dy < m_flMaxYaw)
			dy = m_flMaxYaw;
		break;
	default:
		if (dy < -m_flMaxYaw)
			dy = -m_flMaxYaw;
		if (dy > m_flMaxYaw)
			dy = m_flMaxYaw;
	}
	pev->ideal_yaw = m_flInitialYaw + dy;

	if (m_fSequenceFinished)
	{
		// ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim );
		if (pev->health <= 1)
		{
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
			if (pev->sequence == TENTACLE_ANIM_Pit_Idle)
			{
				pev->health = 75;
			}
		}
		else if ( m_flSoundTime > gpGlobals->time )
		{
			if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30))
			{
				// strike
				m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel );
			}
			else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2) 
			{
				// tap
				m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel );
			}
			else
			{
				// go into rear idle
				m_iGoalAnim = LookupActivity( ACT_T_REARIDLE + m_iSoundLevel );
			}
		}
		else if (pev->sequence == TENTACLE_ANIM_Pit_Idle)
		{
			// stay in pit until hear noise
			m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
		}
		else if (pev->sequence == m_iGoalAnim)
		{
			if (MyLevel() >= 0 && gpGlobals->time < m_flSoundTime)
			{
				if (RANDOM_LONG(0,9) < m_flSoundTime - gpGlobals->time)
				{
					// continue stike
					m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel );
				}
				else
				{
					// tap
					m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel );
				}
			}
			else if (MyLevel( ) < 0)
			{
				m_iGoalAnim = LookupActivity( ACT_T_IDLE + 0 );
			}
			else
			{
				if (m_flNextSong < gpGlobals->time)
				{
					// play "I hear new something" sound
					char *sound;	

					switch( RANDOM_LONG(0,1) )
					{
					case 0: sound = "tentacle/te_sing1.wav"; break;
					case 1: sound = "tentacle/te_sing2.wav"; break;
					}

					EMIT_SOUND(ENT(pev), CHAN_VOICE, sound, 1.0, ATTN_NORM);

					m_flNextSong = gpGlobals->time + RANDOM_FLOAT( 10, 20 );
				}

				if (RANDOM_LONG(0,15) == 0)
				{
					// idle on new level
					m_iGoalAnim = LookupActivity( ACT_T_IDLE + RANDOM_LONG(0,3) );
				}
				else if (RANDOM_LONG(0,3)  == 0)
				{
					// tap
					m_iGoalAnim = LookupActivity( ACT_T_TAP + MyLevel( ) );
				}
				else
				{
					// idle
					m_iGoalAnim = LookupActivity( ACT_T_IDLE + MyLevel( ) );
				}
			}
			if (m_flSoundYaw < 0)
				m_flSoundYaw += RANDOM_FLOAT( 2, 8 );
			else
				m_flSoundYaw -= RANDOM_FLOAT( 2, 8 );
		}

		pev->sequence = FindTransition( pev->sequence, m_iGoalAnim, &m_iDir );

		if (m_iDir > 0)
		{
			pev->frame = 0;
		}
		else
		{
			m_iDir = -1; // just to safe
			pev->frame = 255;
		}
		ResetSequenceInfo( );

		m_flFramerateAdj = RANDOM_FLOAT( -0.2, 0.2 );
		pev->framerate = m_iDir * 1.0 + m_flFramerateAdj;

		switch( pev->sequence)
		{
		case TENTACLE_ANIM_Floor_Tap:
		case TENTACLE_ANIM_Lev1_Tap:
		case TENTACLE_ANIM_Lev2_Tap:
		case TENTACLE_ANIM_Lev3_Tap:
			{
				Vector vecSrc;
				UTIL_MakeVectors( pev->angles );

				TraceResult tr1, tr2;

				vecSrc = pev->origin + Vector( 0, 0, MyHeight() - 4);
				UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr1 );

				vecSrc = pev->origin + Vector( 0, 0, MyHeight() + 8);
				UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr2 );

				// ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 );

				m_flTapRadius = SetBlending( 0, RANDOM_FLOAT( tr1.flFraction * 512, tr2.flFraction * 512 ) );
			}
			break;
		default:
			m_flTapRadius = 336; // 400 - 64
			break;
		}
		pev->view_ofs.z = MyHeight( );
		// ALERT( at_console, "seq %d\n", pev->sequence );
	}

	if (m_flPrevSoundTime + 2.0 > gpGlobals->time)
	{
		// 1.5 normal speed if hears sounds
		pev->framerate = m_iDir * 1.5 + m_flFramerateAdj;
	}
	else if (m_flPrevSoundTime + 5.0 > gpGlobals->time)
	{
		// slowdown to normal
		pev->framerate = m_iDir + m_iDir * (5 - (gpGlobals->time - m_flPrevSoundTime)) / 2 + m_flFramerateAdj;
	}
}
//=========================================================
// RunTask 
//=========================================================
void CNPC_Controller::RunTask ( const Task_t *pTask )
{
	if (m_flShootEnd > gpGlobals->curtime)
	{
		Vector vecHand;
		QAngle vecAngle;
		
		GetAttachment( 2, vecHand, vecAngle );
	
		while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->curtime)
		{
			Vector vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime);
			Vector vecDir;
			
			if (GetEnemy() != NULL)
			{
				if (HasCondition( COND_SEE_ENEMY ))
				{
					m_vecEstVelocity = m_vecEstVelocity * 0.5 + GetEnemy()->GetAbsVelocity() * 0.5;
				}
				else
				{
					m_vecEstVelocity = m_vecEstVelocity * 0.8;
				}
				vecDir = Intersect( vecSrc, GetEnemy()->BodyTarget( GetAbsOrigin() ), m_vecEstVelocity, sk_controller_speedball.GetFloat() );
			
				float delta = 0.03490; // +-2 degree
				vecDir = vecDir + Vector( random->RandomFloat( -delta, delta ), random->RandomFloat( -delta, delta ), random->RandomFloat( -delta, delta ) ) * sk_controller_speedball.GetFloat();

				vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime);
				CAI_BaseNPC *pBall = (CAI_BaseNPC*)Create( "controller_energy_ball", vecSrc, GetAbsAngles(), this );
				pBall->SetAbsVelocity( vecDir );

//				DevMsg( 2, "controller shooting energy ball\n" );
			}
			
			m_flShootTime += 0.2;
		}

		if (m_flShootTime > m_flShootEnd)
		{
			m_iBall[0] = 64;
			m_iBallTime[0] = m_flShootEnd;
			m_iBall[1] = 64;
			m_iBallTime[1] = m_flShootEnd;
			m_fInCombat = FALSE;
		}
	}

	switch ( pTask->iTask )
	{
	case TASK_WAIT_FOR_MOVEMENT:
	case TASK_WAIT:
	case TASK_WAIT_FACE_ENEMY:
	case TASK_WAIT_PVS:
		{
			if( GetEnemy() )
			{
				float idealYaw = UTIL_VecToYaw( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
				GetMotor()->SetIdealYawAndUpdate( idealYaw );
			}

			if ( IsSequenceFinished() || GetActivity() == ACT_IDLE)
			{
				m_fInCombat = false;
			}

			BaseClass::RunTask ( pTask );

			if (!m_fInCombat)
			{
				if( HasCondition( COND_CAN_RANGE_ATTACK1 ))
				{
					SetActivity( ACT_RANGE_ATTACK1 );
					SetCycle( 0 ); 
					ResetSequenceInfo( );
					m_fInCombat = true;
				}
				else if( HasCondition( COND_CAN_RANGE_ATTACK2 ) )
				{
					SetActivity( ACT_RANGE_ATTACK2 );
					SetCycle( 0 );
					ResetSequenceInfo( );
					m_fInCombat = true;
				}
				else
				{
					int iFloatActivity = LookupFloat();
					if( IsSequenceFinished() || iFloatActivity != GetActivity() )
					{
						SetActivity( (Activity)iFloatActivity );
					}
				}
			}
		}
		break;
	default: 
		BaseClass::RunTask ( pTask );
		break;
	}
}
CAI_BaseNPC *CASW_Sentry_Top_Cannon::SelectOptimalEnemy()
{
	// prioritize unfrozen aliens who are going to leave the cone soon.
	// prioritize aliens less the more frozen they get.
	CUtlVectorFixedGrowable< CAI_BaseNPC *,16 > candidates;
	CUtlVectorFixedGrowable< float, 16 > candidatescores;

	// search through all npcs, any that are in LOS and have health
	CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
	for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
	{
		if (ppAIs[i]->GetHealth() > 0 && CanSee(ppAIs[i]))
		{
			// don't shoot marines
			if ( !asw_sentry_friendly_target.GetBool() && ppAIs[i]->Classify() == CLASS_ASW_MARINE )
				continue;

			if ( ppAIs[i]->Classify() == CLASS_SCANNER )
				continue;

			if ( !IsValidEnemy( ppAIs[i] ) )
				continue;

			candidates.AddToTail( ppAIs[i] );
		}
	}

	// bail out if we don't have anyone
	if ( candidates.Count() < 1 )
		return NULL;

	else if ( candidates.Count() == 1 ) // just one candidate is an obvious result
		return candidates[0];

	// score each of the candidates
	candidatescores.EnsureCount( candidates.Count() );
	for ( int i = candidates.Count() - 1; i >= 0 ; --i )
	{
		CAI_BaseNPC * RESTRICT pCandidate = candidates[i];

		// is the candidate moving into or out of the cone?
		Vector vCandVel = GetEnemyVelocity(pCandidate);
		Vector vMeToTarget = pCandidate->GetAbsOrigin() - GetFiringPosition();
		Vector vBaseForward = UTIL_YawToVector( m_fDeployYaw );

		// crush everything to 2d for simplicity
		vMeToTarget.z = 0.0f;
		vCandVel.z = 0.0f;
		vBaseForward.z = 0.0f;

		Vector velCross = vBaseForward.Cross(vCandVel); // this encodes also some info on perpendicularity
		Vector vAimCross = vBaseForward.Cross(vMeToTarget);
		bool bTargetHeadedOutOfCone = !vCandVel.IsZero() && velCross.z * vAimCross.z >= 0; // true if same sign
		float flConeLeavingUrgency;

		if ( bTargetHeadedOutOfCone )
		{
			flConeLeavingUrgency = fabs( velCross.z / vCandVel.Length2D() ); 
			// just the sin; varies 0..1 where 1 means moving perpendicular to my aim
		}
		else
		{
			flConeLeavingUrgency = 0; // not at threat of leaving just yet
		}

		// the angle between my current yaw and what's needed to hit the target
		float flSwivelNeeded = fabs( UTIL_AngleDiff(  // i wish we weren't storing euler angles
			UTIL_VecToYaw( vMeToTarget ), m_fDeployYaw ) );
		flSwivelNeeded /= ASW_SENTRY_ANGLE; // normalize to 0..2

		float fBigness = 0.0f;

		int nClassify = pCandidate->Classify();
		switch( nClassify )
		{
		case CLASS_ASW_SHIELDBUG:
		case CLASS_ASW_MORTAR_BUG:
			fBigness = 4.0f;
			break;

		case CLASS_ASW_HARVESTER:
		case CLASS_ASW_RANGER:
			fBigness = 2.0f;
			break;
		}

		candidatescores[i] = Vector( 3.0f, -1.5f, 4.0f ).Dot( Vector( flConeLeavingUrgency, flSwivelNeeded, fBigness ) );
	}
	// find the highest scoring candidate
	int best = 0;
	for ( int i = 1 ; i < candidatescores.Count() ; ++i )
	{
		if ( candidatescores[i] > candidatescores[best] )
			best = i;
	}

	// NDebugOverlay::EntityBounds(candidates[best], 255, 255, 0, 255, 0.2f );

	return candidates[best];
}
// Move actual view angles towards desired ones.
// This is the only place v_angle is altered.
// TODO: Make stiffness and turn rate constants timestep invariant.
void CCSBot::UpdateLookAngles()
{
	const float deltaT = g_flBotCommandInterval;
	float maxAccel;
	float stiffness;
	float damping;

	// springs are stiffer when attacking, so we can track and move between targets better
	if (IsAttacking())
	{
		stiffness = 300.0f;
		damping = 30.0f;
		maxAccel = 3000.0f;
	}
	else
	{
		stiffness = 200.0f;
		damping = 25.0f;
		maxAccel = 3000.0f;
	}

	// these may be overridden by ladder logic
	float useYaw = m_lookYaw;
	float usePitch = m_lookPitch;

	// Ladders require precise movement, therefore we need to look at the
	// ladder as we approach and ascend/descend it.
	// If we are on a ladder, we need to look up or down to traverse it - override pitch in this case.
	// If we're trying to break something, though, we actually need to look at it before we can
	// look at the ladder
	if (IsUsingLadder())
	{
		// set yaw to aim at ladder
		Vector to = m_pathLadder->m_top - pev->origin;
		float idealYaw = UTIL_VecToYaw(to);

		NavDirType faceDir = m_pathLadder->m_dir;

		if (m_pathLadderFaceIn)
		{
			faceDir = OppositeDirection(faceDir);
		}

		const float lookAlongLadderRange = 100.0f;
		const float ladderPitch = 60.0f;

		// adjust pitch to look up/down ladder as we ascend/descend
		switch (m_pathLadderState)
		{
			case APPROACH_ASCENDING_LADDER:
			{
				Vector to = m_goalPosition - pev->origin;
				useYaw = idealYaw;

				if (to.IsLengthLessThan(lookAlongLadderRange))
					usePitch = -ladderPitch;
				break;
			}
			case APPROACH_DESCENDING_LADDER:
			{
				Vector to = m_goalPosition - pev->origin;
				useYaw = idealYaw;

				if (to.IsLengthLessThan(lookAlongLadderRange))
					usePitch = ladderPitch;
				break;
			}
			case FACE_ASCENDING_LADDER:
			{
				useYaw = idealYaw;
				usePitch = -ladderPitch;
				break;
			}
			case FACE_DESCENDING_LADDER:
			{
				useYaw = idealYaw;
				usePitch = ladderPitch;
				break;
			}
			case MOUNT_ASCENDING_LADDER:
			case ASCEND_LADDER:
			{
				useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder);
				usePitch = -ladderPitch;
				break;
			}
			case MOUNT_DESCENDING_LADDER:
			case DESCEND_LADDER:
			{
				useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder);
				usePitch = ladderPitch;
				break;
			}
			case DISMOUNT_ASCENDING_LADDER:
			case DISMOUNT_DESCENDING_LADDER:
			{
				useYaw = DirectionToAngle(faceDir);
				break;
			}
		}
	}

	// Yaw
	float angleDiff = NormalizeAngle(useYaw - pev->v_angle.y);

	// if almost at target angle, snap to it
	const float onTargetTolerance = 1.0f;
	if (angleDiff < onTargetTolerance && angleDiff > -onTargetTolerance)
	{
		m_lookYawVel = 0.0f;
		pev->v_angle.y = useYaw;
	}
	else
	{
		// simple angular spring/damper
		float accel = stiffness * angleDiff - damping * m_lookYawVel;

		// limit rate
		if (accel > maxAccel)
			accel = maxAccel;

		else if (accel < -maxAccel)
			accel = -maxAccel;

		m_lookYawVel += deltaT * accel;
		pev->v_angle.y += deltaT * m_lookYawVel;
	}

	// Pitch
	// Actually, this is negative pitch.
	angleDiff = usePitch - pev->v_angle.x;

	angleDiff = NormalizeAngle(angleDiff);

	if (false && angleDiff < onTargetTolerance && angleDiff > -onTargetTolerance)
	{
		m_lookPitchVel = 0.0f;
		pev->v_angle.x = usePitch;
	}
	else
	{
		// simple angular spring/damper
		// double the stiffness since pitch is only +/- 90 and yaw is +/- 180
		float accel = 2.0f * stiffness * angleDiff - damping * m_lookPitchVel;

		// limit rate
		if (accel > maxAccel)
			accel = maxAccel;

		else if (accel < -maxAccel)
			accel = -maxAccel;

		m_lookPitchVel += deltaT * accel;
		pev->v_angle.x += deltaT * m_lookPitchVel;
	}

	// limit range - avoid gimbal lock
	if (pev->v_angle.x < -89.0f)
		pev->v_angle.x = -89.0f;
	else if (pev->v_angle.x > 89.0f)
		pev->v_angle.x = 89.0f;

	pev->v_angle.z = 0.0f;
}