diff options
Diffstat (limited to 'mp/src/game/server/hl2/npc_cranedriver.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_cranedriver.cpp | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/mp/src/game/server/hl2/npc_cranedriver.cpp b/mp/src/game/server/hl2/npc_cranedriver.cpp new file mode 100644 index 00000000..c41c4a03 --- /dev/null +++ b/mp/src/game/server/hl2/npc_cranedriver.cpp @@ -0,0 +1,788 @@ +//========= 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()
|