You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
788 lines
21 KiB
788 lines
21 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "ai_network.h" |
|
#include "ai_default.h" |
|
#include "ai_schedule.h" |
|
#include "ai_hull.h" |
|
#include "ai_node.h" |
|
#include "ai_task.h" |
|
#include "ai_senses.h" |
|
#include "ai_navigator.h" |
|
#include "ai_route.h" |
|
#include "entitylist.h" |
|
#include "soundenvelope.h" |
|
#include "gamerules.h" |
|
#include "ndebugoverlay.h" |
|
#include "soundflags.h" |
|
#include "trains.h" |
|
#include "globalstate.h" |
|
#include "vehicle_base.h" |
|
#include "npc_vehicledriver.h" |
|
#include "vehicle_crane.h" |
|
#include "saverestore_utlvector.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
extern ConVar g_debug_vehicledriver; |
|
|
|
//========================================================= |
|
// Custom schedules |
|
//========================================================= |
|
enum |
|
{ |
|
SCHED_CRANE_RANGE_ATTACK1 = LAST_VEHICLEDRIVER_SCHED, |
|
SCHED_CRANE_FIND_LARGE_OBJECT, |
|
SCHED_CRANE_PICKUP_OBJECT, |
|
SCHED_CRANE_FORCED_GO, |
|
SCHED_CRANE_CHASE_ENEMY, |
|
SCHED_CRANE_FORCED_DROP, |
|
}; |
|
|
|
//========================================================= |
|
// Custom tasks |
|
//========================================================= |
|
enum |
|
{ |
|
TASK_CRANE_GET_POSITION_OVER_ENEMY = LAST_VEHICLEDRIVER_TASK, |
|
TASK_CRANE_GET_POSITION_OVER_LASTPOSITION, |
|
TASK_CRANE_GET_POSITION_OVER_OBJECT, |
|
TASK_CRANE_TURN_MAGNET_OFF, |
|
TASK_CRANE_FIND_OBJECT_TO_PICKUP, |
|
TASK_CRANE_DROP_MAGNET, |
|
TASK_END_FORCED_DROP, |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CNPC_CraneDriver : public CNPC_VehicleDriver |
|
{ |
|
DECLARE_CLASS( CNPC_CraneDriver, CNPC_VehicleDriver ); |
|
public: |
|
DECLARE_DATADESC(); |
|
DEFINE_CUSTOM_AI; |
|
|
|
void Spawn( void ); |
|
void Activate( void ); |
|
|
|
// AI |
|
int RangeAttack1Conditions( float flDot, float flDist ); |
|
int TranslateSchedule( int scheduleType ); |
|
int SelectSchedule( void ); |
|
void StartTask( const Task_t *pTask ); |
|
void RunTask( const Task_t *pTask ); |
|
void SetDesiredPosition( const Vector &vecPosition ); |
|
|
|
// Driving |
|
void DriveVehicle( void ); |
|
bool OverrideMove( float flInterval ); |
|
|
|
// Inputs |
|
void InputForcePickup( inputdata_t &inputdata ); |
|
void InputForceDrop( inputdata_t &inputdata ); |
|
|
|
protected: |
|
CHandle<CPropCrane> m_hCrane; |
|
|
|
EHANDLE m_hPickupTarget; |
|
float m_flDistanceToTarget; |
|
CUtlVector< EHANDLE > m_PreviouslyPickedUpObjects; |
|
bool m_bForcedPickup; |
|
bool m_bForcedDropoff; |
|
float m_flDropWait; |
|
float m_flReleasePause; |
|
float m_flReleaseAt; |
|
|
|
// Outputs |
|
COutputEvent m_OnPickedUpObject; |
|
COutputEvent m_OnDroppedObject; |
|
COutputEvent m_OnPausingBeforeDrop; |
|
}; |
|
|
|
BEGIN_DATADESC( CNPC_CraneDriver ) |
|
// Inputs |
|
DEFINE_INPUTFUNC( FIELD_STRING, "ForcePickup", InputForcePickup ), |
|
DEFINE_INPUTFUNC( FIELD_STRING, "ForceDrop", InputForceDrop ), |
|
|
|
//DEFINE_FIELD( m_hCrane, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hPickupTarget, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_flDistanceToTarget, FIELD_FLOAT ), |
|
DEFINE_UTLVECTOR( m_PreviouslyPickedUpObjects, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_bForcedPickup, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bForcedDropoff, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_flDropWait, FIELD_FLOAT ), |
|
DEFINE_KEYFIELD( m_flReleasePause, FIELD_FLOAT, "releasepause" ), |
|
DEFINE_FIELD( m_flReleaseAt, FIELD_FLOAT ), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT( m_OnPickedUpObject, "OnPickedUpObject" ), |
|
DEFINE_OUTPUT( m_OnDroppedObject, "OnDroppedObject" ), |
|
DEFINE_OUTPUT( m_OnPausingBeforeDrop, "OnPausingBeforeDrop" ), |
|
|
|
END_DATADESC() |
|
|
|
LINK_ENTITY_TO_CLASS( npc_cranedriver, CNPC_CraneDriver ); |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
//------------------------------------------------------------------------------ |
|
void CNPC_CraneDriver::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
CapabilitiesClear(); |
|
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 ); |
|
|
|
m_flDistTooFar = 2048.0; |
|
SetDistLook( 2048 ); |
|
|
|
m_PreviouslyPickedUpObjects.Purge(); |
|
m_hPickupTarget = NULL; |
|
m_bForcedPickup = false; |
|
m_bForcedDropoff = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
m_hCrane = dynamic_cast<CPropCrane*>((CBaseEntity*)m_hVehicleEntity); |
|
if ( !m_hCrane ) |
|
{ |
|
Warning( "npc_cranedriver %s couldn't find his crane named %s.\n", STRING(GetEntityName()), STRING(m_iszVehicleName) ); |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
int CNPC_CraneDriver::RangeAttack1Conditions( float flDot, float flDist ) |
|
{ |
|
if ( !HasCondition( COND_SEE_ENEMY ) ) |
|
return COND_NONE; |
|
|
|
// Do our distance check in 2D |
|
Vector2D vecOrigin2D( m_hCrane->GetAbsOrigin().x, m_hCrane->GetAbsOrigin().y ); |
|
Vector2D vecEnemy2D( GetEnemy()->GetAbsOrigin().x, GetEnemy()->GetAbsOrigin().y ); |
|
flDist = (vecOrigin2D - vecEnemy2D).Length(); |
|
|
|
// Maximum & Minimum size of the crane's reach |
|
if ( flDist > MAX_CRANE_FLAT_REACH ) |
|
return COND_TOO_FAR_TO_ATTACK; |
|
|
|
// Crane can't reach any closer than this |
|
if ( flDist < MIN_CRANE_FLAT_REACH ) |
|
return COND_TOO_CLOSE_TO_ATTACK; |
|
|
|
return COND_CAN_RANGE_ATTACK1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CNPC_CraneDriver::SelectSchedule( void ) |
|
{ |
|
if ( HasSpawnFlags(SF_VEHICLEDRIVER_INACTIVE) ) |
|
return BaseClass::SelectSchedule(); |
|
|
|
// If we've got an object to pickup, so go get it |
|
if ( m_hPickupTarget ) |
|
{ |
|
// Only clear the pickup target if we managed to pick something up |
|
if ( m_hCrane->GetTotalMassOnCrane() > 0 ) |
|
{ |
|
if ( m_bForcedPickup ) |
|
{ |
|
m_OnPickedUpObject.FireOutput( m_hPickupTarget, this ); |
|
} |
|
|
|
// Remember what we dropped so we go try something else if we can. |
|
m_PreviouslyPickedUpObjects.AddToTail( m_hPickupTarget ); |
|
m_hPickupTarget = NULL; |
|
} |
|
else |
|
{ |
|
if ( m_NPCState == NPC_STATE_IDLE ) |
|
{ |
|
SetIdealState( NPC_STATE_ALERT ); |
|
} |
|
return SCHED_CRANE_PICKUP_OBJECT; |
|
} |
|
} |
|
|
|
// If we're currently being forced to pickup something, do only that |
|
if ( m_bForcedPickup ) |
|
{ |
|
if ( m_hPickupTarget ) |
|
return SCHED_CRANE_PICKUP_OBJECT; |
|
|
|
// We've picked up our target, we're waiting to be told where to put it |
|
return SCHED_IDLE_STAND; |
|
} |
|
|
|
// If we've been told to drop something off, do that |
|
if ( m_bForcedDropoff ) |
|
return SCHED_CRANE_FORCED_DROP; |
|
|
|
switch ( m_NPCState ) |
|
{ |
|
case NPC_STATE_IDLE: |
|
break; |
|
|
|
case NPC_STATE_ALERT: |
|
break; |
|
|
|
case NPC_STATE_COMBAT: |
|
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) |
|
{ |
|
// Do we have anything on the crane? If not, look for something |
|
if ( m_hCrane->GetTotalMassOnCrane() == 0 ) |
|
return SCHED_CRANE_FIND_LARGE_OBJECT; |
|
|
|
// We've got something on the crane, so try and drop it on the enemy |
|
return SCHED_CRANE_RANGE_ATTACK1; |
|
} |
|
|
|
// We can't attack him, so if we don't have anything on the crane, grab something |
|
if ( m_hCrane->GetTotalMassOnCrane() == 0 ) |
|
return SCHED_CRANE_FIND_LARGE_OBJECT; |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CNPC_CraneDriver::TranslateSchedule( int scheduleType ) |
|
{ |
|
switch ( scheduleType ) |
|
{ |
|
case SCHED_COMBAT_FACE: |
|
// Vehicles can't rotate, so don't try and face |
|
return TranslateSchedule( SCHED_CHASE_ENEMY ); |
|
|
|
case SCHED_ALERT_FACE: |
|
// Vehicles can't rotate, so don't try and face |
|
return SCHED_ALERT_STAND; |
|
|
|
case SCHED_FORCED_GO: |
|
return SCHED_CRANE_FORCED_GO; |
|
|
|
case SCHED_CHASE_ENEMY: |
|
return SCHED_CRANE_CHASE_ENEMY; |
|
} |
|
|
|
return BaseClass::TranslateSchedule(scheduleType); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTask - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::StartTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_WAIT_FOR_MOVEMENT: |
|
break; |
|
|
|
case TASK_CRANE_GET_POSITION_OVER_ENEMY: |
|
{ |
|
if ( !GetEnemy() ) |
|
{ |
|
TaskFail(FAIL_NO_ROUTE); |
|
return; |
|
} |
|
|
|
SetDesiredPosition( GetEnemy()->GetAbsOrigin() ); |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_CRANE_GET_POSITION_OVER_OBJECT: |
|
{ |
|
if ( !m_hPickupTarget ) |
|
{ |
|
TaskFail("No object to pickup!"); |
|
return; |
|
} |
|
|
|
SetDesiredPosition( m_hPickupTarget->GetAbsOrigin() ); |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_CRANE_GET_POSITION_OVER_LASTPOSITION: |
|
{ |
|
SetDesiredPosition( m_vecLastPosition ); |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_CRANE_TURN_MAGNET_OFF: |
|
{ |
|
// If we picked up something, and we were being forced to pick something up, fire our output |
|
if ( m_hCrane->GetTotalMassOnCrane() > 0 && m_bForcedDropoff ) |
|
{ |
|
// Are we supposed to pause first? |
|
if ( m_flReleasePause ) |
|
{ |
|
m_flReleaseAt = gpGlobals->curtime + m_flReleasePause; |
|
m_OnPausingBeforeDrop.FireOutput( this, this ); |
|
return; |
|
} |
|
|
|
m_OnDroppedObject.FireOutput( this, this ); |
|
} |
|
|
|
m_hCrane->TurnMagnetOff(); |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_END_FORCED_DROP: |
|
{ |
|
m_bForcedDropoff = false; |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_CRANE_FIND_OBJECT_TO_PICKUP: |
|
{ |
|
Vector2D vecOrigin2D( m_hCrane->GetAbsOrigin().x, m_hCrane->GetAbsOrigin().y ); |
|
|
|
// Find a large physics object within our reach to pickup |
|
float flLargestMass = 0; |
|
CBaseEntity *pLargestEntity = NULL; |
|
|
|
CBaseEntity *pList[1024]; |
|
Vector delta( m_flDistTooFar, m_flDistTooFar, m_flDistTooFar*2 ); |
|
int count = UTIL_EntitiesInBox( pList, 1024, m_hCrane->GetAbsOrigin() - delta, m_hCrane->GetAbsOrigin() + delta, 0 ); |
|
for ( int i = 0; i < count; i++ ) |
|
{ |
|
if ( !pList[i] ) |
|
continue; |
|
// Ignore the crane & the magnet |
|
if ( pList[i] == m_hCrane || pList[i] == m_hCrane->GetMagnet() ) |
|
continue; |
|
if ( m_PreviouslyPickedUpObjects.Find( pList[i] ) != m_PreviouslyPickedUpObjects.InvalidIndex() ) |
|
continue; |
|
|
|
// Get the VPhysics object |
|
IPhysicsObject *pPhysics = pList[i]->VPhysicsGetObject(); |
|
if ( pPhysics && pList[i]->GetMoveType() == MOVETYPE_VPHYSICS ) |
|
{ |
|
float flMass = pPhysics->GetMass(); |
|
if ( flMass > flLargestMass && (flMass < MAXIMUM_CRANE_PICKUP_MASS) && (flMass > MINIMUM_CRANE_PICKUP_MASS) ) |
|
{ |
|
// Biggest one we've found so far |
|
|
|
// Now make sure it's within our reach |
|
// Do our distance check in 2D |
|
Vector2D vecOrigin2D( m_hCrane->GetAbsOrigin().x, m_hCrane->GetAbsOrigin().y ); |
|
Vector2D vecEnemy2D( pList[i]->GetAbsOrigin().x, pList[i]->GetAbsOrigin().y ); |
|
float flDist = (vecOrigin2D - vecEnemy2D).Length(); |
|
// Maximum & Minimum size of the crane's reach |
|
if ( flDist > MAX_CRANE_FLAT_REACH ) |
|
continue; |
|
if ( flDist < MIN_CRANE_FLAT_REACH ) |
|
continue; |
|
|
|
flLargestMass = flMass; |
|
pLargestEntity = pList[i]; |
|
} |
|
} |
|
} |
|
|
|
// If we didn't find anything new, clear our list of targets |
|
if ( !pLargestEntity ) |
|
{ |
|
m_PreviouslyPickedUpObjects.Purge(); |
|
} |
|
|
|
if ( !pLargestEntity ) |
|
{ |
|
TaskFail("Couldn't find anything to pick up!"); |
|
return; |
|
} |
|
|
|
m_hPickupTarget = pLargestEntity; |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_CRANE_DROP_MAGNET: |
|
{ |
|
// Drop the magnet, but only end the task once the magnet's back up |
|
m_pVehicleInterface->NPC_SecondaryFire(); |
|
|
|
// Don't check to see if drop's finished until this time is up. |
|
// This is necessary because the crane won't start dropping this |
|
// frame, and our cranedriver will think it's finished immediately. |
|
m_flDropWait = gpGlobals->curtime + 0.5; |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::RunTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_WAIT_FOR_MOVEMENT: |
|
{ |
|
// Is the magnet over the target, and are we not moving too fast? |
|
AngularImpulse angVel; |
|
Vector vecVelocity; |
|
CBaseEntity *pMagnet = m_hCrane->GetMagnet(); |
|
IPhysicsObject *pVehiclePhysics = pMagnet->VPhysicsGetObject(); |
|
pVehiclePhysics->GetVelocity( &vecVelocity, &angVel ); |
|
float flVelocity = 100; |
|
float flDistance = 90; |
|
|
|
// More accurate on forced drops |
|
if ( m_bForcedPickup || m_bForcedDropoff ) |
|
{ |
|
flVelocity = 10; |
|
flDistance = 30; |
|
} |
|
|
|
if ( m_flDistanceToTarget < flDistance && m_hCrane->GetTurnRate() < 0.1 && vecVelocity.Length() < flVelocity ) |
|
{ |
|
TaskComplete(); |
|
} |
|
} |
|
break; |
|
|
|
case TASK_CRANE_DROP_MAGNET: |
|
{ |
|
// Wait for the magnet to get back up |
|
if ( m_flDropWait < gpGlobals->curtime && !m_hCrane->IsDropping() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
} |
|
break; |
|
|
|
case TASK_CRANE_TURN_MAGNET_OFF: |
|
{ |
|
// We're waiting for the pause length before dropping whatever's on our magnet |
|
if ( gpGlobals->curtime > m_flReleaseAt ) |
|
{ |
|
if ( m_bForcedDropoff ) |
|
{ |
|
m_OnDroppedObject.FireOutput( this, this ); |
|
} |
|
|
|
m_hCrane->TurnMagnetOff(); |
|
TaskComplete(); |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_CraneDriver::OverrideMove( float flInterval ) |
|
{ |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::SetDesiredPosition( const Vector &vecPosition ) |
|
{ |
|
m_vecDesiredPosition = vecPosition; |
|
m_flDistanceToTarget = 999; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: This takes the current place the NPC's trying to get to, figures out |
|
// what keys to press to get the vehicle to go there, and then sends |
|
// them to the vehicle. |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::DriveVehicle( void ) |
|
{ |
|
// No targets? |
|
if ( !GetEnemy() && m_vecDesiredPosition == vec3_origin ) |
|
return; |
|
|
|
Vector vecTarget = m_vecDesiredPosition; |
|
// Track our targets |
|
if ( m_hPickupTarget ) |
|
{ |
|
vecTarget = m_hPickupTarget->GetAbsOrigin(); |
|
} |
|
else if ( !m_bForcedPickup && !m_bForcedDropoff && GetEnemy() ) |
|
{ |
|
vecTarget = GetEnemy()->GetAbsOrigin(); |
|
} |
|
|
|
// Move the crane over the target |
|
// Use the crane type as a targeting point |
|
Vector vecCraneTip = m_hCrane->GetCraneTipPosition(); |
|
Vector2D vecCraneTip2D( vecCraneTip.x, vecCraneTip.y ); |
|
Vector2D vecTarget2D( vecTarget.x, vecTarget.y ); |
|
Vector2D vecOrigin2D( m_hCrane->GetAbsOrigin().x, m_hCrane->GetAbsOrigin().y ); |
|
|
|
if ( g_debug_vehicledriver.GetInt() ) |
|
{ |
|
NDebugOverlay::Box( vecTarget, -Vector(50,50,50), Vector(50,50,50), 0,255,0, true, 0.1 ); |
|
NDebugOverlay::Box( vecCraneTip, -Vector(2,2,5000), Vector(2,2,5), 0,255,0, true, 0.1 ); |
|
NDebugOverlay::Box( vecTarget, -Vector(2,2,5), Vector(2,2,5000), 0,255,0, true, 0.1 ); |
|
} |
|
// Store off the distance to our target |
|
m_flDistanceToTarget = (vecTarget2D - vecCraneTip2D).Length(); |
|
|
|
// First determine whether we need to extend / retract the arm |
|
float flDistToTarget = (vecOrigin2D - vecTarget2D).LengthSqr(); |
|
float flDistToCurrent = (vecOrigin2D - vecCraneTip2D).LengthSqr(); |
|
float flDelta = fabs(flDistToTarget - flDistToCurrent); |
|
// Slow down as we get closer, but do it based upon our current extension rate |
|
float flMinDelta = 50 + (50 * fabs(m_hCrane->GetExtensionRate() / CRANE_EXTENSION_RATE_MAX)); |
|
flMinDelta *= flMinDelta; |
|
if ( flDelta > flMinDelta ) |
|
{ |
|
if ( flDistToCurrent > flDistToTarget ) |
|
{ |
|
// Retract |
|
m_pVehicleInterface->NPC_ThrottleReverse(); |
|
} |
|
else if ( flDistToCurrent < flDistToTarget ) |
|
{ |
|
// Extend |
|
m_pVehicleInterface->NPC_ThrottleForward(); |
|
} |
|
} |
|
else |
|
{ |
|
m_pVehicleInterface->NPC_ThrottleCenter(); |
|
} |
|
|
|
// Then figure out if we need to rotate. Do it all in 2D space. |
|
Vector vecRight, vecForward; |
|
m_hCrane->GetVectors( &vecForward, &vecRight, NULL ); |
|
vecRight.z = 0; |
|
vecForward.z = 0; |
|
VectorNormalize( vecRight ); |
|
VectorNormalize( vecForward ); |
|
Vector vecToTarget = ( vecTarget - m_hCrane->GetAbsOrigin() ); |
|
vecToTarget.z = 0; |
|
VectorNormalize( vecToTarget ); |
|
float flDotRight = DotProduct( vecRight, vecToTarget ); |
|
float flDotForward = DotProduct( vecForward, vecToTarget ); |
|
|
|
// Start slowing if we're going to hit the point soon |
|
float flTurnInDeg = RAD2DEG( acos(flDotForward) ); |
|
float flSpeed = m_hCrane->GetMaxTurnRate() * (flTurnInDeg / 15.0); |
|
flSpeed = MIN( m_hCrane->GetMaxTurnRate(), flSpeed ); |
|
if ( fabs(flSpeed) < 0.05 ) |
|
{ |
|
// We're approaching the target, so stop turning |
|
m_pVehicleInterface->NPC_TurnCenter(); |
|
} |
|
else |
|
{ |
|
if ( flDotRight < 0 ) |
|
{ |
|
// Turn right |
|
m_pVehicleInterface->NPC_TurnRight( flSpeed ); |
|
} |
|
else if ( flDotRight > 0 ) |
|
{ |
|
// Turn left |
|
m_pVehicleInterface->NPC_TurnLeft( flSpeed ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Force the driver to pickup a specific entity |
|
// Input : &inputdata - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::InputForcePickup( inputdata_t &inputdata ) |
|
{ |
|
string_t iszPickupName = inputdata.value.StringID(); |
|
if ( iszPickupName != NULL_STRING ) |
|
{ |
|
// Turn the magnet off now to drop anything we might have already on the magnet |
|
m_hCrane->TurnMagnetOff(); |
|
m_hPickupTarget = gEntList.FindEntityByName( NULL, iszPickupName, NULL, inputdata.pActivator, inputdata.pCaller ); |
|
m_bForcedPickup = true; |
|
m_bForcedDropoff = false; |
|
SetCondition( COND_PROVOKED ); |
|
CLEARBITS( m_spawnflags, SF_VEHICLEDRIVER_INACTIVE ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Force the driver to drop his held entity at a specific point |
|
// Input : &inputdata - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_CraneDriver::InputForceDrop( inputdata_t &inputdata ) |
|
{ |
|
string_t iszDropName = inputdata.value.StringID(); |
|
if ( iszDropName != NULL_STRING ) |
|
{ |
|
CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, iszDropName, NULL, inputdata.pActivator, inputdata.pCaller ); |
|
if ( !pEntity ) |
|
{ |
|
Warning("Crane couldn't find entity named %s\n", STRING(iszDropName) ); |
|
return; |
|
} |
|
m_bForcedPickup = false; |
|
m_bForcedDropoff = true; |
|
SetDesiredPosition( pEntity->GetAbsOrigin() ); |
|
SetCondition( COND_PROVOKED ); |
|
CLEARBITS( m_spawnflags, SF_VEHICLEDRIVER_INACTIVE ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Schedules |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
AI_BEGIN_CUSTOM_NPC( npc_cranedriver, CNPC_CraneDriver ) |
|
|
|
//Tasks |
|
DECLARE_TASK( TASK_CRANE_GET_POSITION_OVER_ENEMY ) |
|
DECLARE_TASK( TASK_CRANE_GET_POSITION_OVER_LASTPOSITION ) |
|
DECLARE_TASK( TASK_CRANE_GET_POSITION_OVER_OBJECT ) |
|
DECLARE_TASK( TASK_CRANE_TURN_MAGNET_OFF ) |
|
DECLARE_TASK( TASK_END_FORCED_DROP ) |
|
DECLARE_TASK( TASK_CRANE_FIND_OBJECT_TO_PICKUP ) |
|
DECLARE_TASK( TASK_CRANE_DROP_MAGNET ) |
|
|
|
//Schedules |
|
//================================================== |
|
// SCHED_ANTLION_CHASE_ENEMY_BURROW |
|
//================================================== |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_RANGE_ATTACK1, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_CRANE_GET_POSITION_OVER_ENEMY 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_CRANE_TURN_MAGNET_OFF 0" |
|
" " |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_NEW_ENEMY" |
|
" COND_ENEMY_OCCLUDED" |
|
" COND_ENEMY_TOO_FAR" |
|
" COND_PROVOKED" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_FIND_LARGE_OBJECT, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_CRANE_FIND_OBJECT_TO_PICKUP 0" |
|
" " |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_NEW_ENEMY" |
|
" COND_ENEMY_OCCLUDED" |
|
" COND_ENEMY_TOO_FAR" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_PICKUP_OBJECT, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" |
|
" TASK_CRANE_GET_POSITION_OVER_OBJECT 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_CRANE_DROP_MAGNET 0" |
|
" " |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_NEW_ENEMY" |
|
" COND_ENEMY_OCCLUDED" |
|
" COND_ENEMY_TOO_FAR" |
|
" COND_PROVOKED" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_FORCED_GO, |
|
|
|
" Tasks" |
|
" TASK_CRANE_GET_POSITION_OVER_LASTPOSITION 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_CRANE_TURN_MAGNET_OFF 0" |
|
" TASK_WAIT 2" |
|
" " |
|
" Interrupts" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_CHASE_ENEMY, |
|
|
|
" Tasks" |
|
" TASK_CRANE_GET_POSITION_OVER_ENEMY 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_WAIT 5" |
|
" " |
|
" Interrupts" |
|
" COND_NEW_ENEMY" |
|
" COND_ENEMY_DEAD" |
|
" COND_ENEMY_UNREACHABLE" |
|
" COND_CAN_RANGE_ATTACK1" |
|
" COND_TOO_CLOSE_TO_ATTACK" |
|
" COND_TASK_FAILED" |
|
" COND_LOST_ENEMY" |
|
" COND_PROVOKED" |
|
) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_CRANE_FORCED_DROP, |
|
|
|
" Tasks" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_CRANE_TURN_MAGNET_OFF 0" |
|
" TASK_END_FORCED_DROP 0" |
|
" TASK_WAIT 2" |
|
" " |
|
" Interrupts" |
|
) |
|
|
|
AI_END_CUSTOM_NPC()
|
|
|