예제 #1
0
void HapticsDamage(CBasePlayer* pPlayer, const CTakeDamageInfo &info)
{
#if !defined(TF_DLL) && !defined(CSTRIKE_DLL)
	if(!pPlayer->HasHaptics())
		return;// do not send to non haptic users.

	Vector DamageDirection(0,0,0);
	CBaseEntity *eInflictor = info.GetInflictor();
	// Pat: nuero toxix crash fix
	if(!eInflictor) {
		return;
	}
	// Player Data
	Vector playerPosition = pPlayer->GetLocalOrigin();
	Vector inflictorPosition = eInflictor->GetLocalOrigin();

	Vector posWithDir = playerPosition + (playerPosition - inflictorPosition);
	pPlayer->WorldToEntitySpace(posWithDir, &DamageDirection);
	QAngle dir(0,0,0);
	VectorAngles(DamageDirection, dir);
	float yawAngle = dir[YAW];
	float pitchAngle = dir[PITCH];

	int bitDamageType = info.GetDamageType();

	if(bitDamageType & DMG_FALL)
	{
		pitchAngle = ((float)-90.0); // coming from beneath
	}
	else if(bitDamageType & DMG_BURN && (bitDamageType & ~DMG_BURN)==0)
	{
		// just burn, use the z axis here.
		pitchAngle = 0.0;
	}
#ifdef TERROR
	else if(
		(bitDamageType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) && 
		(bitDamageType & ~( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) )==0 )
	{
		// it is time based. and should not really do a punch.
		return;
	}
#endif
	
	float sendDamage = info.GetDamage();

	if(sendDamage>0.0f)
	{
		HapticMsg_HapDmg( pPlayer, pitchAngle, -yawAngle, sendDamage, bitDamageType );
	}
#endif
}
bool CAI_LeadBehavior::SetGoal( const AI_LeadArgs_t &args )
{
	CBaseEntity *pGoalEnt;
	pGoalEnt = gEntList.FindEntityByName( NULL, args.pszGoal );
	
	if ( !pGoalEnt )
		return false;

	m_args 		= args;	// @Q (toml 08-13-02): need to copy string?
	m_goal 		= pGoalEnt->GetLocalOrigin();
	m_goalyaw 	= (args.flags & AILF_USE_GOAL_FACING) ? pGoalEnt->GetLocalAngles().y : -1;
	m_waitpoint = vec3_origin;
	m_waitdistance = args.flWaitDistance;
	m_leaddistance = args.flLeadDistance ? args.flLeadDistance : 64;
	m_retrievedistance = args.flRetrieveDistance ? args.flRetrieveDistance : (m_leaddistance + LEAD_MIN_RETRIEVEDIST_OFFSET);
	m_successdistance = args.flSuccessDistance ? args.flSuccessDistance : 0;
	m_run = args.bRun;
	m_gagleader = args.bGagLeader;
	m_hasspokenstart = args.bDontSpeakStart;
	m_hasspokenarrival = false;
	m_hasPausedScenes = false;
	m_flSpeakNextNagTime = 0;
	m_flWeaponSafetyTimeOut = 0;
	m_flNextLeadIdle = gpGlobals->curtime + 10;
	m_bInitialAheadTest = true;

	if ( args.pszWaitPoint && args.pszWaitPoint[0] )
	{
		CBaseEntity *pWaitPoint = gEntList.FindEntityByName( NULL, args.pszWaitPoint );
		if ( pWaitPoint )
		{
			m_waitpoint = pWaitPoint->GetLocalOrigin();
		}
	}

	return true;
}
//------------------------------------------------------------------------------
// Purpose :
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CBaseHelicopter::UpdatePlayerDopplerShift( )
{
	// -----------------------------
	// make rotor, engine sounds
	// -----------------------------
	if (m_iSoundState == 0)
	{
		// Sound startup.
		InitializeRotorSound();
	}
	else
	{
		CBaseEntity *pPlayer = NULL;

		// UNDONE: this needs to send different sounds to every player for multiplayer.	
		// FIXME: this isn't the correct way to find a player!!!
		pPlayer = gEntList.FindEntityByName( NULL, "!player", this );
		if (pPlayer)
		{
			Vector dir = pPlayer->GetLocalOrigin() - GetLocalOrigin();
			VectorNormalize(dir);

#if 1
			float velReceiver = -DotProduct( pPlayer->GetAbsVelocity(), dir );
			float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
			// speed of sound == 13049in/s
			int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
#else
			// This is a bogus doppler shift, but I like it better
			float relV = DotProduct( GetAbsVelocity() - pPlayer->GetAbsVelocity(), dir );
			int iPitch = (int)(100 + relV / 50.0);
#endif

			// clamp pitch shifts
			if (iPitch > 250) 
				iPitch = 250;
			if (iPitch < 50)
				iPitch = 50;

			UpdateRotorSoundPitch( iPitch );
			// Msg( "Pitch:%d\n", iPitch );
		}
		else
		{
			Msg( "Chopper didn't find a player!\n" );
		}
	}
}
예제 #4
0
void CGunTarget::Next( void )
{
	SetThink( NULL );

	m_hTargetEnt = GetNextTarget();
	CBaseEntity *pTarget = m_hTargetEnt;
	
	if ( !pTarget )
	{
		Stop();
		return;
	}

	SetMoveDone( &CGunTarget::Wait );
	LinearMove( pTarget->GetLocalOrigin(), m_flSpeed );
}
예제 #5
0
void CInfoIntermission::Think ( void )
{
	CBaseEntity *pTarget;

	// find my target
	pTarget = gEntList.FindEntityByName( NULL, m_target );

	if ( pTarget )
	{
		Vector dir = pTarget->GetLocalOrigin() - GetLocalOrigin();
		VectorNormalize( dir );
		QAngle angles;
		VectorAngles( dir, angles );
		SetLocalAngles( angles );
	}
}
예제 #6
0
//-----------------------------------------------------------------------------
// Purpose: For create nodes in wc edit mode
// Input  :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit::CreateAINode( CBasePlayer *pPlayer )
{
	// -------------------------------------------------------------
	//  Check that WC is running with the right map version
	// -------------------------------------------------------------
	if ( !IsWCVersionValid() || !pPlayer )
		return;

	pPlayer->AddSolidFlags( FSOLID_NOT_SOLID );

	int hullType = g_pAINetworkManager->GetEditOps()->m_iHullDrawNum;

	// -----------------------------------
	//  Get position of node to create
	// -----------------------------------
	Vector vNewNodePos = vec3_origin;
	bool bPositionValid = false;
	if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
	{
		vNewNodePos = NWCEdit::AirNodePlacementPosition();

		// Make sure we can see the node
		trace_t tr;
		UTIL_TraceLine(pPlayer->EyePosition(), vNewNodePos, MASK_NPCSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr);
		if (tr.fraction == 1.0)
		{
			bPositionValid = true;
		}
	}
	else
	{
		// Place node by where the player is looking
		Vector forward;
		pPlayer->EyeVectors( &forward );
		Vector	startTrace	= pPlayer->EyePosition();
		Vector	endTrace	= pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH;
		trace_t	tr;
		UTIL_TraceLine(startTrace,endTrace,MASK_NPCSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
		if ( tr.fraction != 1.0)
		{
			// Raise the end position up off the floor, place the node and drop him down
			tr.endpos.z += 48;
			vNewNodePos = tr.endpos;
			bPositionValid = true;
		}
	}

	// -------------------------------------------------------------------------------
	// Now check that this is a valid location for the new node bu using test hull
	// -------------------------------------------------------------------------------
	if (bPositionValid)
	{
		CBaseEntity *testHull = (CBaseEntity*)CAI_TestHull::GetTestHull();

		// Set the size of the test hull
		UTIL_SetSize(testHull, NAI_Hull::Mins(hullType), NAI_Hull::Maxs(hullType));

		// Set origin of test hull
		testHull->SetLocalOrigin( vNewNodePos );

		// -----------------------------------------------------------------------
		// If a ground node, drop to floor and make sure can stand at test postion
		// -----------------------------------------------------------------------
		if (!g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
		{
			UTIL_DropToFloor( testHull, MASK_NPCSOLID );
			vNewNodePos = testHull->GetAbsOrigin();
			CTraceFilterSimple traceFilter( testHull, COLLISION_GROUP_NONE );
			if (!UTIL_CheckBottom(testHull, &traceFilter, sv_stepsize.GetFloat()))
			{
				CAI_TestHull::ReturnTestHull();
				bPositionValid = false;
				goto DoneCreate;
			}
		}

		// -----------------------------------------------------------------------
		// Make sure hull fits at location by seeing if it can move up a fraction
		// -----------------------------------------------------------------------
		Vector vUpBit = testHull->GetAbsOrigin();
		vUpBit.z += 1;
		trace_t tr;
		UTIL_TraceHull( testHull->GetAbsOrigin(), vUpBit, NAI_Hull::Mins(hullType), 
			NAI_Hull::Maxs(hullType), MASK_NPCSOLID, testHull, COLLISION_GROUP_NONE, &tr );
		if (tr.startsolid || tr.fraction != 1.0)
		{
			CAI_TestHull::ReturnTestHull();
			bPositionValid = false;
			goto DoneCreate;
		}

		// <<TEMP>> Round position till DS fixed WC bug
		testHull->SetLocalOrigin( Vector( floor(testHull->GetAbsOrigin().x),
			floor(testHull->GetAbsOrigin().y ), floor(testHull->GetAbsOrigin().z) ) );

		// ---------------------------------------
		//  Send new node to WC
		// ---------------------------------------
		int status;
		if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
		{
			status = Editor_CreateNode("info_node_air", g_pAINetworkManager->GetEditOps()->m_nNextWCIndex, testHull->GetLocalOrigin().x, testHull->GetLocalOrigin().y, testHull->GetLocalOrigin().z, false);
		}
		else
		{
			// Create slightly higher in WC so it can be dropped when its loaded again
			Vector origin = testHull->GetLocalOrigin();
			origin.z += 24.0;
			testHull->SetLocalOrigin( origin );
			status = Editor_CreateNode("info_node", g_pAINetworkManager->GetEditOps()->m_nNextWCIndex, testHull->GetLocalOrigin().x, testHull->GetLocalOrigin().y, testHull->GetLocalOrigin().z, false);
		}
		if (status == Editor_BadCommand)
		{
			Msg( "Worldcraft failed on creation...\n" );
			CAI_TestHull::ReturnTestHull();
		}
		else if (status == Editor_OK)
		{
			// -----------------------
			// Create a new ai node
			// -----------------------
			CNodeEnt *pNodeEnt;
			if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
			{
				pNodeEnt = (CNodeEnt*)CreateEntityByName("info_node_air");
			}
			else
			{
				pNodeEnt = (CNodeEnt*)CreateEntityByName("info_node");
			}

			// Note this is a new entity being created as part of wc editing
			pNodeEnt->SetLocalOrigin( testHull->GetLocalOrigin() );
			CAI_TestHull::ReturnTestHull();

			pNodeEnt->m_NodeData.nWCNodeID =	g_pAINetworkManager->GetEditOps()->m_nNextWCIndex;

			pNodeEnt->m_debugOverlays |= OVERLAY_WC_CHANGE_ENTITY;
			pNodeEnt->Spawn();
		}	
	}

DoneCreate:
	// ----------------------------------------------------------
	// Flash a red box as a warning that the hull won't fit here
	// ----------------------------------------------------------
	if (!bPositionValid)
	{
		NDebugOverlay::Box(vNewNodePos, NAI_Hull::Mins(hullType), NAI_Hull::Maxs(hullType), 255,0,0,0,0.1);
	}

	// Restore player collidability
	pPlayer->SetSolid( SOLID_BBOX );
}
예제 #7
0
//-----------------------------------------------------------------------------
// Purpose: Find a point on my path near to the target and move toward it
//-----------------------------------------------------------------------------
void CTankTrainAI::Think( void )
{
	CFuncTrackTrain *pTrain = m_hTrain;

	if ( !pTrain || pTrain->m_lifeState != LIFE_ALIVE )
	{
		SoundShutdown();
		if ( pTrain )
			UTIL_RemoveHierarchy( pTrain );
		UTIL_Remove( this );
		return;
	}

	int desired = 0;
	CBaseEntity *pTarget = m_hTargetEntity;
	if ( pTarget )
	{
		desired = PathFindDirection( pTrain->m_ppath, pTrain->GetLocalOrigin(), pTarget->GetLocalOrigin() );
	}

	// If the train wants to stop, figure out throttle
	// otherwise, just throttle in the indicated direction and let the train logic
	// clip the speed
	if ( !desired )
	{
		if ( pTrain->m_flSpeed > 0 )
		{
			desired = -1;
		}
		else if ( pTrain->m_flSpeed < 0 )
		{
			desired = 1;
		}
	}
	
	// UNDONE: Align the think time with arrival, and bump this up to a few seconds
	SetNextThink( gpGlobals->curtime + 0.5f );

	if ( desired != 0 )
	{
		int wasMoving = (pTrain->m_flSpeed == 0) ? false : true;
		// chaser wants train to move, send message
		pTrain->SetSpeed( desired );
		int isMoving = (pTrain->m_flSpeed == 0) ? false : true;

		if ( !isMoving && wasMoving )
		{
			SoundEngineStop();
		}
		else if ( isMoving )
		{
			if ( !wasMoving )
			{
				SoundEngineStart();
			}
		}
	}
	else
	{
		SoundEngineStop();
		// UNDONE: Align the think time with arrival, and bump this up to a few seconds
		SetNextThink( gpGlobals->curtime + 1.0f );
	}
}
예제 #8
0
CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void )
{
	CBaseEntity *pSpot = NULL;
	CBaseEntity *pLastSpawnPoint = g_pLastSpawn;
	edict_t		*player = edict();
	const char *pSpawnpointName = "info_player_deathmatch";

	if ( HL2MPRules()->IsTeamplay() == true )
	{
		if ( GetTeamNumber() == TEAM_COMBINE )
		{
			pSpawnpointName = "info_player_combine";
			pLastSpawnPoint = g_pLastCombineSpawn;
		}
		else if ( GetTeamNumber() == TEAM_REBELS )
		{
			pSpawnpointName = "info_player_rebel";
			pLastSpawnPoint = g_pLastRebelSpawn;
		}

		if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL )
		{
			pSpawnpointName = "info_player_deathmatch";
			pLastSpawnPoint = g_pLastSpawn;
		}
	}

	pSpot = pLastSpawnPoint;
	// Randomize the start spot
	for ( int i = random->RandomInt(1,5); i > 0; i-- )
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
	if ( !pSpot )  // skip over the null point
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );

	CBaseEntity *pFirstSpot = pSpot;

	do 
	{
		if ( pSpot )
		{
			// check if pSpot is valid
			if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
			{
				if ( pSpot->GetLocalOrigin() == vec3_origin )
				{
					pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
					continue;
				}

				// if so, go to pSpot
				goto ReturnSpot;
			}
		}
		// increment pSpot
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
	} while ( pSpot != pFirstSpot ); // loop if we're not back to the start

	// we haven't found a place to spawn yet,  so kill any guy at the first spawn point and spawn there
	if ( pSpot )
	{
		CBaseEntity *ent = NULL;
		for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
		{
			// if ent is a client, kill em (unless they are ourselves)
			if ( ent->IsPlayer() && !(ent->edict() == player) )
				ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) );
		}
		goto ReturnSpot;
	}

	if ( !pSpot  )
	{
		pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" );

		if ( pSpot )
			goto ReturnSpot;
	}

ReturnSpot:

	if ( HL2MPRules()->IsTeamplay() == true )
	{
		if ( GetTeamNumber() == TEAM_COMBINE )
		{
			g_pLastCombineSpawn = pSpot;
		}
		else if ( GetTeamNumber() == TEAM_REBELS ) 
		{
			g_pLastRebelSpawn = pSpot;
		}
	}

	g_pLastSpawn = pSpot;

	m_flSlamProtectTime = gpGlobals->curtime + 0.5;

	return pSpot;
} 
CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void )
{
	CBaseEntity *pSpot = NULL;
	CBaseEntity *pLastSpawnPoint = g_pLastSpawn;
	edict_t		*player = edict();
	const char *pSpawnpointName = "info_player_deathmatch";

	pSpot = pLastSpawnPoint;
	// Randomize the start spot
	for ( int i = random->RandomInt(1,5); i > 0; i-- )
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
	if ( !pSpot )  // skip over the null point
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );

	CBaseEntity *pFirstSpot = pSpot;

	do 
	{
		if ( pSpot )
		{
			// check if pSpot is valid
			if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
			{
				if ( pSpot->GetLocalOrigin() == vec3_origin )
				{
					pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
					continue;
				}

				// if so, go to pSpot
				goto ReturnSpot;
			}
		}
		// increment pSpot
		pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
	} while ( pSpot != pFirstSpot ); // loop if we're not back to the start

	// we haven't found a place to spawn yet,  so kill any guy at the first spawn point and spawn there
	if ( pSpot )
	{
		CBaseEntity *ent = NULL;
		for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
		{
			// if ent is a client, kill em (unless they are ourselves)
			if ( ent->IsPlayer() && !(ent->edict() == player) )
				ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) );
		}
		goto ReturnSpot;
	}

	if ( !pSpot  )
	{
		char szMapName[256];
		Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName));
		Q_strlower(szMapName);

		//TDT - Information: Although we don't support official maps for gaming, they are useful for testing and these maps for whatever reason spawn you in the wrong location. As such this
		// code is here to force players to spawn at the beginning of the selected maps. Custom maps won't require this as they will have deathmatch/class based player starts.
		if (!Q_strnicmp(szMapName, "d1_canals_01a", 13)
			|| !Q_strnicmp(szMapName, "d1_canals_03", 12)
			|| !Q_strnicmp(szMapName, "d1_canals_13", 12)
			|| !Q_strnicmp(szMapName, "d1_town_01", 10)
			|| !Q_strnicmp(szMapName, "d1_town_01a", 11)
			|| !Q_strnicmp(szMapName, "d1_town_02", 10)
			|| !Q_strnicmp(szMapName, "d1_town_02a", 11)
			|| !Q_strnicmp(szMapName, "d1_town_03", 10)
			|| !Q_strnicmp(szMapName, "d1_town_04", 10)
			|| !Q_strnicmp(szMapName, "d1_town_05", 10)
			|| !Q_strnicmp(szMapName, "d2_coast_03", 11)
			|| !Q_strnicmp(szMapName, "d2_coast_08", 11)
			|| !Q_strnicmp(szMapName, "d2_coast_11", 11)
			|| !Q_strnicmp(szMapName, "d2_prison_01", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_02", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_03", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_04", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_05", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_06", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_07", 12)
			|| !Q_strnicmp(szMapName, "d2_prison_08", 12)
			|| !Q_strnicmp(szMapName, "d3_c17_08", 9)
			|| !Q_strnicmp(szMapName, "d3_citadel_01", 13)
			|| !Q_strnicmp(szMapName, "d3_citadel_02", 13)
			|| !Q_strnicmp(szMapName, "d3_citadel_03", 13)
			|| !Q_strnicmp(szMapName, "d3_citadel_04", 13)
			|| !Q_strnicmp(szMapName, "d3_citadel_05", 13)
			|| !Q_strnicmp(szMapName, "d3_breen_01", 11)
			|| !Q_strnicmp(szMapName, "ep1_c17_00", 10)
			|| !Q_strnicmp(szMapName, "ep1_c17_00a", 11)
			|| !Q_strnicmp(szMapName, "ep1_c17_02b", 11)
			|| !Q_strnicmp(szMapName, "ep1_c17_05", 10)
			|| !Q_strnicmp(szMapName, "ep2_outland_01a", 15)
			|| !Q_strnicmp(szMapName, "ep2_outland_03", 14)
			|| !Q_strnicmp(szMapName, "ep2_outland_08", 14)
			|| !Q_strnicmp(szMapName, "ep2_outland_06", 14)
			)
		{
			CBaseEntity *pEntity = NULL;
			CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin());
			Vector vecOrigin = pPlayer->GetAbsOrigin();
			pEntity = gEntList.FindEntityByClassnameNearest("item_suit", vecOrigin, 0);


			if (pEntity != NULL)
			{
				vecOrigin = pEntity->GetAbsOrigin();
				pEntity = gEntList.FindEntityByClassnameNearest("info_player_start", vecOrigin, 0);
				pSpot = pEntity;
				pSpawnpointName = "info_player_start";
				goto ReturnSpot;
			}
			else
			{
				pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start");
			}
		}
		else if (!Q_strnicmp(szMapName, "d1_trainstation_05", 18))
		{
			CBaseEntity *pEntity = NULL;
			CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin());
			Vector vecOrigin = pPlayer->GetAbsOrigin();
			pEntity = gEntList.FindEntityByClassnameNearest("npc_alyx", vecOrigin, 0);
			if (pEntity != NULL)
			{
				vecOrigin = pEntity->GetAbsOrigin();
				pEntity = gEntList.FindEntityByClassnameNearest("info_player_start", vecOrigin, 0);
				pSpot = pEntity;
				pSpawnpointName = "info_player_start";
				goto ReturnSpot;
			}
			else
			{
				pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start");
			}
		}
		else
		{
			pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start");
		}

		if ( pSpot )
			goto ReturnSpot;
	}

ReturnSpot:

	g_pLastSpawn = pSpot;

	m_flSlamProtectTime = gpGlobals->curtime + 0.5;

	return pSpot;
}
예제 #10
0
void CWeaponGravityGun::EffectUpdate( void )
{
	Vector start, angles, forward, right;
	trace_t tr;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( !pOwner )
		return;

	m_viewModelIndex = pOwner->entindex();
	// Make sure I've got a view model
	CBaseViewModel *vm = pOwner->GetViewModel();
	if ( vm )
	{
		m_viewModelIndex = vm->entindex();
	}

	pOwner->EyeVectors( &forward, &right, NULL );

	start = pOwner->Weapon_ShootPosition();
	Vector end = start + forward * 4096;

	UTIL_TraceLine( start, end, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
	end = tr.endpos;
	float distance = tr.fraction * 4096;
	if ( tr.fraction != 1 )
	{
		// too close to the player, drop the object
		if ( distance < 36 )
		{
			DetachObject();
			return;
		}
	}

	if ( m_hObject == NULL && tr.DidHitNonWorldEntity() )
	{
		CBaseEntity *pEntity = tr.m_pEnt;
		// inform the object what was hit
		ClearMultiDamage();
		pEntity->DispatchTraceAttack( CTakeDamageInfo( pOwner, pOwner, 0, DMG_PHYSGUN ), forward, &tr );
		ApplyMultiDamage();
		AttachObject( pEntity, start, tr.endpos, distance );
		m_lastYaw = pOwner->EyeAngles().y;
	}

	// Add the incremental player yaw to the target transform
	matrix3x4_t curMatrix, incMatrix, nextMatrix;
	QAngle ang(0.0f, pOwner->EyeAngles().y - m_lastYaw, 0.0f);
	AngleMatrix( m_gravCallback.m_targetRotation, curMatrix );
	AngleMatrix( ang, incMatrix );
	ConcatTransforms( incMatrix, curMatrix, nextMatrix );
	MatrixAngles( nextMatrix, m_gravCallback.m_targetRotation );
	m_lastYaw = pOwner->EyeAngles().y;

	CBaseEntity *pObject = m_hObject;
	if ( pObject )
	{
		if ( m_useDown )
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = false;
			}
		}
		else 
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = true;
			}
		}

		if ( m_useDown )
		{
			pOwner->SetPhysicsFlag( PFLAG_DIROVERRIDE, true );
			if ( pOwner->m_nButtons & IN_FORWARD )
			{
				m_distance = UTIL_Approach( 1024, m_distance, gpGlobals->frametime * 100 );
			}
			if ( pOwner->m_nButtons & IN_BACK )
			{
				m_distance = UTIL_Approach( 40, m_distance, gpGlobals->frametime * 100 );
			}
		}

		if ( pOwner->m_nButtons & IN_WEAPON1 )
		{
			m_distance = UTIL_Approach( 1024, m_distance, m_distance * 0.1 );
		}
		if ( pOwner->m_nButtons & IN_WEAPON2 )
		{
			m_distance = UTIL_Approach( 40, m_distance, m_distance * 0.1 );
		}

		// Send the object a physics damage message (0 damage). Some objects interpret this 
		// as something else being in control of their physics temporarily.
		pObject->TakeDamage( CTakeDamageInfo( this, pOwner, 0, DMG_PHYSGUN ) );

		Vector newPosition = start + forward * m_distance;
		// 24 is a little larger than 16 * sqrt(2) (extent of player bbox)
		// HACKHACK: We do this so we can "ignore" the player and the object we're manipulating
		// If we had a filter for tracelines, we could simply filter both ents and start from "start"
		Vector awayfromPlayer = start + forward * 24;

		UTIL_TraceLine( start, awayfromPlayer, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
		if ( tr.fraction == 1 )
		{
			UTIL_TraceLine( awayfromPlayer, newPosition, MASK_SOLID, pObject, COLLISION_GROUP_NONE, &tr );
			Vector dir = tr.endpos - newPosition;
			float distance = VectorNormalize(dir);
			float maxDist = m_gravCallback.m_maxVel * gpGlobals->frametime;
			if ( distance >  maxDist )
			{
				newPosition += dir * maxDist;
		}
		else
		{
			newPosition = tr.endpos;
		}
		}
		else
		{
			newPosition = tr.endpos;
		}

		CreatePelletAttraction( phys_gunglueradius.GetFloat(), pObject );
			
		// If I'm looking more than 20 degrees away from the glue point, then give up
		// This lets the player "gesture" for the glue to let go.
		Vector pelletDir = m_gravCallback.m_worldPosition - start;
		VectorNormalize(pelletDir);
		if ( DotProduct( pelletDir, forward ) < 0.939 )	// 0.939 ~= cos(20deg)
			{
			// lose attach for 2 seconds if you're too far away
			m_glueTime = gpGlobals->curtime + 1;
			}

		if ( m_pelletHeld >= 0 && gpGlobals->curtime > m_glueTime )
		{
			CGravityPellet *pPelletAttract = m_activePellets[m_pelletAttract].pellet;

			g_pEffects->Sparks( pPelletAttract->GetAbsOrigin() );
		}

		m_gravCallback.SetTargetPosition( newPosition );
		Vector dir = (newPosition - pObject->GetLocalOrigin());
		m_movementLength = dir.Length();
	}
	else
	{
		m_gravCallback.SetTargetPosition( end );
	}
	if ( m_pelletHeld >= 0 && gpGlobals->curtime > m_glueTime )
	{
		Vector worldNormal, worldPos;
		GetPelletWorldCoords( m_pelletAttract, &worldPos, &worldNormal );

		m_gravCallback.SetAutoAlign( m_activePellets[m_pelletHeld].localNormal, m_activePellets[m_pelletHeld].pellet->GetLocalOrigin(), worldNormal, worldPos );
	}
	else
	{
		m_gravCallback.ClearAutoAlign();
	}
}
예제 #11
0
void CWeaponGravityGun::EffectUpdate( void )
{
	Vector start, forward, right;
	trace_t tr;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( !pOwner )
		return;

	pOwner->EyeVectors( &forward, &right, NULL );

	start = pOwner->Weapon_ShootPosition();

	TraceLine( &tr );
	Vector end = tr.endpos;
	float distance = tr.fraction * 4096;

	if ( m_hObject == NULL && tr.DidHitNonWorldEntity() )
	{
		CBaseEntity *pEntity = tr.m_pEnt;
		AttachObject( pEntity, GetPhysObjFromPhysicsBone( pEntity, tr.physicsbone ), tr.physicsbone, start, tr.endpos, distance );
	}

	// Add the incremental player yaw to the target transform
	QAngle angles = m_gravCallback.TransformAnglesFromPlayerSpace( m_gravCallback.m_targetRotation, pOwner );

	CBaseEntity *pObject = m_hObject;
	if ( pObject )
	{
		if ( m_useDown )
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = false;
			}
		}
		else 
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = true;
			}
		}

		if ( m_useDown )
		{
#ifndef CLIENT_DLL
			pOwner->SetPhysicsFlag( PFLAG_DIROVERRIDE, true );
#endif
			if ( pOwner->m_nButtons & IN_FORWARD )
			{
				m_distance = Approach( 1024, m_distance, gpGlobals->frametime * 100 );
			}
			if ( pOwner->m_nButtons & IN_BACK )
			{
				m_distance = Approach( 40, m_distance, gpGlobals->frametime * 100 );
			}
		}

		if ( pOwner->m_nButtons & IN_WEAPON1 )
		{
			m_distance = Approach( 1024, m_distance, m_distance * 0.1 );
		}
		if ( pOwner->m_nButtons & IN_WEAPON2 )
		{
			m_distance = Approach( 40, m_distance, m_distance * 0.1 );
		}

		IPhysicsObject *pPhys = GetPhysObjFromPhysicsBone( pObject, m_physicsBone );
		if ( pPhys )
		{
			if ( pPhys->IsAsleep() )
			{
				// on the odd chance that it's gone to sleep while under anti-gravity
				pPhys->Wake();
			}

			Vector newPosition = start + forward * m_distance;
			Vector offset;
			pPhys->LocalToWorld( &offset, m_worldPosition );
			Vector vecOrigin;
			pPhys->GetPosition( &vecOrigin, NULL );
			m_gravCallback.SetTargetPosition( newPosition + (vecOrigin - offset), angles );
			Vector dir = (newPosition - pObject->GetLocalOrigin());
			m_movementLength = dir.Length();
		}
	}
	else
	{
		m_targetPosition = end;
		//m_gravCallback.SetTargetPosition( end, m_gravCallback.m_targetRotation );
	}
}
예제 #12
0
void CWeaponGravityGun::EffectUpdate( void )
{
	Vector start, forward, right;
	trace_t tr;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( !pOwner )
		return;

	pOwner->EyeVectors( &forward, &right, NULL );

	start = pOwner->Weapon_ShootPosition();

	TraceLine( &tr );
	Vector end = tr.endpos;
	float distance = tr.fraction * 4096;
	if ( tr.fraction != 1 )
	{
		// too close to the player, drop the object
		if ( distance < 36 )
		{
			DetachObject();
			return;
		}
	}

	if ( m_hObject == NULL && tr.DidHitNonWorldEntity() )
	{
		CBaseEntity *pEntity = tr.m_pEnt;
		AttachObject( pEntity, start, tr.endpos, distance );
		m_lastYaw = pOwner->EyeAngles().y;
	}

	// Add the incremental player yaw to the target transform
	matrix3x4_t curMatrix, incMatrix, nextMatrix;

	AngleMatrix( m_gravCallback.m_targetRotation, curMatrix );
	AngleMatrix( QAngle(0,pOwner->EyeAngles().y - m_lastYaw,0), incMatrix );
	ConcatTransforms( incMatrix, curMatrix, nextMatrix );
	MatrixAngles( nextMatrix, m_gravCallback.m_targetRotation );
	m_lastYaw = pOwner->EyeAngles().y;

	CBaseEntity *pObject = m_hObject;
	if ( pObject )
	{
		if ( m_useDown )
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = false;
			}
		}
		else 
		{
			if ( pOwner->m_afButtonPressed & IN_USE )
			{
				m_useDown = true;
			}
		}

		if ( m_useDown )
		{
#ifndef CLIENT_DLL
			pOwner->SetPhysicsFlag( PFLAG_DIROVERRIDE, true );
#endif
			if ( pOwner->m_nButtons & IN_FORWARD )
			{
				m_distance = Approach( 1024, m_distance, gpGlobals->frametime * 100 );
			}
			if ( pOwner->m_nButtons & IN_BACK )
			{
				m_distance = Approach( 40, m_distance, gpGlobals->frametime * 100 );
			}
		}

		if ( pOwner->m_nButtons & IN_WEAPON1 )
		{
			m_distance = Approach( 1024, m_distance, m_distance * 0.1 );
#ifdef CLIENT_DLL
			if ( gpGlobals->maxClients > 1 )
			{
				gHUD.m_bSkipClear = false;
			}
#endif
		}
		if ( pOwner->m_nButtons & IN_WEAPON2 )
		{
			m_distance = Approach( 40, m_distance, m_distance * 0.1 );
#ifdef CLIENT_DLL
			if ( gpGlobals->maxClients > 1 )
			{
				gHUD.m_bSkipClear = false;
			}
#endif
		}

		IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
		if ( pPhys && pPhys->IsAsleep() )
		{
			// on the odd chance that it's gone to sleep while under anti-gravity
			pPhys->Wake();
		}

		Vector newPosition = start + forward * m_distance;
		Vector offset;
		pObject->EntityToWorldSpace( m_worldPosition, &offset );
		m_gravCallback.SetTargetPosition( newPosition + (pObject->GetAbsOrigin() - offset), m_gravCallback.m_targetRotation );
		Vector dir = (newPosition - pObject->GetLocalOrigin());
		m_movementLength = dir.Length();
	}
	else
	{
		m_targetPosition = end;
		//m_gravCallback.SetTargetPosition( end, m_gravCallback.m_targetRotation );
	}
}
Vector QUA_helicopter::CalcDamageForceVector( const CTakeDamageInfo &info )
{
	// Already have a damage force in the data, use that.
	if (info.GetDamageForce() != vec3_origin || (info.GetDamageType() & /*DMG_NO_PHYSICS_FORCE*/DMG_BLAST))
	{
		//if( info.GetDamageType() & DMG_BLAST )
		//{
			// Fudge blast forces a little bit, so that each
			// victim gets a slightly different trajectory. 
			// This simulates features that usually vary from
			// person-to-person variables such as bodyweight,
			// which are all indentical for characters using the same model.
			float scale = random->RandomFloat( 0.85, 1.15 );
			Vector force = info.GetDamageForce();
			force.x *= scale;
			force.y *= scale;
			// Try to always exaggerate the upward force because we've got pretty harsh gravity
			force.z *= (force.z > 0) ? 1.15 : scale;
			return force;
		//}

		return info.GetDamageForce();
	}

	CBaseEntity *pForce = info.GetInflictor();
	if ( !pForce )
	{
		pForce = info.GetAttacker();
	}

	if ( pForce )
	{
		// Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
		float forceScale = info.GetDamage() * 75 * 4;

		Vector forceVector;
		// If the damage is a blast, point the force vector higher than usual, this gives 
		// the ragdolls a bodacious "really got blowed up" look.
		if( info.GetDamageType() & DMG_BLAST )
		{
			// exaggerate the force from explosions a little (37.5%)
			forceVector = (GetLocalOrigin() + Vector(0, 0, WorldAlignSize().z) ) - pForce->GetLocalOrigin();
			VectorNormalize(forceVector);
			forceVector *= 1.375f;
		}
		else
		{
			// taking damage from self?  Take a little random force, but still try to collapse on the spot.
			if ( this == pForce )
			{
				forceVector.x = random->RandomFloat( -1.0f, 1.0f );
				forceVector.y = random->RandomFloat( -1.0f, 1.0f );
				forceVector.z = 0.0;
				forceScale = random->RandomFloat( 1000.0f, 2000.0f );
			}
			else
			{
				// UNDONE: Collision forces are baked in to CTakeDamageInfo now
				// UNDONE: Is this MOVETYPE_VPHYSICS code still necessary?
				if ( pForce->GetMoveType() == MOVETYPE_VPHYSICS )
				{
					// killed by a physics object
					IPhysicsObject *pPhysics = VPhysicsGetObject();
					if ( !pPhysics )
					{
						pPhysics = pForce->VPhysicsGetObject();
					}
					pPhysics->GetVelocity( &forceVector, NULL );
					forceScale = pPhysics->GetMass();
				}
				else
				{
					forceVector = GetLocalOrigin() - pForce->GetLocalOrigin();
					VectorNormalize(forceVector);
				}
			}
		}
		return forceVector * forceScale;
	}
	return vec3_origin;
}
예제 #14
0
Vector CASW_Simple_Alien::CalcDeathForceVector( const CTakeDamageInfo &info )
{
	// Already have a damage force in the data, use that.
	if ( info.GetDamageForce() != vec3_origin || (g_pGameRules->Damage_NoPhysicsForce(info.GetDamageType())))
	{
		if( info.GetDamageType() & DMG_BLAST )
		{
			float scale = random->RandomFloat( 0.85, 1.15 );
			Vector force = info.GetDamageForce();
			force.x *= scale;
			force.y *= scale;
			// Try to always exaggerate the upward force because we've got pretty harsh gravity
			force.z *= (force.z > 0) ? 1.15 : scale;
			return force;
		}

		return info.GetDamageForce();
	}

	CBaseEntity *pForce = info.GetInflictor();
	if ( !pForce )
	{
		pForce = info.GetAttacker();
	}

	if ( pForce )
	{
		// Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
		float forceScale = info.GetDamage() * 75 * 4;

		Vector forceVector;
		// If the damage is a blast, point the force vector higher than usual, this gives 
		// the ragdolls a bodacious "really got blowed up" look.
		if( info.GetDamageType() & DMG_BLAST )
		{
			// exaggerate the force from explosions a little (37.5%)
			forceVector = (GetLocalOrigin() + Vector(0, 0, WorldAlignSize().z) ) - pForce->GetLocalOrigin();
			VectorNormalize(forceVector);
			forceVector *= 1.375f;
		}
		else
		{
			// taking damage from self?  Take a little random force, but still try to collapse on the spot.
			if ( this == pForce )
			{
				forceVector.x = random->RandomFloat( -1.0f, 1.0f );
				forceVector.y = random->RandomFloat( -1.0f, 1.0f );
				forceVector.z = 0.0;
				forceScale = random->RandomFloat( 1000.0f, 2000.0f );
			}
			else
			{
				// UNDONE: Collision forces are baked in to CTakeDamageInfo now
				// UNDONE: Is this MOVETYPE_VPHYSICS code still necessary?
				if ( pForce->GetMoveType() == MOVETYPE_VPHYSICS )
				{
					// killed by a physics object
					IPhysicsObject *pPhysics = VPhysicsGetObject();
					if ( !pPhysics )
					{
						pPhysics = pForce->VPhysicsGetObject();
					}
					pPhysics->GetVelocity( &forceVector, NULL );
					forceScale = pPhysics->GetMass();
				}
				else
				{
					forceVector = GetLocalOrigin() - pForce->GetLocalOrigin();
					VectorNormalize(forceVector);
				}
			}
		}
		return forceVector * forceScale;
	}
	return vec3_origin;
}
예제 #15
0
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFastZombie::StartTask( const Task_t *pTask )
{
    switch( pTask->iTask )
    {
    case TASK_FASTZOMBIE_VERIFY_ATTACK:
        // Simply ensure that the zombie still has a valid melee attack
        if( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
        {
            TaskComplete();
        }
        else
        {
            TaskFail("");
        }
        break;

    case TASK_FASTZOMBIE_JUMP_BACK:
    {
        SetActivity( ACT_IDLE );

        RemoveFlag( FL_ONGROUND );

        BeginAttackJump();

        Vector forward;
        AngleVectors( GetLocalAngles(), &forward );

        //
        // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
        //
        UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));

        ApplyAbsVelocityImpulse( forward * -200 + Vector( 0, 0, 200 ) );
    }
    break;

    case TASK_FASTZOMBIE_UNSTICK_JUMP:
    {
        RemoveFlag( FL_ONGROUND );

        // Call begin attack jump. A little bit later if we fail to pathfind, we check
        // this value to see if we just jumped. If so, we assume we've jumped
        // to someplace that's not pathing friendly, and so must jump again to get out.
        BeginAttackJump();

        //
        // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
        //
        UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));

        CBaseEntity *pEnemy = GetEnemy();
        Vector vecJumpDir;

        if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN )
        {
            // Jump off the pipe backwards!
            Vector forward;

            GetVectors( &forward, NULL, NULL );

            ApplyAbsVelocityImpulse( forward * -200 );
        }
        else if( pEnemy )
        {
            vecJumpDir = pEnemy->GetLocalOrigin() - GetLocalOrigin();
            VectorNormalize( vecJumpDir );
            vecJumpDir.z = 0;

            ApplyAbsVelocityImpulse( vecJumpDir * 300 + Vector( 0, 0, 200 ) );
        }
        else
        {
            Msg("UNHANDLED CASE! Stuck Fast Zombie with no enemy!\n");
        }
    }
    break;

    case TASK_WAIT_FOR_MOVEMENT:
        // If we're waiting for movement, that means that pathfinding succeeded, and
        // we're about to be moving. So we aren't stuck. So clear this flag.
        m_fJustJumped = false;

        BaseClass::StartTask( pTask );
        break;

    case TASK_FACE_ENEMY:
    {
        // We don't use the base class implementation of this, because GetTurnActivity
        // stomps our landing scrabble animations (sjb)
        Vector flEnemyLKP = GetEnemyLKP();
        GetMotor()->SetIdealYawToTarget( flEnemyLKP );
    }
    break;

    case TASK_FASTZOMBIE_LAND_RECOVER:
    {
        // Set the ideal yaw
        Vector flEnemyLKP = GetEnemyLKP();
        GetMotor()->SetIdealYawToTarget( flEnemyLKP );

        // figure out which way to turn.
        float flDeltaYaw = GetMotor()->DeltaIdealYaw();

        if( flDeltaYaw < 0 )
        {
            SetIdealActivity( (Activity)ACT_FASTZOMBIE_LAND_RIGHT );
        }
        else
        {
            SetIdealActivity( (Activity)ACT_FASTZOMBIE_LAND_LEFT );
        }


        TaskComplete();
    }
    break;

    case TASK_RANGE_ATTACK1:

        // Make melee attacks impossible until we land!
        m_flNextMeleeAttack = gpGlobals->curtime + 60;

        SetTouch( LeapAttackTouch );
        break;

    case TASK_FASTZOMBIE_DO_ATTACK:
        SetActivity( (Activity)ACT_FASTZOMBIE_LEAP_SOAR );
        break;

    default:
        BaseClass::StartTask( pTask );
        break;
    }
}
예제 #16
0
//=========================================================
// FindNearestFriend
// Scan for nearest, visible friend. If fPlayer is true, look for
// nearest player
//=========================================================
CBaseEntity *CAI_PlayerAlly::FindNearestFriend(bool fPlayer)
{
	CBaseEntity *pFriend = NULL;
	CBaseEntity *pNearest = NULL;
	float range = 10000000.0;
	trace_t tr;
	Vector vecStart = GetAbsOrigin();
	Vector vecCheck;
	int i;
	const char *pszFriend;
	int cfriends;

	vecStart.z = GetAbsMaxs().z;
	
	if (fPlayer)
		cfriends = 1;
	else
		cfriends = TLK_CFRIENDS;

	// for each type of friend...

	for (i = cfriends-1; i > -1; i--)
	{
		if (fPlayer)
		{
			CBaseEntity *pPlayer = UTIL_PlayerByIndex(1);
			if ( pPlayer )
			{
				pszFriend = STRING(pPlayer->m_iClassname);
			}
			else
			{
				pszFriend = "player";
			}
		}
		else
			pszFriend = m_szFriends[FriendNumber(i)];

		if (!pszFriend)
			continue;

		// for each friend in this bsp...
		while (pFriend = gEntList.FindEntityByClassname( pFriend, pszFriend ))
		{
			if (pFriend == this || !pFriend->IsAlive())
				// don't talk to self or dead people
				continue;

			CAI_BaseNPC *pNPC = pFriend->MyNPCPointer();

			// If not a NPC for some reason, or in a script.
			if ( pNPC && (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE))
				continue;

			if( pNPC && pNPC->IsSelected() )
			{
				// Don't bother people that are awaiting orders.
				return false;
			}

			vecCheck = pFriend->GetLocalOrigin();
			vecCheck.z = pFriend->GetAbsMaxs().z;

			// if closer than previous friend, and in range, see if he's visible

			if (range > (vecStart - vecCheck).Length())
			{
				UTIL_TraceLine(vecStart, vecCheck, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);

				if (tr.fraction == 1.0)
				{
					// visible and in range, this is the new nearest scientist
					if ((vecStart - vecCheck).Length() < TALKRANGE_MIN)
					{
						pNearest = pFriend;
						range = (vecStart - vecCheck).Length();
					}
				}
			}
		}
	}
	return pNearest;
}