//-----------------------------------------------------------------------------
// Fling the buster with the physcannon either via punt or launch.
//-----------------------------------------------------------------------------
void CWeaponStriderBuster::Launch( CBasePlayer *pPhysGunUser )
{
	if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) )
	{
		WeaponManager_RemoveManaged( this );
	}

	m_bLaunched = true;

	// Notify all nearby hunters that we were launched.
	Hunter_StriderBusterLaunched( this );

	// Start up the eye glow
	m_hMainGlow = CSprite::SpriteCreate( "sprites/blueglow1.vmt", GetLocalOrigin(), false );

	if ( m_hMainGlow != NULL )
	{
		m_hMainGlow->FollowEntity( this );
		m_hMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 140, kRenderFxNoDissipation );
		m_hMainGlow->SetScale( 2.0f );
		m_hMainGlow->SetGlowProxySize( 8.0f );
	}

	if ( !m_bNoseDiving )
	{
		DispatchParticleEffect( "striderbuster_trail", PATTACH_ABSORIGIN_FOLLOW, this );
	}
	else
	{
		DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this );
	}

	// We get our touch function from the physics system
	SetTouch ( &CWeaponStriderBuster::BusterTouch );

	SetThink( &CWeaponStriderBuster::BusterFlyThink );
	SetNextThink( gpGlobals->curtime );

	gamestats->Event_WeaponFired( pPhysGunUser, true, GetClassname() );
}
void CDHLProjectile::ReceiveMessage( int classID, bf_read &msg )
{
	if ( classID != GetClientClass()->m_ClassID )
	{
		// message is for subclass
		BaseClass::ReceiveMessage( classID, msg );
		return;
	}
	int iType = msg.ReadByte();

	//Server is letting us know that we're about to be deleted
	if ( iType == MSG_NOTIFY_REMOVAL )
	{
		if ( !m_bCollided )
		{
			Vector vecDir = vec3_origin;
			if ( GetAbsVelocity() != vec3_origin )
				vecDir = GetAbsVelocity();
			else
				vecDir = m_vecProjectileVelocity;
			VectorNormalize( vecDir );

			Vector vecStartPos = GetMoveType() == MOVETYPE_CUSTOM ? GetLocalOrigin() : m_vecProjectileOrigin;

			//Try to plant a decal
			trace_t decaltr;
			UTIL_TraceLine( vecStartPos, vecStartPos + (vecDir * 120.0), MASK_SHOT, this, //Pretty long distance, but seems necessary in practice
				COLLISION_GROUP_NONE, &decaltr );
			//DebugDrawLine( decaltr.startpos, decaltr.endpos, 255, 0, 0, false, 3.0f );
			if ( decaltr.DidHit() )
			{
				OnTouch( decaltr, true );
			}

			m_bCollided = true;
		}

	}
}
void CASW_Parasite::Leap( const Vector &vecVel )
{
	SetTouch( &CASW_Parasite::LeapTouch );

	SetCondition( COND_FLOATING_OFF_GROUND );
	SetGroundEntity( NULL );

	m_flIgnoreWorldCollisionTime = gpGlobals->curtime + PARASITE_IGNORE_WORLD_COLLISION_TIME;

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

	SetAbsVelocity( vecVel );

	// Think every frame so the player sees the headcrab where he actually is...
	m_bMidJump = true;
	SetThink( &CASW_Parasite::LeapThink );
	SetNextThink( gpGlobals->curtime );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CASW_Shotgun_Pellet_Predicted::Spawn( void )
{
	Precache();

	SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
	SetSolid( SOLID_BBOX );
	//m_flGravity = 1.0;
	SetFriction( 0.75 );
	SetModel( PELLET_MODEL );	

	SetSize( -Vector(1,1,1), Vector(1,1,1) );
	SetSolid( SOLID_BBOX );
	SetGravity( 0.05f );
	SetCollisionGroup( ASW_COLLISION_GROUP_SHOTGUN_PELLET );

	SetTouch( &CASW_Shotgun_Pellet_Predicted::PelletTouch );	

	SetThink( &CASW_Shotgun_Pellet_Predicted::SUB_Remove );
	SetNextThink( gpGlobals->curtime + 1.0f );

	m_flDamage		= 10;
	m_takedamage	= DAMAGE_NO;

	// Create a white light
	CBasePlayer *player = ToBasePlayer( GetOwnerEntity() );
	if ( player )
	{
		m_hLiveSprite = SPRITE_CREATE_PREDICTABLE( "sprites/chargeball2.vmt", GetLocalOrigin() + Vector(0,0,1), false );
		if ( m_hLiveSprite )
		{
			m_hLiveSprite->SetOwnerEntity( player );
			m_hLiveSprite->SetPlayerSimulated( player );
			m_hLiveSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation );
			m_hLiveSprite->SetBrightness( 255 );
			m_hLiveSprite->SetScale( 0.15, 5.0f );
			m_hLiveSprite->SetAttachment( this, 0 );
		}
	}
}
//-----------------------------------------------------------------------------
// Purpose: Explode
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CPlayer_Missile::LoseMissileControl(void)
{
	// Create a missile to take the place of this one
	CGrenadeHomer *pGrenade = (CGrenadeHomer*)CreateEntityByName( "grenade_homer" );
	if ( pGrenade )
	{
		pGrenade->Spawn();
		pGrenade->SetLocalOrigin( GetLocalOrigin() );
		pGrenade->SetLocalAngles( GetLocalAngles() );
		pGrenade->SetModel( PMISSILE_MISSILE_MODEL );
		pGrenade->SetDamage(m_flDamage);
		pGrenade->SetDamageRadius(m_flDamageRadius);
		pGrenade->Launch(this,NULL,GetAbsVelocity(),0,0,HOMER_SMOKE_TRAIL_OFF);
		pGrenade->m_hRocketTrail[0] = m_hSmokeTrail;
		((SmokeTrail*)(CBaseEntity*)m_hSmokeTrail)->FollowEntity(ENTINDEX(pGrenade->pev));
	}

	m_takedamage	= DAMAGE_NO;
	m_lifeState = LIFE_DEAD;
	StopSound( "Player_Manhack.Fly" );
	ControlDeactivate();
}
//-----------------------------------------------------------------------------
// Purpose: Aim the offset barrel at a position in parent space
// Input  : parentTarget - the position of the target in parent space
// Output : Vector - angles in local space
//-----------------------------------------------------------------------------
QAngle CAPCController::AimBarrelAt( const Vector &parentTarget )
{
	Vector target = parentTarget - GetLocalOrigin();
	float quadTarget = target.LengthSqr();
	float quadTargetXY = target.x*target.x + target.y*target.y;

		// We're trying to aim the offset barrel at an arbitrary point.
		// To calculate this, I think of the target as being on a sphere with 
		// it's center at the origin of the gun.
		// The rotation we need is the opposite of the rotation that moves the target 
		// along the surface of that sphere to intersect with the gun's shooting direction
		// To calculate that rotation, we simply calculate the intersection of the ray 
		// coming out of the barrel with the target sphere (that's the new target position)
		// and use atan2() to get angles

		// angles from target pos to center
		float targetToCenterYaw = atan2( target.y, target.x );
		float centerToGunYaw = atan2( m_barrelPos.y, sqrt( quadTarget - (m_barrelPos.y*m_barrelPos.y) ) );

		float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
		float centerToGunPitch = atan2( -m_barrelPos.z, sqrt( quadTarget - (m_barrelPos.z*m_barrelPos.z) ) );
		return QAngle( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 );
}
Beispiel #7
0
// Assumes this is ALWAYS enabled
CPathTrack *CPathTrack::Nearest( const Vector &origin )
{
	int			deadCount;
	float		minDist, dist;
	Vector		delta;
	CPathTrack	*ppath, *pnearest;


	delta = origin - GetLocalOrigin();
	delta.z = 0;
	minDist = delta.Length();
	pnearest = this;
	ppath = GetNext();

	// Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :)
	deadCount = 0;
	while ( ppath && ppath != this )
	{
		deadCount++;
		if ( deadCount > 9999 )
		{
			Warning( "Bad sequence of path_tracks from %s\n", GetDebugName() );
			Assert(0);
			return NULL;
		}
		delta = origin - ppath->GetLocalOrigin();
		delta.z = 0;
		dist = delta.Length();
		if ( dist < minDist )
		{
			minDist = dist;
			pnearest = ppath;
		}
		ppath = ppath->GetNext();
	}
	return pnearest;
}
//------------------------------------------------------------------------------
// Purpose: routine called every frame when a task is running
// Input  : pTask - the task structure
//------------------------------------------------------------------------------
void CAI_ASW_MeleeBehavior::RunTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{
		case TASK_MELEE_FLIP_AROUND:
			{
				CBaseEntity *pTarget = GetEnemy();
				if ( pTarget )
				{
					GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetLocalOrigin(), AI_KEEP_YAW_SPEED );
				}

				if ( GetOuter()->IsActivityFinished() )
				{
					TaskComplete();
				}
			}
			break;

		default:
			BaseClass::RunTask( pTask );
			break;
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CShieldGrenade::Spawn( void )
{
	BaseClass::Spawn();
	m_LastCollision.Init( 0, 0, 0 );
	SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
	SetSolid( SOLID_BBOX );
	SetGravity( 1.0 );
	SetFriction( 0.9 );
	SetModel( "models/weapons/w_grenade.mdl");
	UTIL_SetSize(this, Vector( -4, -4, -4), Vector(4, 4, 4));
	m_IsEMPed = false;
	m_IsDeployed = false;

	m_flEMPDamageEndTime = 0.0f;

	SetTouch( StickyTouch );
	SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );

	// Create a green light
	m_pLiveSprite = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin() + Vector(0,0,1), false );
	m_pLiveSprite->SetTransparency( kRenderGlow, 0, 0, 255, 128, kRenderFxNoDissipation );
	m_pLiveSprite->SetScale( 1 );
	m_pLiveSprite->SetAttachment( this, 0 );
}
Beispiel #10
0
//-----------------------------------------------------------------------------
//	Little hack to avoid game crash when changing bodygroup (DmitRex)
//-----------------------------------------------------------------------------
void CZombie::SetHeadlessModel( void )
{
	SetModel("");
	CreateRagGib( "models/zombie/classic.mdl", GetLocalOrigin(), GetLocalAngles(), GetLocalVelocity(), 0, ShouldIgniteZombieGib() );
}
void CFuncTank::TrackTarget( void )
{
	trace_t tr;
	bool updateTime = FALSE, lineOfSight;
	QAngle angles;
	Vector barrelEnd;
	CBaseEntity *pTarget = NULL;

	barrelEnd.Init();

	// Get a position to aim for
	if (m_pController)
	{
		// Tanks attempt to mirror the player's angles
		angles = m_pController->EyeAngles();
		SetNextThink( gpGlobals->curtime + 0.05 );
	}
	else
	{
		if ( IsActive() )
		{
			SetNextThink( gpGlobals->curtime + 0.1f );
		}
		else
		{
			return;
		}

		// -----------------------------------
		//  Get world target position
		// -----------------------------------
		barrelEnd = WorldBarrelPosition();
		Vector worldTargetPosition;
		if (m_spawnflags & SF_TANK_AIM_AT_POS)
		{
			worldTargetPosition = m_vTargetPosition;
		}
		else
		{
			CBaseEntity *pEntity = (CBaseEntity *)m_hTarget;
			if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) )
			{
				if ( m_targetEntityName != NULL_STRING )	// New HL2 behavior
				{
					m_hTarget = FindTarget( m_targetEntityName, NULL );
				}
				else	// HL1 style
				{
					m_hTarget = ToBasePlayer( GetContainingEntity( UTIL_FindClientInPVS( edict() ) ) );
				}

				if ( m_hTarget != NULL )
				{
					SetNextThink( gpGlobals->curtime );	// Think again immediately
				}
				else
				{
					if ( IsActive() )
					{
						SetNextThink( gpGlobals->curtime + 2 );	// Wait 2 secs
					}

					if ( m_fireLast !=0 )
					{
						m_OnLoseTarget.FireOutput(this, this);
						m_fireLast = 0;
					}
				}

				return;
			}
			pTarget = pEntity;

			// Calculate angle needed to aim at target
			worldTargetPosition = pEntity->EyePosition();
		}

		float range = (worldTargetPosition - barrelEnd).Length();
		
		if ( !InRange( range ) )
		{
			m_fireLast = 0;
			return;
		}

		UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

		if (m_spawnflags & SF_TANK_AIM_AT_POS)
		{
			updateTime		= TRUE;
			m_sightOrigin	= m_vTargetPosition;
		}
		else
		{
			lineOfSight = FALSE;
			// No line of sight, don't track
			if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget )
			{
				lineOfSight = TRUE;

				CBaseEntity *pInstance = pTarget;
				if ( InRange( range ) && pInstance && pInstance->IsAlive() )
				{
					updateTime = TRUE;

					// Sight position is BodyTarget with no noise (so gun doesn't bob up and down)
					m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false );
				}
			}
		}

		// Convert targetPosition to parent
		angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) );
	}

	// Force the angles to be relative to the center position
	float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter );
	float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter );
	angles.y = m_yawCenter + offsetY;
	angles.x = m_pitchCenter + offsetX;

	// Limit against range in y

	// MDB - don't check pitch! If two func_tanks are meant to align,
	// and one can pitch and the other cannot, this can lead to them getting 
	// different values for angles.y. Nothing is lost by not updating yaw
	// because the target is not in pitch range.

	bool bOutsideYawRange = ( fabs( offsetY ) > m_yawRange + m_yawTolerance );
	bool bOutsidePitchRange = ( fabs( offsetX ) > m_pitchRange + m_pitchTolerance );

	Vector vecToTarget = m_sightOrigin - GetLocalOrigin();

	// if target is outside yaw range
	if ( bOutsideYawRange )
	{
		if ( angles.y > m_yawCenter + m_yawRange )
		{
			angles.y = m_yawCenter + m_yawRange;
		}
		else if ( angles.y < (m_yawCenter - m_yawRange) )
		{
			angles.y = (m_yawCenter - m_yawRange);
		}
	}

	if ( bOutsidePitchRange || bOutsideYawRange || ( vecToTarget.Length() < ( barrelEnd - GetAbsOrigin() ).Length() ) )
	{
		// Don't update if you saw the player, but out of range
		updateTime = false;
	}

	if ( updateTime )
	{
		m_lastSightTime = gpGlobals->curtime;
		m_persist2burst = 0;
	}

	// Move toward target at rate or less
	float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y );

	QAngle vecAngVel = GetLocalAngularVelocity();
	vecAngVel.y = distY * 10;
	vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate );

	// Limit against range in x
	angles.x = clamp( angles.x, m_pitchCenter - m_pitchRange, m_pitchCenter + m_pitchRange );

	// Move toward target at rate or less
	float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x );
	vecAngVel.x = distX  * 10;
	vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate );
	SetLocalAngularVelocity( vecAngVel );

	SetMoveDoneTime( 0.1 );
	if ( m_pController )
		return;

	if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (m_spawnflags & SF_TANK_LINEOFSIGHT) ) )
	{
		bool fire = FALSE;
		Vector forward;
		AngleVectors( GetLocalAngles(), &forward );
		forward = m_parentMatrix.ApplyRotation( forward );


		if ( m_spawnflags & SF_TANK_LINEOFSIGHT )
		{
			float length = (m_maxRange > 0) ? m_maxRange : MAX_TRACE_LENGTH;
			UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

			if ( tr.m_pEnt == pTarget )
				fire = TRUE;
		}
		else
			fire = TRUE;

		if ( fire )
		{
			if (m_fireLast == 0)
			{
				m_OnAquireTarget.FireOutput(this, this);
			}
			FiringSequence( barrelEnd, forward, this );
		}
		else 
		{
			if (m_fireLast !=0)
			{
				m_OnLoseTarget.FireOutput(this, this);
			}
			m_fireLast = 0;
		}
	}
	else 
	{
		if (m_fireLast !=0)
		{
			m_OnLoseTarget.FireOutput(this, this);
		}
		m_fireLast = 0;
	}
}
void CPhysMotor::Spawn( void )
{
	m_motor.m_axis -= GetLocalOrigin();
	VectorNormalize(m_motor.m_axis);
	UTIL_SnapDirectionToAxis( m_motor.m_axis );
}
Beispiel #13
0
void CBaseDoor::Spawn( void )
{
	if( pev->skin == CONTENTS_NONE )
	{
		// normal door
		if( FBitSet( pev->spawnflags, SF_DOOR_PASSABLE ))
			pev->solid = SOLID_NOT;
		else
			pev->solid = SOLID_BSP;
	}
	else
	{
		SetBits( pev->spawnflags, SF_DOOR_SILENT );
		pev->solid = SOLID_NOT; // special contents
	}

	Precache();

	pev->movetype = MOVETYPE_PUSH;
	SET_MODEL( edict(), GetModel() );

	// NOTE: original Half-Life was contain a bug in LinearMove function
	// while m_flWait was equal 0 then object has stopped forever. See code from quake:
	/*
		void LinearMove( Vector vecDest, float flSpeed )
		{
			...
			...
			...
			if( flTravelTime < 0.1f )
			{
				pev->velocity = g_vecZero;
				pev->nextthink = pev->ltime + 0.1f;
				return;
			}
		}
	*/
	// this block was removed from Half-Life and there no difference
	// between wait = 0 and wait = -1. But in Xash this bug was fixed
	// and level-designer errors is now actual. I'm set m_flWait to -1 for compatibility
	if( m_flWait == 0.0f )
		m_flWait = -1;

	if( pev->speed == 0 )
		pev->speed = 100;

	if( pev->movedir == g_vecZero )
		pev->movedir = Vector( 1.0f, 0.0f, 0.0f );

	m_vecPosition1 = GetLocalOrigin();

	// Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
	Vector vecSize = pev->size - Vector( 2, 2, 2 );
	m_vecPosition2 = m_vecPosition1 + (pev->movedir * (DotProductAbs( pev->movedir, vecSize ) - m_flLip));

	ASSERTSZ( m_vecPosition1 != m_vecPosition2, "door start/end positions are equal" );

	if( FBitSet( pev->spawnflags, SF_DOOR_START_OPEN ))
	{	
		UTIL_SetOrigin( this, m_vecPosition2 );
		m_vecPosition2 = m_vecPosition1;
		m_vecPosition1 = GetLocalOrigin();
	}
	else
	{
		UTIL_SetOrigin( this, m_vecPosition1 );
	}

	// another hack: PhysX 2.8.4.0 crashed while trying to created kinematic body from this brush-model
	if ( FStrEq( STRING( gpGlobals->mapname ), "c2a5e" ) && FStrEq( STRING( pev->model ), "*103" ));
	else m_pUserData = WorldPhysic->CreateKinematicBodyFromEntity( this );
	m_iState = STATE_OFF;

	// if the door is flagged for USE button activation only, use NULL touch function
	if( FBitSet( pev->spawnflags, SF_DOOR_USE_ONLY ))
	{
		SetTouch( NULL );
	}
	else
	{
		// touchable button
		SetTouch( DoorTouch );
	}
}
Beispiel #14
0
//-----------------------------------------------------------------------------
// Step iteratively toward a destination position
//-----------------------------------------------------------------------------
AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, bool bTestZ, AIMoveTrace_t *pTraceResult )
{
	// By definition, this will produce different results than GroundMoveLimit() 
	// because there's no guarantee that it will step exactly one step 

	// See how far toward the new position we can step...
	// But don't actually test for ground geometric validity;
	// if it isn't valid, there's not much we can do about it
	AIMoveTrace_t moveTrace;
	unsigned testFlags = AITGM_IGNORE_FLOOR;

	char *pchHackBoolToInt = (char*)(&bTestZ);
	if ( *pchHackBoolToInt == 2 )
	{
		testFlags |= AITGM_CRAWL_LARGE_STEPS;
	}
	else
	{
		if ( !bTestZ )
			testFlags |= AITGM_2D;
	}

#ifdef DEBUG
	if ( ai_draw_motor_movement.GetBool() )
		testFlags |= AITGM_DRAW_RESULTS;
#endif

	GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, GetOuter()->GetAITraceMask(), testFlags, &moveTrace );
	if ( pTraceResult )
	{
		*pTraceResult = moveTrace;
	}

	bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction ));

	// Move forward either if there was no obstruction or if we're told to
	// move as far as we can, regardless
	bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus);
	if ( !bIsBlocked || bAsFarAsCan || bHitTarget )
	{
#ifdef DEBUG
		if ( GetMoveProbe()->CheckStandPosition( GetLocalOrigin(), GetOuter()->GetAITraceMask() ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, GetOuter()->GetAITraceMask() ) )
		{
			DevMsg( 2, "Warning: AI motor probably given invalid instructions\n" );
		}
#endif

		// The true argument here causes it to touch all triggers
		// in the volume swept from the previous position to the current position
		UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);

		// check to see if our ground entity has changed
		// NOTE: This is to detect changes in ground entity as the movement code has optimized out
		// ground checks.  So now we have to do a simple recheck to make sure we detect when we've 
		// stepped onto a new entity.
		if ( GetOuter()->GetFlags() & FL_ONGROUND )
		{
			GetOuter()->PhysicsStepRecheckGround();
		}

		// skip tiny steps, but notify the shadow object of any large steps
		if ( moveTrace.flStepUpDistance > 0.1f )
		{
			float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() );
			IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject();
			if ( pPhysicsObject )
			{
				IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController();
				if ( pShadow )
				{
					pShadow->StepUp( height );
				}
			}
		}
		if ( yaw != -1 )
		{
			QAngle angles = GetLocalAngles();
			angles.y = yaw;
			SetLocalAngles( angles );
		}
		if ( bHitTarget )
			return AIM_PARTIAL_HIT_TARGET;
			
		if ( !bIsBlocked )
			return AIM_SUCCESS;
			
		if ( moveTrace.fStatus == AIMR_BLOCKED_NPC )
			return AIM_PARTIAL_HIT_NPC;

		return AIM_PARTIAL_HIT_WORLD;
	}
	return AIM_FAILED;
}
void CAPCController::TrackTarget( void )
{
	trace_t tr;
	bool updateTime = FALSE, lineOfSight;
	QAngle angles;
	Vector barrelEnd;
	CBaseEntity *pTarget = NULL;

	barrelEnd.Init();

	if ( IsActive() )
	{
		SetNextThink( gpGlobals->curtime + 0.1f );
	}
	else
	{
		return;
	}

	// -----------------------------------
	//  Get world target position
	// -----------------------------------
	barrelEnd = WorldBarrelPosition();
	Vector worldTargetPosition;
	CBaseEntity *pEntity = (CBaseEntity *)m_hTarget;
	if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) )
	{
		m_hTarget = FindTarget( m_targetEntityName, NULL );
		if ( IsActive() )
		{
			SetNextThink( gpGlobals->curtime + 2 );	// Wait 2 sec s
		}

		return;
	}
	pTarget = pEntity;

	// Calculate angle needed to aim at target
	worldTargetPosition = pEntity->EyePosition();

	float range = (worldTargetPosition - barrelEnd).Length();

	if ( !InRange( range ) )
	{
		m_bFireDelayed = false;
		return;
	}

	UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr );

	lineOfSight = FALSE;
	// No line of sight, don't track
	if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget )
	{
		lineOfSight = TRUE;

		CBaseEntity *pInstance = pTarget;
		if ( InRange( range ) && pInstance && pInstance->IsAlive() )
		{
			updateTime = TRUE;

			// Sight position is BodyTarget with no noise (so gun doesn't bob up and down)
			m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false );
		}
	}

	// Convert targetPosition to parent
	angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) );


	// Force the angles to be relative to the center position
	float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter );
	float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter );
	angles.y = m_yawCenter + offsetY;
	angles.x = m_pitchCenter + offsetX;

	// Move toward target at rate or less
	float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y );

	QAngle vecAngVel = GetLocalAngularVelocity();
	vecAngVel.y = distY * 10;
	vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate );

	// Move toward target at rate or less
	float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x );
	vecAngVel.x = distX  * 10;
	vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate );
	SetLocalAngularVelocity( vecAngVel );

	SetMoveDoneTime( 0.1 );

	Vector forward;
	AngleVectors( GetLocalAngles(), &forward );
	forward = m_parentMatrix.ApplyRotation( forward );

	AngleVectors(angles, &forward);

	if ( lineOfSight == TRUE )
	{
		// FIXME: This will ultimately have to deal with NPCs being in the vehicle as well
		// See if the target is in a vehicle. If so, check its relationship
		CBasePlayer *pPlayer = ToBasePlayer( pTarget );
		if ( pPlayer && pPlayer->IsInAVehicle() )
		{
			IServerVehicle *pVehicle = pPlayer->GetVehicle();
			if ( pVehicle->ClassifyPassenger( pPlayer, CLASS_PLAYER ) == CLASS_PLAYER)
			{
				if ( !m_bFireDelayed )
				{
					m_bFireDelayed = true;
					m_flFiringDelay = gpGlobals->curtime + 1.5;	// setup delay time before we start firing
					return;
				}
				if ( gpGlobals->curtime > m_flFiringDelay )
				{
					m_OnFireAtTarget.Set(forward, this, this);		// tell apc to fire rockets, and what direction
				}
			}
		}
	}
	else
	{
		m_bFireDelayed = false;		// reset flag since we can no longer see target
	}
}
Beispiel #16
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pTask - 
//-----------------------------------------------------------------------------
void CNPC_Crow::StartTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
		//
		// This task enables us to build a path that requires flight.
		//
//		case TASK_CROW_PREPARE_TO_FLY:
//		{
//			SetFlyingState( FlyState_Flying );
//			TaskComplete();
//			break;
//		}

		case TASK_CROW_TAKEOFF:
		{
			if ( random->RandomInt( 1, 4 ) == 1 )
			{
				AlertSound();
			}

			FlapSound();

			SetIdealActivity( ( Activity )ACT_CROW_TAKEOFF );
			break;
		}

		case TASK_CROW_PICK_EVADE_GOAL:
		{
			if ( GetEnemy() != NULL )
			{
				//
				// Get our enemy's position in x/y.
				//
				Vector vecEnemyOrigin = GetEnemy()->GetAbsOrigin();
				vecEnemyOrigin.z = GetAbsOrigin().z;

				//
				// Pick a hop goal a random distance along a vector away from our enemy.
				//
				m_vSavePosition = GetAbsOrigin() - vecEnemyOrigin;
				VectorNormalize( m_vSavePosition );
				m_vSavePosition = GetAbsOrigin() + m_vSavePosition * ( 32 + random->RandomInt( 0, 32 ) );

				GetMotor()->SetIdealYawToTarget( m_vSavePosition );
				TaskComplete();
			}
			else
			{
				TaskFail( "No enemy" );
			}
			break;
		}

		case TASK_CROW_FALL_TO_GROUND:
		{
			SetFlyingState( FlyState_Falling );
			break;
		}

		case TASK_FIND_HINTNODE:
		{
			if ( GetGoalEnt() )
			{
				TaskComplete();
				return;
			}
			// Overloaded because we search over a greater distance.
			if ( !GetHintNode() )
			{
				SetHintNode(CAI_HintManager::FindHint( this, HINT_CROW_FLYTO_POINT, bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP, 10000 ));
			}

			if ( GetHintNode() )
			{
				TaskComplete();
			}
			else
			{
				TaskFail( FAIL_NO_HINT_NODE );
			}
			break;
		}

		case TASK_GET_PATH_TO_HINTNODE:
		{
			//How did this happen?!
			if ( GetGoalEnt() == this )
			{
				SetGoalEnt( NULL );
			}

			if ( GetGoalEnt() )
			{
				SetFlyingState( FlyState_Flying );
				StartTargetHandling( GetGoalEnt() );
			
				m_bReachedMoveGoal = false;
				TaskComplete();
				SetHintNode( NULL );
				return;
			}

			if ( GetHintNode() )
			{
				Vector vHintPos;
				GetHintNode()->GetPosition(this, &vHintPos);
		
				SetNavType( NAV_FLY );
				CapabilitiesAdd( bits_CAP_MOVE_FLY );
				if ( !GetNavigator()->SetGoal( vHintPos ) )
					SetHintNode(NULL);
				CapabilitiesRemove( bits_CAP_MOVE_FLY );
			}

			if ( GetHintNode() )
			{
				m_bReachedMoveGoal = false;
				TaskComplete();
			}
			else
			{
				TaskFail( FAIL_NO_ROUTE );
			}
			break;
		}

		//
		// We have failed to fly normally. Pick a random "up" direction and fly that way.
		//
		case TASK_CROW_FLY:
		{
			float flYaw = UTIL_AngleMod( random->RandomInt( -180, 180 ) );

			Vector vecNewVelocity( cos( DEG2RAD( flYaw ) ), sin( DEG2RAD( flYaw ) ), random->RandomFloat( 0.1f, 0.5f ) );
			vecNewVelocity *= CROW_AIRSPEED;
			SetAbsVelocity( vecNewVelocity );

			SetIdealActivity( ACT_FLY );

			m_bSoar = false;
			m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );

			break;
		}

		case TASK_CROW_PICK_RANDOM_GOAL:
		{
			m_vSavePosition = GetLocalOrigin() + Vector( random->RandomFloat( -48.0f, 48.0f ), random->RandomFloat( -48.0f, 48.0f ), 0 );
			TaskComplete();
			break;
		}

		case TASK_CROW_HOP:
		{
			SetIdealActivity( ACT_HOP );
			m_flHopStartZ = GetLocalOrigin().z;
			break;
		}

		case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
		{
			break;
		}

		default:
		{
			BaseClass::StartTask( pTask );
		}
	}
}
Beispiel #17
0
//-----------------------------------------------------------------------------
// Purpose: Catches the monster-specific messages that occur when tagged
//			animation frames are played.
// Input  : pEvent - 
//-----------------------------------------------------------------------------
void CNPC_Crow::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_CROW_TAKEOFF )
	{
		if ( GetNavigator()->GetPath()->GetCurWaypoint() )
		{
			Takeoff( GetNavigator()->GetCurWaypointPos() );
		}
		return;
	}

	if( pEvent->event == AE_CROW_HOP )
	{
		SetGroundEntity( NULL );

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

		//
		// How fast does the crow need to travel to reach the hop goal given gravity?
		//
		float flHopDistance = ( m_vSavePosition - GetLocalOrigin() ).Length();
		float gravity = sv_gravity.GetFloat();
		if ( gravity <= 1 )
		{
			gravity = 1;
		}

		float height = 0.25 * flHopDistance;
		float speed = sqrt( 2 * gravity * height );
		float time = speed / gravity;

		//
		// Scale the sideways velocity to get there at the right time
		//
		Vector vecJumpDir = m_vSavePosition - GetLocalOrigin();
		vecJumpDir = vecJumpDir / time;

		//
		// Speed to offset gravity at the desired height.
		//
		vecJumpDir.z = speed;

		//
		// Don't jump too far/fast.
		//
		float distance = vecJumpDir.Length();
		if ( distance > 650 )
		{
			vecJumpDir = vecJumpDir * ( 650.0 / distance );
		}

		m_nMorale -= random->RandomInt( 1, 6 );
		if ( m_nMorale <= 0 )
		{
			m_nMorale = 0;
		}

		// Play a hop flap sound.
		EmitSound( "NPC_Crow.Hop" );

		SetAbsVelocity( vecJumpDir );
		return;
	}

	if( pEvent->event == AE_CROW_FLY )
	{
		//
		// Start flying.
		//
		SetActivity( ACT_FLY );

		m_bSoar = false;
		m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );

		return;
	}

	CAI_BaseNPC::HandleAnimEvent( pEvent );
}
//-----------------------------------------------------------------------------
// Purpose: Stick to an entity (using hierarchy if we can)
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponStriderBuster::StickToEntity( CBaseEntity *pOther )
{
	// Make sure the object is travelling fast enough to stick
	if ( m_flCollisionSpeedSqr > 50 && !m_bNoseDiving )
	{
		// See if this is a valid strider bit
		if ( ShouldStickToEntity( pOther ) )
		{
			// Attempt to constraint to it
			if ( CreateConstraintToObject( pOther ) )
			{
				// Only works for striders, at the moment
				CBaseEntity *pFollowParent = pOther->GetOwnerEntity();
				if ( pFollowParent == NULL )
					return false;

				// Allows us to identify our constrained object later
				SetOwnerEntity( pFollowParent );

				// Make a sound
				EmitSound( "Weapon_StriderBuster.StickToEntity" );
				
				DispatchParticleEffect( "striderbuster_attach", GetAbsOrigin(), GetAbsAngles(), NULL );

				if( striderbuster_use_particle_flare.GetBool() )
				{
					// We don't have to save any pointers or handles to this because it's parented to the buster.
					// So it will die when the buster dies. Yay.
					CParticleSystem *pFlare = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
					
					if ( pFlare != NULL )
					{
						pFlare->KeyValue( "start_active", "1" );
						pFlare->KeyValue( "effect_name", "striderbuster_attached_pulse" );
						pFlare->SetParent( this );
						pFlare->SetLocalOrigin( vec3_origin );
						DispatchSpawn( pFlare );
						pFlare->Activate();
					}
				}
				else
				{
					// Create a glow sprite
					m_hGlowSprite = CSprite::SpriteCreate( "sprites/orangeflare1.vmt", GetLocalOrigin(), false );

					Assert( m_hGlowSprite );
					if ( m_hGlowSprite != NULL )
					{
						m_hGlowSprite->TurnOn();
						m_hGlowSprite->SetTransparency( kRenderWorldGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
						m_hGlowSprite->SetAbsOrigin( GetAbsOrigin() );
						m_hGlowSprite->SetScale( 5.0f );
						m_hGlowSprite->m_nRenderFX = kRenderFxStrobeFaster;
						m_hGlowSprite->SetGlowProxySize( 16.0f );
						m_hGlowSprite->SetParent( this );
					}
				}

				// Stop touching things
				SetTouch( NULL );

				// Must be a strider
				CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent);
				if ( pStrider == NULL )
					return false;

				// Notify the strider we're attaching to him
				pStrider->StriderBusterAttached( this );
				
				m_OnAttachToStrider.FireOutput( this, this );

				// Start the ping sound.
				SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext );
				
				// Don't autodelete this one!
				WeaponManager_RemoveManaged( this );

				return true;
			}

			return false;
		}
	}

	return false;
}
Beispiel #19
0
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
{
	switch ( pEvent->event )
	{
		case HOUND_AE_WARN:
			// do stuff for this event.
			WarnSound();
			break;

		case HOUND_AE_STARTATTACK:
			WarmUpSound();
			break;

		case HOUND_AE_HOPBACK:
			{
				float flGravity = sv_gravity.GetFloat();

				SetGroundEntity( NULL );

				Vector forward;
				AngleVectors( GetLocalAngles(), &forward );
				Vector vecNewVelocity = forward * -200;
				//jump up 36 inches
				vecNewVelocity.z += sqrt( 2 * flGravity * 36 );
				SetAbsVelocity( vecNewVelocity );
				break;
			}

		case HOUND_AE_THUMP:
			// emit the shockwaves
			SonicAttack();
			m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
			break;

		case HOUND_AE_ANGERSOUND1:
			{
				EmitSound( "NPC_Houndeye.Anger1" );
			}
			break;

		case HOUND_AE_ANGERSOUND2:
			{
			EmitSound( "NPC_Houndeye.Anger2" );
			}
			break;

		case HOUND_AE_CLOSE_EYE:
			if ( !m_fDontBlink )
			{
			//<<TEMP>>	pev->skin = HOUNDEYE_EYE_FRAMES - 1;
			}
			break;

		case HOUND_AE_LEAP_HIT:
			{
				//<<TEMP>>return;//<<TEMP>>
				SetGroundEntity( NULL );

				//
				// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
				//
				UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
				Vector vecJumpDir;
				if ( GetEnemy() != NULL )
				{
					Vector vecEnemyEyePos = GetEnemy()->EyePosition();

					float gravity = sv_gravity.GetFloat();
					if ( gravity <= 1 )
					{
						gravity = 1;
					}

					//
					// How fast does the houndeye need to travel to reach my enemy's eyes given gravity?
					//
					float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
					if ( height < 16 )
					{
						height = 16;
					}
					else if ( height > 120 )
					{
						height = 120;
					}
					float speed = sqrt( 2 * gravity * height );
					float time = speed / gravity;

					//
					// Scale the sideways velocity to get there at the right time
					//
					vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
					vecJumpDir = vecJumpDir / time;

					//
					// Speed to offset gravity at the desired height.
					//
					vecJumpDir.z = speed;

					//
					// Don't jump too far/fast.
					//
					float distance = vecJumpDir.Length();
					if ( distance > 650 )
					{
						vecJumpDir = vecJumpDir * ( 650.0 / distance );
					}
				}
				else
				{
					Vector forward, up;
					AngleVectors( GetLocalAngles(), &forward, NULL, &up );
					//
					// Jump hop, don't care where.
					//
					vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
				}

				SetAbsVelocity( vecJumpDir );
				m_flNextAttack = gpGlobals->curtime + 2;
				break;
			}
		default:
			BaseClass::HandleAnimEvent( pEvent );
			break;
	}
}
void CAI_LeadBehavior::RunTask( const Task_t *pTask )		
{ 
	switch ( pTask->iTask )
	{
		case TASK_LEAD_SUCCEED:
		{
			if ( !IsSpeaking() )
			{
				TaskComplete();
				NotifyEvent( LBE_DONE );
			}
			break;
		}
		case TASK_LEAD_ARRIVE:
		{
			if ( !IsSpeaking() )
			{
				TaskComplete();
				NotifyEvent( LBE_ARRIVAL_DONE );
			}
			break;
		}

		case TASK_LEAD_MOVE_TO_RANGE:
		{
			// If we haven't spoken our start speech, move closer
 			if ( !m_hasspokenstart)
			{
				ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 );
			}
			else
			{
 				ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance );

				if ( !TaskIsComplete() )
				{
					// Transition to a walk when we get near the player
					// Check Z first, and only check 2d if we're within that
					Vector vecGoalPos = GetNavigator()->GetGoalPos();
					float distance = fabs(vecGoalPos.z - GetLocalOrigin().z);
					bool bWithinZ = false;
					if ( distance < m_retrievedistance )
					{
						distance = ( vecGoalPos - GetLocalOrigin() ).Length2D();
						bWithinZ = true;
					}

					if ( distance > m_retrievedistance )
					{
						Activity followActivity = ACT_WALK;
						if ( GetOuter()->GetState() == NPC_STATE_COMBAT || ( (!bWithinZ || distance < (m_retrievedistance*4)) && GetOuter()->GetState() != NPC_STATE_COMBAT ) )
						{
							followActivity = ACT_RUN;
						}

						// Don't confuse move and shoot by resetting the activity every think
						Activity curActivity = GetNavigator()->GetMovementActivity();
						switch( curActivity )
						{
						case ACT_WALK_AIM:	curActivity = ACT_WALK;	break;
						case ACT_RUN_AIM:	curActivity = ACT_RUN;	break;
						}
						
						if ( curActivity != followActivity )
						{
							GetNavigator()->SetMovementActivity(followActivity);
						}
						GetNavigator()->SetArrivalDirection( GetOuter()->GetTarget() );
					}
				}
			}
			break;
		}

		case TASK_LEAD_RETRIEVE_WAIT:
		{
			ChainRunTask( TASK_WAIT_INDEFINITE );
			break;
		}

		case TASK_LEAD_WALK_PATH:
		{
			// If we're leading, and we're supposed to run, run instead of walking
			if ( m_run && 
				( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) )
			{
				ChainRunTask( TASK_RUN_PATH );
			}
			else
			{
				ChainRunTask( TASK_WALK_PATH );
			}

			// While we're walking
			if ( TaskIsRunning() && IsCurSchedule( SCHED_LEAD_PLAYER, false ) )
			{
				// If we're not speaking, and we haven't tried for a while, try to speak lead idle
				if ( m_flNextLeadIdle < gpGlobals->curtime && !IsSpeaking() )
				{
					m_flNextLeadIdle = gpGlobals->curtime + RandomFloat( 10,15 );

					if ( !m_args.iRetrievePlayer && HasCondition( COND_LEAD_FOLLOWER_LOST ) && HasCondition(COND_SEE_PLAYER) )
					{
						Speak( TLK_LEAD_COMINGBACK );
					}
					else
					{
						Speak( TLK_LEAD_IDLE );
					}
				}
			}

			break;
		}

		default:
			BaseClass::RunTask( pTask);
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CAI_LeadBehavior::GatherConditions( void )
{
	BaseClass::GatherConditions();

	if ( HasGoal() )
	{
		// Fix for bad transition case (to investigate)
		if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() > (64*64) && IsCurSchedule( SCHED_LEAD_AWAIT_SUCCESS, false)  )
		{
			GetOuter()->ClearSchedule( "Lead behavior - bad transition?" );
		}

		// We have to collect data about the person we're leading around.
		CBaseEntity *pFollower = AI_GetSinglePlayer();

		if( pFollower )
		{
			ClearCondition( COND_LEAD_FOLLOWER_VERY_CLOSE );
			ClearCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME );

			// Check distance to the follower
			float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length();
			bool bLagging = flFollowerDist > (m_leaddistance*4);
			if ( bLagging )
			{
				if ( PlayerIsAheadOfMe() )
				{
					bLagging = false;
				}
			}

			// Player heading towards me?
			// Only factor this in if you're not too far from them
			if ( flFollowerDist < (m_leaddistance*4) )
			{
				Vector vecVelocity = pFollower->GetSmoothedVelocity();
				if ( VectorNormalize(vecVelocity) > 50 )
				{
					Vector vecToPlayer = (GetAbsOrigin() - pFollower->GetAbsOrigin());
					VectorNormalize( vecToPlayer );
					if ( DotProduct( vecVelocity, vecToPlayer ) > 0.5 )
					{
						SetCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME );
						bLagging = false;
					}
				}
			}

			// If he's outside our lag range, consider him lagging
			if ( bLagging )
			{
				SetCondition( COND_LEAD_FOLLOWER_LAGGING );
				ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );
			}
			else
			{
				ClearCondition( COND_LEAD_FOLLOWER_LAGGING );
				SetCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );

				// If he's really close, note that
				if ( flFollowerDist < m_leaddistance )
				{
					SetCondition( COND_LEAD_FOLLOWER_VERY_CLOSE );
				}
			}

			// To be considered not lagging, the follower must be visible, and within the lead distance
			if ( GetOuter()->FVisible( pFollower ) && GetOuter()->GetSenses()->ShouldSeeEntity( pFollower ) )
			{
				SetCondition( COND_LEAD_HAVE_FOLLOWER_LOS );
				m_LostLOSTimer.Stop();
			}
			else
			{
				ClearCondition( COND_LEAD_HAVE_FOLLOWER_LOS );

				// We don't have a LOS. But if we did have LOS, don't clear it until the timer is up.
				if ( m_LostLOSTimer.IsRunning() )
				{
					if ( m_LostLOSTimer.Expired() )
					{
						SetCondition( COND_LEAD_FOLLOWER_LAGGING );
						ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING );
					}
				}
				else
				{
					m_LostLOSTimer.Start();
				}
			}

			// Now we want to see if the follower is lost. Being lost means being (far away || out of LOS ) 
			// && some time has passed. Also, lagging players are considered lost if the NPC's never delivered
			// the start speech, because it means the NPC should run to the player to start the lead.
			if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) )
			{
				if ( !m_hasspokenstart )
				{
					SetCondition( COND_LEAD_FOLLOWER_LOST );
				}
				else
				{
					if ( m_args.bStopScenesWhenPlayerLost )
					{
						// Try and stop me speaking my monolog, if I am
						if ( !m_hasPausedScenes && IsRunningScriptedScene( GetOuter() ) )
						{
							//Msg("Stopping scenes.\n");
							PauseActorsScriptedScenes( GetOuter(), false );
							m_hasPausedScenes = true;
						}
					}

					if( m_LostTimer.IsRunning() )
					{
						if( m_LostTimer.Expired() )
						{
							SetCondition( COND_LEAD_FOLLOWER_LOST );
						}
					}
					else
					{
						m_LostTimer.Start();
					}
				}
			}
			else
			{
				// If I was speaking a monolog, resume it
				if ( m_args.bStopScenesWhenPlayerLost && m_hasPausedScenes )
				{
					if ( IsRunningScriptedScene( GetOuter() ) )
					{
						//Msg("Resuming scenes.\n");
						ResumeActorsScriptedScenes( GetOuter(), false );
					}

					m_hasPausedScenes = false;
				}

				m_LostTimer.Stop();
				ClearCondition( COND_LEAD_FOLLOWER_LOST );
			}

			// Evaluate for success
			// Success right now means being stationary, close to the goal, and having the player close by
			if ( !( m_args.flags & AILF_NO_DEF_SUCCESS ) )
			{
				ClearCondition( COND_LEAD_SUCCESS );

				// Check Z first, and only check 2d if we're within that
				bool bWithinZ = fabs(GetLocalOrigin().z - m_goal.z) < 64;
				if ( bWithinZ && (GetLocalOrigin() - m_goal).Length2D() <= 64 )
				{
					if ( HasCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ) )
					{
						SetCondition( COND_LEAD_SUCCESS );
					}
					else if ( m_successdistance )
					{
						float flDistSqr = (pFollower->GetAbsOrigin() - GetLocalOrigin()).Length2DSqr();
						if ( flDistSqr < (m_successdistance*m_successdistance) )
						{
							SetCondition( COND_LEAD_SUCCESS );
						}
					}
				}
			}
			if ( m_MoveMonitor.IsMarkSet() && m_MoveMonitor.TargetMoved( pFollower ) )
				SetCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK );
			else
				ClearCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK );
		}
	}

	if( m_args.bLeadDuringCombat )
	{
		ClearCondition( COND_LIGHT_DAMAGE );
		ClearCondition( COND_HEAVY_DAMAGE );
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//			&m_LastMoveTarget - 
//			eMoveType - 
//-----------------------------------------------------------------------------
void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType )
{
	// dvs: something is setting this bit, causing us to stop moving and get stuck that way
	Forget( bits_MEMORY_TURNING );

	Vector Steer, SteerAvoid, SteerRel;
	Vector forward, right, up;

	//Get our orientation vectors.
	GetVectors( &forward, &right, &up);

	if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) )
	{
		SteerSeek( Steer, GetEnemy()->GetAbsOrigin() );
	}
	else
	{
		//If we are approaching our goal, use an arrival steering mechanism.
		if ( eMoveType == ICH_MOVETYPE_ARRIVE )
		{
			SteerArrive( Steer, MoveTarget );
		}
		else
		{
			//Otherwise use a seek steering mechanism.
			SteerSeek( Steer, MoveTarget );
		}
	}
	
#if FEELER_COLLISION

	Vector f, u, l, r, d;

	float	probeLength = GetAbsVelocity().Length();

	if ( probeLength < 150 )
		probeLength = 150;

	if ( probeLength > 500 )
		probeLength = 500;

	f = DoProbe( GetLocalOrigin() + (probeLength * forward) );
	r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) );
	l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) );
	u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) );
	d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) );

	SteerAvoid = f+r+l+u+d;
	
	//NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f );	

	if ( SteerAvoid.LengthSqr() )
	{
		Steer = (SteerAvoid*0.5f);
	}

	m_vecVelocity = m_vecVelocity + (Steer*0.5f);

	VectorNormalize( m_vecVelocity );

	SteerRel.x = forward.Dot( m_vecVelocity );
	SteerRel.y = right.Dot( m_vecVelocity );
	SteerRel.z = up.Dot( m_vecVelocity );

	m_vecVelocity *= m_flGroundSpeed;

#else

	//See if we need to avoid any obstacles.
	if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) )
	{
		//Take the avoidance vector
		Steer = SteerAvoid;
	}

	//Clamp our ideal steering vector to within our physical limitations.
	ClampSteer( Steer, SteerRel, forward, right, up );

	ApplyAbsVelocityImpulse( Steer * flInterval );
	
#endif

	Vector vecNewVelocity = GetAbsVelocity();
	float flLength = vecNewVelocity.Length();

	//Clamp our final speed
	if ( flLength > m_flGroundSpeed )
	{
		vecNewVelocity *= ( m_flGroundSpeed / flLength );
		flLength = m_flGroundSpeed;
	}

	Vector	workVelocity = vecNewVelocity;

	AddSwimNoise( &workVelocity );

	// Pose the fish properly
	SetPoses( SteerRel, flLength );

	//Drag our victim before moving
	if ( m_pVictim != NULL )
	{
		DragVictim( (workVelocity*flInterval).Length() );
	}

	//Move along the current velocity vector
	if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false )
	{
		//Attempt a half-step
		if ( WalkMove( (workVelocity*0.5f) * flInterval,  MASK_NPCSOLID) == false )
		{
			//Restart the velocity
			//VectorNormalize( m_vecVelocity );
			vecNewVelocity *= 0.5f;
		}
		else
		{
			//Cut our velocity in half
			vecNewVelocity *= 0.5f;
		}
	}

	SetAbsVelocity( vecNewVelocity );

}
Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe )
{
	trace_t	tr;
	float	fraction = 1.0f;
	bool	collided = false;
	Vector	normal	 = Vector( 0, 0, -1 );

	float	waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 );

	waterLevel -= GetAbsOrigin().z;
	waterLevel /= 150;

	if ( waterLevel < 1.0f )
	{
		collided = true;
		fraction = waterLevel;
	}

	AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
	
	if ( ( collided == false ) || ( tr.fraction < fraction ) )
	{
		fraction	= tr.fraction;
		normal		= tr.plane.normal;
	}

	if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) )
	{
#if FEELER_COLLISION_VISUALIZE
		NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f );
#endif
		
		Vector	probeDir = probe - GetLocalOrigin();

		Vector	normalToProbeAndWallNormal = probeDir.Cross( normal );
		Vector	steeringVector = normalToProbeAndWallNormal.Cross( probeDir );

		Vector	velDir = GetAbsVelocity();
		VectorNormalize( velDir );

		float	steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir );

		if ( steeringForce < 0.0f )
		{
			steeringForce = -steeringForce;
		}

		velDir = steeringVector;
		VectorNormalize( velDir );

		steeringVector = steeringForce * velDir;
		
		return steeringVector;
	}

#if FEELER_COLLISION_VISUALIZE
	NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f );
#endif

	return Vector( 0.0f, 0.0f, 0.0f );
}
Beispiel #24
0
//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been set.
//-----------------------------------------------------------------------------
void CFuncRotating::Spawn( )
{
#ifdef TF_DLL
	AddSpawnFlags( SF_BRUSH_ROTATE_CLIENTSIDE );
#endif

	//
	// Maintain compatibility with previous maps.
	//
	if (m_flVolume == 0.0)
	{
		m_flVolume = 1.0;
	}

	//
	// If the designer didn't set a sound attenuation, default to one.
	//
	if ( HasSpawnFlags(SF_BRUSH_ROTATE_SMALLRADIUS) )
	{
		m_flAttenuation = ATTN_IDLE;
	}
	else if ( HasSpawnFlags(SF_BRUSH_ROTATE_MEDIUMRADIUS) )
	{
		m_flAttenuation = ATTN_STATIC;
	}
	else if ( HasSpawnFlags(SF_BRUSH_ROTATE_LARGERADIUS) )
	{
		m_flAttenuation = ATTN_NORM;
	}
	else
	{
		m_flAttenuation = ATTN_NORM;
	}

	//
	// Prevent divide by zero if level designer forgets friction!
	//
	if ( m_flFanFriction == 0 )
	{
		m_flFanFriction = 1;
	}
	
	//
	// Build the axis of rotation based on spawnflags.
	//
	if ( HasSpawnFlags(SF_BRUSH_ROTATE_Z_AXIS) )
	{
		m_vecMoveAng = QAngle(0,0,1);
	}
	else if ( HasSpawnFlags(SF_BRUSH_ROTATE_X_AXIS) )
	{
		m_vecMoveAng = QAngle(1,0,0);
	}
	else
	{
		m_vecMoveAng = QAngle(0,1,0);	// y-axis
	}

	//
	// Check for reverse rotation.
	//
	if ( HasSpawnFlags(SF_BRUSH_ROTATE_BACKWARDS) )
	{
		m_vecMoveAng = m_vecMoveAng * -1;
	}

	SetSolid( SOLID_VPHYSICS );

	//
	// Some rotating objects like fake volumetric lights will not be solid.
	//
	if ( HasSpawnFlags(SF_ROTATING_NOT_SOLID) )
	{
		AddSolidFlags( FSOLID_NOT_SOLID );
		SetMoveType( MOVETYPE_PUSH );
	}
	else
	{
		RemoveSolidFlags( FSOLID_NOT_SOLID );
		SetMoveType( MOVETYPE_PUSH );
	}

	SetModel( STRING( GetModelName() ) );

	SetUse( &CFuncRotating::RotatingUse );

	//
	// Did level designer forget to assign a maximum speed? Prevent a divide by
	// zero in RampPitchVol as well as allowing the rotator to work.
	//
	m_flMaxSpeed = fabs( m_flMaxSpeed );
	if (m_flMaxSpeed == 0)
	{
		m_flMaxSpeed = 100;
	}

	//
	// If the brush should be initially rotating, use it in a little while.
	//
	if ( HasSpawnFlags(SF_BRUSH_ROTATE_START_ON) )
	{		
		SetThink( &CFuncRotating::SUB_CallUseToggle );
		SetNextThink( gpGlobals->curtime + .2 );	// leave a magic delay for client to start up
	}

	//
	// Can this brush inflict pain?
	//
	if ( HasSpawnFlags(SF_BRUSH_HURT) )
	{
		SetTouch( &CFuncRotating::HurtTouch );
	}

	//
	// Set speed to 0 in case there's an old "speed" key lying around.
	//
	m_flSpeed = 0;
	
	Precache( );
	CreateVPhysics();

	m_angStart = GetLocalAngles();
	
	// Slam the object back to solid - if we really want it to be solid.
	if ( m_bSolidBsp )
	{
		SetSolid( SOLID_BSP );
	}

	if ( HasSpawnFlags(SF_BRUSH_ROTATE_CLIENTSIDE) )
	{
		m_vecClientOrigin = GetLocalOrigin();
		m_vecClientAngles = GetLocalAngles();
	}
}
Beispiel #25
0
void CMomentaryDoor :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	m_hActivator = pActivator;

	if( IsLockedByMaster( pActivator ))
		return;

	if( useType == USE_RESET )
	{
		// stop moving immediately
		MoveDone();
		return;
	}

	float speed = 0.0f;

	// allow on-off mode that simulate standard door
	if( FBitSet( pev->spawnflags, SF_DOOR_ONOFF_MODE ))
	{
		if( useType == USE_ON )
		{
			useType = USE_SET;
			value = 1.0f;
		}
		else if( useType == USE_OFF )
		{
			useType = USE_SET;
			value = 0.0f;
		}
	}

	// momentary buttons will pass down a float in here
	if( useType != USE_SET ) return;

	value = bound( 0.0f, value, 1.0f );
	Vector move = m_vecPosition1 + (value * ( m_vecPosition2 - m_vecPosition1 ));

	// NOTE: historically momentary_door is completely ignore pev->speed
	// and get local speed that based on distance between new and current position
	// with interval 0.1 secs. This flag is allow constant speed like for func_door
	if( FBitSet( pev->spawnflags, SF_DOOR_CONSTANT_SPEED ))
	{
		speed = pev->speed;
	}
	else
	{	
		// classic Valve method to determine speed
		Vector delta = move - GetLocalOrigin();
		speed = delta.Length() * 10;
	}

	if( move != g_vecZero )
	{
		// This entity only thinks when it moves, so if it's thinking, it's in the process of moving
		// play the sound when it starts moving
		if( m_iState == STATE_OFF ) EMIT_SOUND( edict(), CHAN_STATIC, STRING( pev->noise ), 1, ATTN_NORM );

		m_iState = STATE_ON; // we are "in-moving"

		// clear think (that stops sounds)
		SetThink( NULL );

		LinearMove( move, speed );
	}
}
Beispiel #26
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//			 - 
//			*pTraceResult - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::AutoMovement( float flInterval, CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
{
	bool ignored;
	Vector newPos;
	QAngle newAngles;

	if (flInterval <= 0.0)
		return true;

	m_ScheduleState.bTaskRanAutomovement = true;

	if (GetIntervalMovement( flInterval, ignored, newPos, newAngles ))
	{
		// DevMsg( "%.2f : (%.1f) %.1f %.1f %.1f\n", gpGlobals->curtime, (newPos - GetLocalOrigin()).Length(), newPos.x, newPos.y, newAngles.y );
	
		if ( m_hCine )
		{
			m_hCine->ModifyScriptedAutoMovement( &newPos );
		}

		if (GetMoveType() == MOVETYPE_STEP)
		{
			if (!(GetFlags() & FL_FLY))
			{
				if ( !pTarget )
				{
					pTarget = GetNavTargetEntity();
				}

				// allow NPCs to adjust the automatic movement
				if ( ModifyAutoMovement( newPos ) )
				{
					// Set our motor's speed here
					Vector vecOriginalPosition = GetAbsOrigin();
					bool bResult = false;
					if (!TaskIsComplete())
					{
						bResult = ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
					}

					Vector change = GetAbsOrigin() - vecOriginalPosition;
					if (flInterval != 0)
					{
						change /= flInterval;
					}

					GetMotor()->SetMoveVel(change);

					return bResult;
				}

				return ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
			}
			else
			{
				// FIXME: here's no direct interface to a fly motor, plus this needs to support a state where going through the world is okay.
				// FIXME: add callbacks into the script system for validation
				// FIXME: add function on scripts to force only legal movements
				// FIXME: GetIntervalMovement deals in Local space, nor global.  Currently now way to communicate that through these interfaces.
				SetLocalOrigin( newPos );
				SetLocalAngles( newAngles );
				return true;
			}
		}
		else if (GetMoveType() == MOVETYPE_FLY)
		{
			Vector dist = newPos - GetLocalOrigin();

			VectorScale( dist, 1.0 / flInterval, dist );

			SetLocalVelocity( dist );
			return true;
		}
	}
	return false;
}
Beispiel #27
0
void CBaseDoor::Blocked( CBaseEntity *pOther )
{
	// hurt the blocker a little.
	if( pev->dmg ) pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH );

	// if a door has a negative wait, it would never come back if blocked,
	// so let it just squash the object to death real fast
	if( m_flWait >= 0 )
	{
		if( !FBitSet( pev->spawnflags, SF_DOOR_SILENT ))
			STOP_SOUND( edict(), CHAN_STATIC, STRING( pev->noise1 ));

		if( m_iState == STATE_TURN_OFF )
		{
			DoorGoUp();
		}
		else
		{
			DoorGoDown();
		}
	}

	// block all door pieces with the same targetname here.
	if( !FStringNull( pev->targetname ))
	{
		CBaseDoor *pDoorList[64];
		int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE( pDoorList ));

		for( int i = 0; i < doorCount; i++ )
		{
			CBaseDoor *pDoor = pDoorList[i];

			if( pDoor->m_flWait >= 0)
			{
				if( m_bDoorGroup && pDoor->pev->movedir == pev->movedir && pDoor->GetAbsVelocity() == GetAbsVelocity() && pDoor->GetLocalAvelocity() == GetLocalAvelocity( ))
				{
					pDoor->m_iPhysicsFrame = g_ulFrameCount; // don't run physics this frame if you haven't run yet

					// this is the most hacked, evil, bastardized thing I've ever seen. kjb
					if( !pDoor->IsRotatingDoor( ))
					{
						// set origin to realign normal doors
						pDoor->SetLocalOrigin( GetLocalOrigin( ));
						pDoor->SetAbsVelocity( g_vecZero ); // stop!

					}
					else
					{
						// set angles to realign rotating doors
						pDoor->SetLocalAngles( GetLocalAngles( ));
						pDoor->SetLocalAvelocity( g_vecZero );
					}
				}
			
				if( pDoor->m_iState == STATE_TURN_OFF )
					pDoor->DoorGoUp();
				else pDoor->DoorGoDown();
			}
		}
	}
}
void CAI_PlaneSolver::GenerateSuggestionFromTrace( const AILocalMoveGoal_t &goal, 
												   const AIMoveTrace_t &moveTrace, float probeDist, 
												   float arcCenter, float arcSpan, int probeOffset )
{
	AI_MoveSuggestion_t suggestion;
	AI_MoveSuggType_t type;

	switch ( moveTrace.fStatus )
	{
		case AIMR_BLOCKED_ENTITY:	type = AIMST_AVOID_OBJECT;	break;
		case AIMR_BLOCKED_WORLD:	type = AIMST_AVOID_WORLD;	break;
		case AIMR_BLOCKED_NPC:		type = AIMST_AVOID_NPC;		break;
		case AIMR_ILLEGAL:			type = AIMST_AVOID_DANGER;	break;
		default:					type = AIMST_NO_KNOWLEDGE;	AssertMsg( 0, "Unexpected mode status" ); break; 
	}

	if ( goal.pMoveTarget != NULL && goal.pMoveTarget == moveTrace.pObstruction )
	{
		suggestion.Set( type, 0,
						arcCenter, arcSpan, 
						moveTrace.pObstruction );
						
		m_Solver.AddRegulation( suggestion );

		return;
	}
	
	float clearDist = probeDist - moveTrace.flDistObstructed;
	float pctBlocked = 1.0 - ( clearDist / probeDist );
	
	float weight = CalculateRegulationWeight( moveTrace, pctBlocked );

	if ( weight < 0.001 )
		return;
	
	if ( pctBlocked < 0.5 )
	{
		arcSpan *= pctBlocked * 2.0;
	}

	Vector vecToEnd = moveTrace.vEndPosition - GetLocalOrigin();
	Vector crossProduct;
	bool favorLeft = false, favorRight = false;
	
	if ( moveTrace.fStatus == AIMR_BLOCKED_NPC )
	{
		Vector vecToOther = moveTrace.pObstruction->GetLocalOrigin() - GetLocalOrigin();
		
		CrossProduct(vecToEnd, vecToOther, crossProduct);

		favorLeft  = ( crossProduct.z < 0 );
		favorRight = ( crossProduct.z > 0 );
	}
	else if ( moveTrace.vHitNormal != vec3_origin )
	{
		CrossProduct(vecToEnd, moveTrace.vHitNormal, crossProduct);
		favorLeft  = ( crossProduct.z > 0 );
		favorRight = ( crossProduct.z < 0 );
	}
	
	float thirdSpan = arcSpan / 3.0;
	float favoredWeight = weight * pctBlocked;
	
	suggestion.Set( type, weight,
					arcCenter, thirdSpan, 
					moveTrace.pObstruction );
					
	m_Solver.AddRegulation( suggestion );

	suggestion.Set( type, ( favorRight ) ? favoredWeight : weight,
					arcCenter - thirdSpan, thirdSpan, 
					moveTrace.pObstruction );
					
	m_Solver.AddRegulation( suggestion );

	suggestion.Set( type, ( favorLeft ) ? favoredWeight : weight,
					arcCenter + thirdSpan, thirdSpan, 
					moveTrace.pObstruction );
					
	m_Solver.AddRegulation( suggestion );
}
void CPhysMotor::Activate( void )
{
	BaseClass::Activate();
	
	// This gets called after all objects spawn and after all objects restore
	if ( m_attachedObject == NULL )
	{
		CBaseEntity *pAttach = gEntList.FindEntityByName( NULL, m_nameAttach, NULL );
		if ( pAttach && pAttach->GetMoveType() == MOVETYPE_VPHYSICS )
		{
			m_attachedObject = pAttach;
			IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject();
			CalculateAcceleration();
			matrix3x4_t matrix;
			pPhys->GetPositionMatrix( matrix );
			Vector motorAxis_ls;
			VectorIRotate( m_motor.m_axis, matrix, motorAxis_ls );
			float inertia = DotProductAbs( pPhys->GetInertia(), motorAxis_ls );
			m_motor.m_maxTorque = inertia * m_motor.m_inertiaFactor * (m_angularAcceleration + m_additionalAcceleration);
			m_motor.m_restistanceDamping = 1.0f;
		}
	}

	if ( m_attachedObject )
	{
		IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject();

		// create a hinge constraint for this object?
		if ( m_spawnflags & SF_MOTOR_HINGE )
		{
			// UNDONE: Don't do this on restore?
			if ( !m_pHinge )
			{
				constraint_hingeparams_t hingeParams;
				hingeParams.Defaults();
				hingeParams.worldAxisDirection = m_motor.m_axis;
				hingeParams.worldPosition = GetLocalOrigin();

				m_pHinge = physenv->CreateHingeConstraint( g_PhysWorldObject, pPhys, NULL, hingeParams );
				m_pHinge->SetGameData( (void *)this );
			}

			if ( m_spawnflags & SF_MOTOR_NOCOLLIDE )
			{
				physenv->DisableCollisions( pPhys, g_PhysWorldObject );
			}
		}
		else
		{
			m_pHinge = NULL;
		}

		// NOTE: On restore, this path isn't run because m_pController will not be NULL
		if ( !m_pController )
		{
			m_pController = physenv->CreateMotionController( &m_motor );
			m_pController->AttachObject( m_attachedObject->VPhysicsGetObject() );

			if ( m_spawnflags & SF_MOTOR_START_ON )
			{
				TurnOn();
			}
		}
	}

	// Need to do this on restore since there's no good way to save this
	if ( m_pController )
	{
		m_pController->SetEventHandler( &m_motor );
	}
}
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;
}