Advertisement
Guest User

Untitled

a guest
Sep 14th, 2011
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 86.32 KB | None | 0 0
  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7.  
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_hull.h"
  13. #include "ai_squadslot.h"
  14. #include "ai_basenpc.h"
  15. #include "ai_navigator.h"
  16. #include "ai_interactions.h"
  17. #include "ndebugoverlay.h"
  18. #include "explode.h"
  19. #include "bitstring.h"
  20. #include "vstdlib/random.h"
  21. #include "engine/IEngineSound.h"
  22. #include "decals.h"
  23. #include "antlion_dust.h"
  24. #include "ai_memory.h"
  25. #include "ai_squad.h"
  26. #include "ai_senses.h"
  27. #include "beam_shared.h"
  28. #include "iservervehicle.h"
  29. #include "SoundEmitterSystem/isoundemittersystembase.h"
  30. #include "physics_saverestore.h"
  31. #include "vphysics/constraints.h"
  32. #include "vehicle_base.h"
  33. #include "eventqueue.h"
  34. #include "te_effect_dispatch.h"
  35. #include "npc_rollermine.h"
  36. #include "func_break.h"
  37. #include "soundenvelope.h"
  38. #include "mapentities.h"
  39. #include "RagdollBoogie.h"
  40. #include "physics_collisionevent.h"
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43.  
  44. #define ROLLERMINE_MAX_TORQUE_FACTOR    5
  45. extern short g_sModelIndexWExplosion;
  46.  
  47. ConVar  sk_rollermine_shock( "sk_rollermine_shock","0");
  48. ConVar  sk_rollermine_stun_delay("sk_rollermine_stun_delay", "1");
  49. ConVar  sk_rollermine_vehicle_intercept( "sk_rollermine_vehicle_intercept","1");
  50.  
  51. enum
  52. {
  53.     ROLLER_SKIN_REGULAR = 0,
  54.     ROLLER_SKIN_FRIENDLY,
  55.     ROLLER_SKIN_DETONATE,
  56. };
  57. //-----------------------------------------------------------------------------
  58. // CRollerController implementation
  59. //-----------------------------------------------------------------------------
  60. //-----------------------------------------------------------------------------
  61. // Purpose: This class only implements the IMotionEvent-specific behavior
  62. //          It keeps track of the forces so they can be integrated
  63. //-----------------------------------------------------------------------------
  64. class CRollerController : public IMotionEvent
  65. {
  66.     DECLARE_SIMPLE_DATADESC();
  67.  
  68. public:
  69.     IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  70.  
  71.     AngularImpulse  m_vecAngular;
  72.     Vector          m_vecLinear;
  73.  
  74.     void Off( void ) { m_fIsStopped = true; }
  75.     void On( void ) { m_fIsStopped = false; }
  76.  
  77.     bool IsOn( void ) { return !m_fIsStopped; }
  78.  
  79. private:
  80.     bool    m_fIsStopped;
  81. };
  82.  
  83. BEGIN_SIMPLE_DATADESC( CRollerController )
  84.  
  85.     DEFINE_FIELD( m_vecAngular, FIELD_VECTOR ),
  86.     DEFINE_FIELD( m_vecLinear, FIELD_VECTOR ),
  87.     DEFINE_FIELD( m_fIsStopped, FIELD_BOOLEAN ),
  88.  
  89. END_DATADESC()
  90.  
  91.  
  92. //-----------------------------------------------------------------------------
  93. IMotionEvent::simresult_e CRollerController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  94. {
  95.     if( m_fIsStopped )
  96.     {
  97.         return SIM_NOTHING;
  98.     }
  99.  
  100.     linear = m_vecLinear;
  101.     angular = m_vecAngular;
  102.    
  103.     return IMotionEvent::SIM_LOCAL_ACCELERATION;
  104. }
  105. //-----------------------------------------------------------------------------
  106.  
  107.  
  108. #define ROLLERMINE_IDLE_SEE_DIST                    2048
  109. #define ROLLERMINE_NORMAL_SEE_DIST                  2048
  110. #define ROLLERMINE_WAKEUP_DIST                      256
  111. #define ROLLERMINE_SEE_VEHICLESONLY_BEYOND_IDLE     300     // See every other than vehicles upto this distance (i.e. old idle see dist)
  112. #define ROLLERMINE_SEE_VEHICLESONLY_BEYOND_NORMAL   800     // See every other than vehicles upto this distance (i.e. old normal see dist)
  113.  
  114. #define ROLLERMINE_RETURN_TO_PLAYER_DIST            (200*200)
  115.  
  116. #define ROLLERMINE_MIN_ATTACK_DIST  1
  117. #define ROLLERMINE_MAX_ATTACK_DIST  4096
  118.  
  119. #define ROLLERMINE_OPEN_THRESHOLD   256
  120.  
  121. #define ROLLERMINE_VEHICLE_OPEN_THRESHOLD   400
  122. #define ROLLERMINE_VEHICLE_HOP_THRESHOLD    300
  123.  
  124. #define ROLLERMINE_HOP_DELAY                2           // Don't allow hops faster than this
  125.  
  126. //#define ROLLERMINE_REQUIRED_TO_EXPLODE_VEHICLE        4
  127.  
  128. #define ROLLERMINE_FEAR_DISTANCE            (300*300)
  129.  
  130. //=========================================================
  131. // Custom schedules
  132. //=========================================================
  133. enum
  134. {
  135.     SCHED_ROLLERMINE_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE,
  136.     SCHED_ROLLERMINE_CHASE_ENEMY,
  137.     SCHED_ROLLERMINE_BURIED_WAIT,
  138.     SCHED_ROLLERMINE_BURIED_UNBURROW,
  139.     SCHED_ROLLERMINE_FLEE,
  140.     SCHED_ROLLERMINE_ALERT_STAND,
  141.     SCHED_ROLLERMINE_NUDGE_TOWARDS_NODES,
  142.     SCHED_ROLLERMINE_PATH_TO_PLAYER,
  143.     SCHED_ROLLERMINE_ROLL_TO_PLAYER,
  144.     SCHED_ROLLERMINE_POWERDOWN,
  145. };
  146.  
  147. //=========================================================
  148. // Custom tasks
  149. //=========================================================
  150. enum
  151. {
  152.     TASK_ROLLERMINE_CHARGE_ENEMY = LAST_SHARED_TASK,
  153.     TASK_ROLLERMINE_BURIED_WAIT,
  154.     TASK_ROLLERMINE_UNBURROW,
  155.     TASK_ROLLERMINE_GET_PATH_TO_FLEE,
  156.     TASK_ROLLERMINE_NUDGE_TOWARDS_NODES,
  157.     TASK_ROLLERMINE_RETURN_TO_PLAYER,
  158.     TASK_ROLLERMINE_POWERDOWN,
  159. };
  160.  
  161.  
  162. // This are little 'sound event' flags. Set the flag after you play the
  163. // sound, and the sound will not be allowed to play until the flag is then cleared.
  164. #define ROLLERMINE_SE_CLEAR     0x00000000
  165. #define ROLLERMINE_SE_CHARGE    0x00000001
  166. #define ROLLERMINE_SE_TAUNT     0x00000002
  167. #define ROLLERMINE_SE_SHARPEN   0x00000004
  168. #define ROLLERMINE_SE_TOSSED    0x00000008
  169.  
  170. enum rollingsoundstate_t { ROLL_SOUND_NOT_READY = 0, ROLL_SOUND_OFF, ROLL_SOUND_CLOSED, ROLL_SOUND_OPEN };
  171.  
  172. //=========================================================
  173. //=========================================================
  174. class CNPC_RollerMine : public CNPCBaseInteractive<CAI_BaseNPC>, public CDefaultPlayerPickupVPhysics
  175. {
  176.     DECLARE_CLASS( CNPC_RollerMine, CNPCBaseInteractive<CAI_BaseNPC> );
  177.     DECLARE_SERVERCLASS();
  178.  
  179. public:
  180.  
  181.     CNPC_RollerMine( void ) { m_bTurnedOn = true; m_bUniformSight = false; }
  182.     ~CNPC_RollerMine( void );
  183.  
  184.     void    Spawn( void );
  185.     bool    CreateVPhysics();
  186.     void    RunAI();
  187.     void    StartTask( const Task_t *pTask );
  188.     void    RunTask( const Task_t *pTask );
  189.     void    SpikeTouch( CBaseEntity *pOther );
  190.     void    ShockTouch( CBaseEntity *pOther );
  191.     void    CloseTouch( CBaseEntity *pOther );
  192.     void    EmbedTouch( CBaseEntity *pOther );
  193.     float   GetAttackDamageScale( CBaseEntity *pVictim );
  194.     void    VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
  195.     void    Precache( void );
  196.     void    OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
  197.     void    OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason );
  198.     void    StopLoopingSounds( void );
  199.     void    PrescheduleThink();
  200.     bool    ShouldSavePhysics() { return true; }
  201.     void    OnRestore();
  202.     void    Bury( trace_t *tr );
  203.     bool    QuerySeeEntity(CBaseEntity *pSightEnt, bool bOnlyHateOrFearIfNPC = false );
  204.  
  205.     int     RangeAttack1Conditions ( float flDot, float flDist );
  206.     int     SelectSchedule( void );
  207.     int     TranslateSchedule( int scheduleType );
  208.     int     GetHackedIdleSchedule( void );
  209.  
  210.     bool    OverrideMove( float flInterval ) { return true; }
  211.     bool    IsValidEnemy( CBaseEntity *pEnemy );
  212.     bool    IsPlayerVehicle( CBaseEntity *pEntity );
  213.     bool    IsShocking() { return gpGlobals->curtime < m_flShockTime ? true : false; }
  214.     void    UpdateRollingSound();
  215.     void    UpdatePingSound();
  216.     void    StopRollingSound();
  217.     void    StopPingSound();
  218.     float   RollingSpeed();
  219.     float   GetStunDelay();
  220.     void    EmbedOnGroundImpact();
  221.     void    UpdateEfficiency( bool bInPVS ) { SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL ); SetMoveEfficiency( AIME_NORMAL ); }
  222.     void    DrawDebugGeometryOverlays()
  223.     {
  224.         if (m_debugOverlays & OVERLAY_BBOX_BIT)
  225.         {
  226.             float dist = GetSenses()->GetDistLook();
  227.             Vector range(dist, dist, 64);
  228.             NDebugOverlay::Box( GetAbsOrigin(), -range, range, 255, 0, 0, 0, 0 );
  229.         }
  230.         BaseClass::DrawDebugGeometryOverlays();
  231.     }
  232.     // UNDONE: Put this in the qc file!
  233.     Vector EyePosition()
  234.     {
  235.         // This takes advantage of the fact that the system knows
  236.         // that the abs origin is at the center of the rollermine
  237.         // and that the OBB is actually world-aligned despite the
  238.         // fact that SOLID_VPHYSICS is being used
  239.         Vector eye = CollisionProp()->GetCollisionOrigin();
  240.         eye.z += CollisionProp()->OBBMaxs().z;
  241.         return eye;
  242.     }
  243.  
  244.     int     OnTakeDamage( const CTakeDamageInfo &info );
  245.     void    TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
  246.  
  247.     Class_T Classify()
  248.     {
  249.         if( !m_bTurnedOn )
  250.             return CLASS_NONE;
  251.  
  252.         //About to blow up after being hacked so do damage to the player.
  253.         if ( m_bHackedByAlyx && ( m_flPowerDownDetonateTime > 0.0f && m_flPowerDownDetonateTime <= gpGlobals->curtime ) )
  254.             return CLASS_COMBINE;
  255.  
  256.         return ( m_bHeld || m_bHackedByAlyx ) ? CLASS_HACKED_ROLLERMINE : CLASS_COMBINE;
  257.     }
  258.  
  259.     virtual bool ShouldGoToIdleState()
  260.     {
  261.         return gpGlobals->curtime > m_flGoIdleTime ? true : false;
  262.     }
  263.  
  264.     virtual void OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
  265.  
  266.     // Vehicle interception
  267.     bool    EnemyInVehicle( void );
  268.     float   VehicleHeading( CBaseEntity *pVehicle );
  269.  
  270.     NPC_STATE SelectIdealState();
  271.  
  272.     // Vehicle sticking
  273.     void        StickToVehicle( CBaseEntity *pOther );
  274.     void        AnnounceArrivalToOthers( CBaseEntity *pOther );
  275.     void        UnstickFromVehicle( void );
  276.     CBaseEntity *GetVehicleStuckTo( void );
  277.     int         CountRollersOnMyVehicle( CUtlVector<CNPC_RollerMine*> *pRollerList );
  278.     void        InputConstraintBroken( inputdata_t &inputdata );
  279.     void        InputRespondToChirp( inputdata_t &inputdata );
  280.     void        InputRespondToExplodeChirp( inputdata_t &inputdata );
  281.     void        InputJoltVehicle( inputdata_t &inputdata );
  282.     void        InputTurnOn( inputdata_t &inputdata );
  283.     void        InputTurnOff( inputdata_t &inputdata );
  284.     void        InputPowerdown( inputdata_t &inputdata );
  285.  
  286.     void        PreventUnstickUntil( float flTime ) { m_flPreventUnstickUntil = flTime; }
  287.  
  288.     virtual unsigned int    PhysicsSolidMaskForEntity( void ) const;
  289.  
  290.     void        SetRollerSkin( void );
  291.  
  292.     COutputEvent m_OnPhysGunDrop;
  293.     COutputEvent m_OnPhysGunPickup;
  294.  
  295. protected:
  296.     DEFINE_CUSTOM_AI;
  297.     DECLARE_DATADESC();
  298.  
  299.     bool    BecomePhysical();
  300.     void    WakeNeighbors();
  301.     bool    WakeupMine( CAI_BaseNPC *pNPC );
  302.  
  303.     void    Open( void );
  304.     void    Close( void );
  305.     void    Explode( void );
  306.     void    PreDetonate( void );
  307.     void    Hop( float height );
  308.  
  309.     void    ShockTarget( CBaseEntity *pOther );
  310.  
  311.     bool    IsActive() { return m_flActiveTime > gpGlobals->curtime ? false : true; }
  312.  
  313.     // INPCInteractive Functions
  314.     virtual bool    CanInteractWith( CAI_BaseNPC *pUser ) { return true; }
  315.     virtual bool    HasBeenInteractedWith() { return m_bHackedByAlyx; }
  316.     virtual void    NotifyInteraction( CAI_BaseNPC *pUser );
  317.  
  318.     CSoundPatch                 *m_pRollSound;
  319.     CSoundPatch                 *m_pPingSound;
  320.  
  321.     CRollerController           m_RollerController;
  322.     IPhysicsMotionController    *m_pMotionController;
  323.  
  324.     float   m_flSeeVehiclesOnlyBeyond;
  325.     float   m_flChargeTime;
  326.     float   m_flGoIdleTime;
  327.     float   m_flShockTime;
  328.     float   m_flForwardSpeed;
  329.     int     m_iSoundEventFlags;
  330.     rollingsoundstate_t m_rollingSoundState;
  331.  
  332.     CNetworkVar( bool, m_bIsOpen );
  333.     CNetworkVar( float, m_flActiveTime );   //If later than the current time, this will force the mine to be active
  334.  
  335.     bool    m_bHeld;        //Whether or not the player is holding the mine
  336.     EHANDLE m_hVehicleStuckTo;
  337.     float   m_flPreventUnstickUntil;
  338.     float   m_flNextHop;
  339.     bool    m_bStartBuried;
  340.     bool    m_bBuried;
  341.     bool    m_bIsPrimed;
  342.     bool    m_wakeUp;
  343.     bool    m_bEmbedOnGroundImpact;
  344.     CNetworkVar( bool,  m_bHackedByAlyx );
  345.  
  346.     // Constraint used to stick us to a vehicle
  347.     IPhysicsConstraint *m_pConstraint;
  348.  
  349.     bool    m_bTurnedOn;
  350.     bool    m_bUniformSight;
  351.  
  352.     CNetworkVar( bool,  m_bPowerDown );
  353.     float   m_flPowerDownTime;
  354.     float   m_flPowerDownDetonateTime;
  355.  
  356.     static string_t gm_iszDropshipClassname;
  357. };
  358.  
  359. string_t CNPC_RollerMine::gm_iszDropshipClassname;
  360.  
  361. LINK_ENTITY_TO_CLASS( npc_rollermine, CNPC_RollerMine );
  362.  
  363. //-----------------------------------------------------------------------------
  364. //-----------------------------------------------------------------------------
  365. BEGIN_DATADESC( CNPC_RollerMine )
  366.  
  367.     DEFINE_SOUNDPATCH( m_pRollSound ),
  368.     DEFINE_SOUNDPATCH( m_pPingSound ),
  369.     DEFINE_EMBEDDED( m_RollerController ),
  370.     DEFINE_PHYSPTR( m_pMotionController ),
  371.  
  372.     DEFINE_FIELD( m_flSeeVehiclesOnlyBeyond, FIELD_FLOAT ),
  373.     DEFINE_FIELD( m_flActiveTime, FIELD_TIME ),
  374.     DEFINE_FIELD( m_flChargeTime, FIELD_TIME ),
  375.     DEFINE_FIELD( m_flGoIdleTime, FIELD_TIME ),
  376.     DEFINE_FIELD( m_flShockTime, FIELD_TIME ),
  377.     DEFINE_FIELD( m_flForwardSpeed, FIELD_FLOAT ),
  378.     DEFINE_FIELD( m_bIsOpen, FIELD_BOOLEAN ),
  379.     DEFINE_FIELD( m_bHeld, FIELD_BOOLEAN ),
  380.     DEFINE_FIELD( m_hVehicleStuckTo, FIELD_EHANDLE ),
  381.     DEFINE_FIELD( m_flPreventUnstickUntil, FIELD_TIME ),
  382.     DEFINE_FIELD( m_flNextHop, FIELD_FLOAT ),
  383.     DEFINE_FIELD( m_bIsPrimed, FIELD_BOOLEAN ),
  384.     DEFINE_FIELD( m_iSoundEventFlags, FIELD_INTEGER ),
  385.     DEFINE_FIELD( m_rollingSoundState, FIELD_INTEGER ),
  386.  
  387.     DEFINE_KEYFIELD( m_bStartBuried, FIELD_BOOLEAN, "StartBuried" ),
  388.     DEFINE_FIELD( m_bBuried, FIELD_BOOLEAN ),
  389.     DEFINE_FIELD( m_wakeUp, FIELD_BOOLEAN ),
  390.     DEFINE_FIELD( m_bEmbedOnGroundImpact, FIELD_BOOLEAN ),
  391.     DEFINE_FIELD( m_bHackedByAlyx, FIELD_BOOLEAN ),
  392.  
  393.     DEFINE_FIELD( m_bPowerDown, FIELD_BOOLEAN ),
  394.     DEFINE_FIELD( m_flPowerDownTime,    FIELD_TIME ),
  395.     DEFINE_FIELD( m_flPowerDownDetonateTime,    FIELD_TIME ),
  396.  
  397.     DEFINE_PHYSPTR( m_pConstraint ),
  398.  
  399.     DEFINE_FIELD( m_bTurnedOn, FIELD_BOOLEAN ),
  400.     DEFINE_KEYFIELD( m_bUniformSight, FIELD_BOOLEAN, "uniformsightdist" ),
  401.  
  402.     DEFINE_INPUTFUNC( FIELD_VOID, "ConstraintBroken", InputConstraintBroken ),
  403.     DEFINE_INPUTFUNC( FIELD_VOID, "RespondToChirp", InputRespondToChirp ),
  404.     DEFINE_INPUTFUNC( FIELD_VOID, "RespondToExplodeChirp", InputRespondToExplodeChirp ),
  405.     DEFINE_INPUTFUNC( FIELD_VOID, "JoltVehicle", InputJoltVehicle ),
  406.     DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  407.     DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  408.     DEFINE_INPUTFUNC( FIELD_VOID, "PowerDown", InputPowerdown ),
  409.  
  410.     // Function Pointers
  411.     DEFINE_ENTITYFUNC( SpikeTouch ),
  412.     DEFINE_ENTITYFUNC( ShockTouch ),
  413.     DEFINE_ENTITYFUNC( CloseTouch ),
  414.     DEFINE_ENTITYFUNC( EmbedTouch ),
  415.     DEFINE_THINKFUNC( Explode ),
  416.     DEFINE_THINKFUNC( PreDetonate ),
  417.  
  418.     DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ),
  419.     DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ),
  420.  
  421.     DEFINE_BASENPCINTERACTABLE_DATADESC(),
  422.  
  423. END_DATADESC()
  424.  
  425. IMPLEMENT_SERVERCLASS_ST( CNPC_RollerMine, DT_RollerMine )
  426.     SendPropInt(SENDINFO(m_bIsOpen), 1, SPROP_UNSIGNED ),
  427.     SendPropFloat(SENDINFO(m_flActiveTime), 0, SPROP_NOSCALE ),
  428.     SendPropInt(SENDINFO(m_bHackedByAlyx), 1, SPROP_UNSIGNED ),
  429.     SendPropInt(SENDINFO(m_bPowerDown), 1, SPROP_UNSIGNED ),
  430. END_SEND_TABLE()
  431.  
  432. bool NPC_Rollermine_IsRollermine( CBaseEntity *pEntity )
  433. {
  434.     CNPC_RollerMine *pRoller = dynamic_cast<CNPC_RollerMine *>(pEntity);
  435.     return pRoller ? true : false;
  436. }
  437.  
  438. CBaseEntity *NPC_Rollermine_DropFromPoint( const Vector &originStart, CBaseEntity *pOwner, const char *pszTemplate )
  439. {
  440.     CBaseEntity *pEntity = NULL;
  441.     CNPC_RollerMine *pMine = NULL;
  442.  
  443.     // Use the template, if we have it
  444.     if ( pszTemplate && pszTemplate[0] )
  445.     {
  446.         MapEntity_ParseEntity( pEntity, pszTemplate, NULL );
  447.         pMine = dynamic_cast<CNPC_RollerMine *>(pEntity);
  448.     }
  449.     else
  450.     {
  451.         pMine = (CNPC_RollerMine*)CreateEntityByName("npc_rollermine");
  452.     }
  453.  
  454.     if ( pMine )
  455.     {
  456.         pMine->SetAbsOrigin( originStart );
  457.         pMine->SetOwnerEntity( pOwner );
  458.         pMine->Spawn();
  459.  
  460.         if ( !pszTemplate || !pszTemplate[0] )
  461.         {
  462.             pMine->EmbedOnGroundImpact();
  463.         }
  464.     }
  465.     else
  466.     {
  467.         Warning( "NULL Ent in Rollermine Create!\n" );
  468.     }
  469.  
  470.     return pMine;
  471. }
  472.  
  473. //-----------------------------------------------------------------------------
  474. // Purpose:
  475. //-----------------------------------------------------------------------------
  476. CNPC_RollerMine::~CNPC_RollerMine( void )
  477. {
  478.     if ( m_pMotionController != NULL )
  479.     {
  480.         physenv->DestroyMotionController( m_pMotionController );
  481.         m_pMotionController = NULL;
  482.     }
  483.  
  484.     UnstickFromVehicle();
  485. }
  486.  
  487. //-----------------------------------------------------------------------------
  488. // Purpose:
  489. // Input  :
  490. // Output :
  491. //-----------------------------------------------------------------------------
  492. void CNPC_RollerMine::Precache( void )
  493. {
  494.     PrecacheModel( "models/roller.mdl" );
  495.     PrecacheModel( "models/roller_spikes.mdl" );
  496.  
  497.     PrecacheModel( "sprites/bluelight1.vmt" );
  498.     PrecacheModel( "sprites/rollermine_shock.vmt" );
  499.     PrecacheModel( "sprites/rollermine_shock_yellow.vmt" );
  500.  
  501.     PrecacheScriptSound( "NPC_RollerMine.Taunt" );
  502.     PrecacheScriptSound( "NPC_RollerMine.OpenSpikes" );
  503.     PrecacheScriptSound( "NPC_RollerMine.Warn" );
  504.     PrecacheScriptSound( "NPC_RollerMine.Shock" );
  505.     PrecacheScriptSound( "NPC_RollerMine.ExplodeChirp" );
  506.     PrecacheScriptSound( "NPC_RollerMine.Chirp" );
  507.     PrecacheScriptSound( "NPC_RollerMine.ChirpRespond" );
  508.     PrecacheScriptSound( "NPC_RollerMine.ExplodeChirpRespond" );
  509.     PrecacheScriptSound( "NPC_RollerMine.JoltVehicle" );
  510.     PrecacheScriptSound( "NPC_RollerMine.Tossed" );
  511.     PrecacheScriptSound( "NPC_RollerMine.Hurt" );
  512.  
  513.     PrecacheScriptSound( "NPC_RollerMine.Roll" );
  514.     PrecacheScriptSound( "NPC_RollerMine.RollWithSpikes" );
  515.     PrecacheScriptSound( "NPC_RollerMine.Ping" );
  516.     PrecacheScriptSound( "NPC_RollerMine.Held" );
  517.  
  518.     PrecacheScriptSound( "NPC_RollerMine.Reprogram" );
  519.  
  520.     PrecacheMaterial( "effects/rollerglow" );
  521.  
  522.     gm_iszDropshipClassname = AllocPooledString( "npc_combinedropship" ); // For fast string compares.
  523. #ifdef HL2_EPISODIC
  524.     PrecacheScriptSound( "RagdollBoogie.Zap" );
  525. #endif
  526.  
  527.     BaseClass::Precache();
  528. }
  529.  
  530.  
  531. //-----------------------------------------------------------------------------
  532. // Purpose:
  533. // Input  :
  534. // Output :
  535. //-----------------------------------------------------------------------------
  536. void CNPC_RollerMine::Spawn( void )
  537. {
  538.     Precache();
  539.  
  540.     SetSolid( SOLID_VPHYSICS );
  541.     AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED | FSOLID_NOT_STANDABLE );
  542.  
  543.     BaseClass::Spawn();
  544.  
  545.     AddEFlags( EFL_NO_DISSOLVE );
  546.  
  547.     CapabilitiesClear();
  548.     CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_SQUAD );
  549.  
  550.     m_pRollSound = NULL;
  551.  
  552.     m_bIsOpen = true;
  553.     Close();
  554.  
  555.     m_bPowerDown = false;
  556.    
  557.     m_flFieldOfView     = -1.0f;
  558.     m_flForwardSpeed    = -1200;
  559.     m_bloodColor        = DONT_BLEED;
  560.  
  561.     SetHullType(HULL_SMALL_CENTERED);
  562.  
  563.     SetHullSizeNormal();
  564.  
  565.     m_flActiveTime      = 0;
  566.  
  567.     m_bBuried = m_bStartBuried;
  568.     if ( m_bStartBuried )
  569.     {
  570.         trace_t tr;
  571.         Bury( &tr );
  572.     }
  573.  
  574.     NPCInit();
  575.  
  576.     m_takedamage = DAMAGE_EVENTS_ONLY;
  577.     SetDistLook( ROLLERMINE_IDLE_SEE_DIST );
  578.  
  579.     if( m_bUniformSight )
  580.     {
  581.         m_flSeeVehiclesOnlyBeyond = ROLLERMINE_IDLE_SEE_DIST;
  582.     }
  583.     else
  584.     {
  585.         m_flSeeVehiclesOnlyBeyond = ROLLERMINE_SEE_VEHICLESONLY_BEYOND_IDLE;
  586.     }
  587.  
  588.     //Suppress superfluous warnings from animation system
  589.     m_flGroundSpeed = 20;
  590.     m_NPCState      = NPC_STATE_NONE;
  591.  
  592.     m_rollingSoundState = ROLL_SOUND_OFF;
  593.  
  594.     m_pConstraint = NULL;
  595.     m_hVehicleStuckTo = NULL;
  596.     m_flPreventUnstickUntil = 0;
  597.     m_flNextHop = 0;
  598.  
  599.     m_flPowerDownDetonateTime = 0.0f;
  600.     m_bPowerDown = false;
  601.     m_flPowerDownTime = 0.0f;
  602.  
  603.     //Set their yaw speed to 0 so the motor doesn't rotate them.
  604.     GetMotor()->SetYawSpeed( 0.0f );
  605.     SetRollerSkin();
  606. }
  607.  
  608. //-----------------------------------------------------------------------------
  609. // Set the contents types that are solid by default to this NPC
  610. //-----------------------------------------------------------------------------
  611. unsigned int CNPC_RollerMine::PhysicsSolidMaskForEntity( void ) const
  612. {
  613.     if ( HasSpawnFlags( SF_ROLLERMINE_PROP_COLLISION ) )
  614.         return MASK_SOLID;
  615.  
  616.     return MASK_NPCSOLID;
  617. }
  618.  
  619. //-----------------------------------------------------------------------------
  620. // Purpose:
  621. //-----------------------------------------------------------------------------
  622. void CNPC_RollerMine::Bury( trace_t *tr )
  623. {
  624.     AI_TraceHull( GetAbsOrigin() + Vector(0,0,64), GetAbsOrigin() - Vector( 0, 0, MAX_TRACE_LENGTH ), Vector(-16,-16,-16), Vector(16,16,16), MASK_NPCSOLID, this, GetCollisionGroup(), tr );
  625.  
  626.     //NDebugOverlay::Box( tr->startpos, Vector(-16,-16,-16), Vector(16,16,16), 255, 0, 0, 64, 10.0 );
  627.     //NDebugOverlay::Box( tr->endpos, Vector(-16,-16,-16), Vector(16,16,16), 0, 255, 0, 64, 10.0 );
  628.  
  629.     // Move into the ground layer
  630.     Vector buriedPos = tr->endpos - Vector( 0, 0, GetHullHeight() * 0.5 );
  631.     Teleport( &buriedPos, NULL, &vec3_origin );
  632.     SetMoveType( MOVETYPE_NONE );
  633.  
  634.     SetSchedule( SCHED_ROLLERMINE_BURIED_WAIT );
  635. }
  636.  
  637. //-----------------------------------------------------------------------------
  638. // Purpose:
  639. //-----------------------------------------------------------------------------
  640. bool CNPC_RollerMine::WakeupMine( CAI_BaseNPC *pNPC )
  641. {
  642.     if ( pNPC && pNPC->m_iClassname == m_iClassname && pNPC != this )
  643.     {
  644.         CNPC_RollerMine *pMine = dynamic_cast<CNPC_RollerMine *>(pNPC);
  645.         if ( pMine )
  646.         {
  647.             if ( pMine->m_NPCState == NPC_STATE_IDLE )
  648.             {
  649.                 pMine->m_wakeUp = false;
  650.                 pMine->SetIdealState( NPC_STATE_ALERT );
  651.                 return true;
  652.             }
  653.         }
  654.     }
  655.  
  656.     return false;
  657. }
  658.  
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. //-----------------------------------------------------------------------------
  662. void CNPC_RollerMine::WakeNeighbors()
  663. {
  664.     if ( !m_wakeUp || !IsActive() )
  665.         return;
  666.     m_wakeUp = false;
  667.  
  668.     if ( m_pSquad )
  669.     {
  670.         AISquadIter_t iter;
  671.         for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  672.         {
  673.             WakeupMine( pSquadMember );
  674.         }
  675.         return;
  676.     }
  677.  
  678.     CBaseEntity *entityList[64];
  679.     Vector range(ROLLERMINE_WAKEUP_DIST,ROLLERMINE_WAKEUP_DIST,64);
  680.     int boxCount = UTIL_EntitiesInBox( entityList, ARRAYSIZE(entityList), GetAbsOrigin()-range, GetAbsOrigin()+range, FL_NPC );
  681.     //NDebugOverlay::Box( GetAbsOrigin(), -range, range, 255, 0, 0, 64, 10.0 );
  682.     int wakeCount = 0;
  683.     while ( boxCount > 0 )
  684.     {
  685.         boxCount--;
  686.         CAI_BaseNPC *pNPC = entityList[boxCount]->MyNPCPointer();
  687.         if ( WakeupMine( pNPC ) )
  688.         {
  689.             wakeCount++;
  690.             if ( wakeCount >= 2 )
  691.                 return;
  692.         }
  693.     }
  694. }
  695.  
  696. //-----------------------------------------------------------------------------
  697. // Purpose:
  698. //-----------------------------------------------------------------------------
  699. void CNPC_RollerMine::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  700. {
  701.     if ( NewState == NPC_STATE_IDLE )
  702.     {
  703.         SetDistLook( ROLLERMINE_IDLE_SEE_DIST );
  704.         m_flDistTooFar = ROLLERMINE_IDLE_SEE_DIST;
  705.  
  706.         if( m_bUniformSight )
  707.         {
  708.             m_flSeeVehiclesOnlyBeyond = ROLLERMINE_IDLE_SEE_DIST;
  709.         }
  710.         else
  711.         {
  712.             m_flSeeVehiclesOnlyBeyond = ROLLERMINE_SEE_VEHICLESONLY_BEYOND_IDLE;
  713.         }
  714.        
  715.         m_RollerController.m_vecAngular = vec3_origin;
  716.         m_wakeUp = true;
  717.     }
  718.     else
  719.     {
  720.         if ( OldState == NPC_STATE_IDLE )
  721.         {
  722.             // wake the neighbors!
  723.             WakeNeighbors();
  724.         }
  725.         SetDistLook( ROLLERMINE_NORMAL_SEE_DIST );
  726.  
  727.         if( m_bUniformSight )
  728.         {
  729.             m_flSeeVehiclesOnlyBeyond = ROLLERMINE_NORMAL_SEE_DIST;
  730.         }
  731.         else
  732.         {
  733.             m_flSeeVehiclesOnlyBeyond = ROLLERMINE_SEE_VEHICLESONLY_BEYOND_NORMAL;
  734.         }
  735.  
  736.         m_flDistTooFar = ROLLERMINE_NORMAL_SEE_DIST;
  737.     }
  738.     BaseClass::OnStateChange( OldState, NewState );
  739. }
  740.  
  741. //-----------------------------------------------------------------------------
  742. // Purpose:
  743. //-----------------------------------------------------------------------------
  744. NPC_STATE CNPC_RollerMine::SelectIdealState( void )
  745. {
  746.     switch ( m_NPCState )
  747.     {
  748.         case NPC_STATE_COMBAT:
  749.         {
  750.             if ( HasCondition( COND_ENEMY_TOO_FAR ) )
  751.             {
  752.                 ClearEnemyMemory();
  753.                 SetEnemy( NULL );
  754.                 m_flGoIdleTime = gpGlobals->curtime + 10;
  755.                 return NPC_STATE_ALERT;
  756.             }
  757.         }
  758.  
  759.         default:
  760.             break;
  761.     }
  762.  
  763.     return BaseClass::SelectIdealState();
  764. }
  765.  
  766. //-----------------------------------------------------------------------------
  767. // Purpose:
  768. // Output : Returns true on success, false on failure.
  769. //-----------------------------------------------------------------------------
  770. bool CNPC_RollerMine::BecomePhysical( void )
  771. {
  772.     VPhysicsDestroyObject();
  773.  
  774.     RemoveSolidFlags( FSOLID_NOT_SOLID );
  775.  
  776.     //Setup the physics controller on the roller
  777.     IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_VPHYSICS, GetSolidFlags() , false );
  778.  
  779.     if ( pPhysicsObject == NULL )
  780.         return false;
  781.  
  782.     m_pMotionController = physenv->CreateMotionController( &m_RollerController );
  783.     m_pMotionController->AttachObject( pPhysicsObject, true );
  784.  
  785.     SetMoveType( MOVETYPE_VPHYSICS );
  786.  
  787.     return true;
  788. }
  789.  
  790. //-----------------------------------------------------------------------------
  791. // Purpose:
  792. //-----------------------------------------------------------------------------
  793. void CNPC_RollerMine::OnRestore()
  794. {
  795.     BaseClass::OnRestore();
  796.     if ( m_pMotionController )
  797.     {
  798.         m_pMotionController->SetEventHandler( &m_RollerController );
  799.     }
  800.  
  801.     // If we're stuck to a vehicle over a level transition, restart our jolt inputs
  802.     if ( GetVehicleStuckTo() )
  803.     {
  804.         if ( !g_EventQueue.HasEventPending( this, "JoltVehicle" ) )
  805.         {
  806.             g_EventQueue.AddEvent( this, "JoltVehicle", RandomFloat(3,6), NULL, NULL );
  807.         }
  808.     }
  809. }
  810.  
  811. //-----------------------------------------------------------------------------
  812. // Purpose:
  813. //-----------------------------------------------------------------------------
  814. bool CNPC_RollerMine::CreateVPhysics()
  815. {
  816.     if ( m_bBuried )
  817.     {
  818.         VPhysicsInitStatic();
  819.         return true;
  820.     }
  821.     else
  822.     {
  823.         return BecomePhysical();
  824.     }
  825. }  
  826.  
  827. //-----------------------------------------------------------------------------
  828. //-----------------------------------------------------------------------------
  829. void CNPC_RollerMine::RunAI()
  830. {
  831.     if( m_bTurnedOn )
  832.     {
  833.         // Scare combine if hacked by Alyx.
  834.         IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  835.  
  836.         Vector vecVelocity;
  837.  
  838.         if ( pPhysicsObject != NULL )
  839.         {
  840.             pPhysicsObject->GetVelocity( &vecVelocity, NULL );
  841.         }
  842.  
  843.         if( !m_bHeld && vecVelocity.Length() > 64.0 )
  844.         {
  845.             if( m_bHackedByAlyx )
  846.             {
  847.                 // Scare combine
  848.                 CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_COMBINE_ONLY | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  849.             }
  850.             else
  851.             {
  852.                 // Scare player allies
  853.                 CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_EXCLUDE_COMBINE | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  854.             }
  855.         }
  856.  
  857.         BaseClass::RunAI();
  858.     }
  859. }
  860.  
  861. //-----------------------------------------------------------------------------
  862. // Purpose:
  863. // Input  :
  864. // Output :
  865. //-----------------------------------------------------------------------------
  866. int CNPC_RollerMine::RangeAttack1Conditions( float flDot, float flDist )
  867. {
  868.     if( HasCondition( COND_SEE_ENEMY ) == false )
  869.         return COND_NONE;
  870.  
  871.     if ( EnemyInVehicle() )
  872.         return COND_CAN_RANGE_ATTACK1;
  873.  
  874.     if( flDist > ROLLERMINE_MAX_ATTACK_DIST  )
  875.         return COND_TOO_FAR_TO_ATTACK;
  876.    
  877.     if (flDist < ROLLERMINE_MIN_ATTACK_DIST )
  878.         return COND_TOO_CLOSE_TO_ATTACK;
  879.  
  880.     return COND_CAN_RANGE_ATTACK1;
  881. }
  882.  
  883. //-----------------------------------------------------------------------------
  884. // Purpose:
  885. // Input  :
  886. // Output :
  887. //-----------------------------------------------------------------------------
  888. int CNPC_RollerMine::SelectSchedule( void )
  889. {
  890.     if ( m_bPowerDown )
  891.         return SCHED_ROLLERMINE_POWERDOWN;
  892.  
  893.     if ( m_bBuried )
  894.     {
  895.         if ( HasCondition(COND_NEW_ENEMY) || HasCondition(COND_LIGHT_DAMAGE) )
  896.             return SCHED_ROLLERMINE_BURIED_UNBURROW;
  897.  
  898.         return SCHED_ROLLERMINE_BURIED_WAIT;
  899.     }
  900.  
  901.     //If we're held, don't try and do anything
  902.     if ( ( m_bHeld ) || !IsActive() || m_hVehicleStuckTo )
  903.         return SCHED_ALERT_STAND;
  904.  
  905.     // If we can see something we're afraid of, run from it
  906.     if ( HasCondition( COND_SEE_FEAR ) )
  907.         return SCHED_ROLLERMINE_FLEE;
  908.  
  909.     switch( m_NPCState )
  910.     {
  911.     case NPC_STATE_COMBAT:
  912.  
  913.         if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  914.             return SCHED_ROLLERMINE_RANGE_ATTACK1;
  915.        
  916.         return SCHED_ROLLERMINE_CHASE_ENEMY;
  917.         break;
  918.  
  919.     default:
  920.         break;
  921.     }
  922.  
  923.     // Rollermines never wait to fall to the ground
  924.     ClearCondition( COND_FLOATING_OFF_GROUND );
  925.  
  926.     return BaseClass::SelectSchedule();
  927. }
  928.  
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. //-----------------------------------------------------------------------------
  932. int CNPC_RollerMine::GetHackedIdleSchedule( void )
  933. {
  934.     // If we've been hacked, return to the player
  935.     if ( !m_bHackedByAlyx || m_bHeld )
  936.         return SCHED_NONE;
  937.  
  938.     // Are we near the player?
  939.     CBaseEntity *pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  940.     if ( !pPlayer )
  941.         return SCHED_NONE;
  942.  
  943.     if ( !HasCondition(COND_SEE_PLAYER) )
  944.         return SCHED_ROLLERMINE_PATH_TO_PLAYER;
  945.  
  946.     if ( GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ) > ROLLERMINE_RETURN_TO_PLAYER_DIST )
  947.         return SCHED_ROLLERMINE_ROLL_TO_PLAYER;
  948.  
  949.     return SCHED_NONE;
  950. }
  951.  
  952. //-----------------------------------------------------------------------------
  953. //-----------------------------------------------------------------------------
  954. int CNPC_RollerMine::TranslateSchedule( int scheduleType )
  955. {
  956.     switch( scheduleType )
  957.     {
  958.     case SCHED_IDLE_STAND:
  959.         {
  960.             int iSched = GetHackedIdleSchedule();
  961.             if ( iSched != SCHED_NONE )
  962.                 return iSched;
  963.  
  964.             return SCHED_IDLE_STAND;
  965.         }
  966.         break;
  967.  
  968.     case SCHED_ALERT_STAND:
  969.         {
  970.             int iSched = GetHackedIdleSchedule();
  971.             if ( iSched != SCHED_NONE )
  972.                 return iSched;
  973.  
  974.             return SCHED_ROLLERMINE_ALERT_STAND;
  975.         }
  976.         break;
  977.  
  978.     case SCHED_ROLLERMINE_RANGE_ATTACK1:
  979.         if( HasCondition(COND_ENEMY_OCCLUDED) )
  980.         {
  981.             // Because of an unfortunate arrangement of cascading failing schedules, the rollermine
  982.             // could end up here with instructions to drive towards the target, although the target is
  983.             // not in sight. Nudge around randomly until we're back on the nodegraph.
  984.             return SCHED_ROLLERMINE_NUDGE_TOWARDS_NODES;
  985.         }
  986.         break;
  987.     }
  988.  
  989.     return scheduleType;
  990. }
  991.  
  992.  
  993. #if 0
  994. #define ROLLERMINE_DETECTION_RADIUS     350
  995. //-----------------------------------------------------------------------------
  996. // Purpose:
  997. // Output : Returns true on success, false on failure.
  998. //-----------------------------------------------------------------------------
  999. bool CNPC_RollerMine::DetectedEnemyInProximity( void )
  1000. {
  1001.     CBaseEntity *pEnt = NULL;
  1002.     CBaseEntity *pBestEnemy = NULL;
  1003.     float       flBestDist = MAX_TRACE_LENGTH;
  1004.  
  1005.     while ( ( pEnt = gEntList.FindEntityInSphere( pEnt, GetAbsOrigin(), ROLLERMINE_DETECTION_RADIUS ) ) != NULL )
  1006.     {
  1007.         if ( IRelationType( pEnt ) != D_HT )
  1008.             continue;
  1009.  
  1010.         float distance = ( pEnt->GetAbsOrigin() - GetAbsOrigin() ).Length();
  1011.        
  1012.         if ( distance >= flBestDist )
  1013.             continue;
  1014.  
  1015.         pBestEnemy = pEnt;
  1016.         flBestDist = distance;
  1017.     }
  1018.  
  1019.     if ( pBestEnemy != NULL )
  1020.     {
  1021.         SetEnemy( pBestEnemy );
  1022.         return true;
  1023.     }
  1024.  
  1025.     return false;
  1026. }
  1027. #endif
  1028.  
  1029. //-----------------------------------------------------------------------------
  1030. // Purpose:
  1031. // Input  : *pSightEnt -
  1032. // Output : Returns true on success, false on failure.
  1033. //-----------------------------------------------------------------------------
  1034. bool CNPC_RollerMine::QuerySeeEntity(CBaseEntity *pSightEnt, bool bOnlyHateOrFearIfNPC)
  1035. {
  1036.     if ( IRelationType( pSightEnt ) == D_FR )
  1037.     {
  1038.         // Only see feared objects up close
  1039.         float flDist = (WorldSpaceCenter() - pSightEnt->WorldSpaceCenter()).LengthSqr();
  1040.         if ( flDist > ROLLERMINE_FEAR_DISTANCE )
  1041.             return false;
  1042.     }
  1043.  
  1044.     return BaseClass::QuerySeeEntity(pSightEnt, bOnlyHateOrFearIfNPC);
  1045. }
  1046.  
  1047. //-----------------------------------------------------------------------------
  1048. // Purpose:
  1049. // Input  :
  1050. // Output :
  1051. //-----------------------------------------------------------------------------
  1052. void CNPC_RollerMine::StartTask( const Task_t *pTask )
  1053. {
  1054.     switch( pTask->iTask )
  1055.     {
  1056.     case TASK_FACE_REASONABLE:
  1057.     case TASK_FACE_SAVEPOSITION:
  1058.     case TASK_FACE_LASTPOSITION:
  1059.     case TASK_FACE_TARGET:
  1060.     case TASK_FACE_AWAY_FROM_SAVEPOSITION:
  1061.     case TASK_FACE_HINTNODE:
  1062.     case TASK_FACE_ENEMY:
  1063.     case TASK_FACE_PLAYER:
  1064.     case TASK_FACE_PATH:
  1065.     case TASK_FACE_IDEAL:
  1066.         // This only applies to NPCs that aren't spheres with omnidirectional eyesight.
  1067.         TaskComplete();
  1068.         break;
  1069.  
  1070.     case TASK_ROLLERMINE_UNBURROW:
  1071.        
  1072.         {
  1073.             AddSolidFlags( FSOLID_NOT_SOLID );
  1074.             SetMoveType( MOVETYPE_NOCLIP );
  1075.             SetAbsVelocity( Vector( 0, 0, 256 ) );
  1076.             Open();
  1077.  
  1078.             trace_t tr;
  1079.             AI_TraceLine( GetAbsOrigin()+Vector(0,0,1), GetAbsOrigin()-Vector(0,0,64), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1080.  
  1081.             if ( tr.fraction < 1.0f )
  1082.             {
  1083.                 UTIL_CreateAntlionDust( tr.endpos + Vector(0,0,24), GetLocalAngles() );    
  1084.             }
  1085.         }
  1086.  
  1087.         return;
  1088.         break;
  1089.  
  1090.     case TASK_ROLLERMINE_BURIED_WAIT:
  1091.         if ( HasCondition( COND_SEE_ENEMY ) )
  1092.         {
  1093.             TaskComplete();
  1094.         }
  1095.         break;
  1096.  
  1097.     case TASK_STOP_MOVING:
  1098.  
  1099.         //Stop turning
  1100.         m_RollerController.m_vecAngular = vec3_origin;
  1101.        
  1102.         TaskComplete();
  1103.         return;
  1104.         break;
  1105.  
  1106.     case TASK_WAIT_FOR_MOVEMENT:
  1107.         {
  1108.             // TASK_RUN_PATH and TASK_WALK_PATH work different on the rollermine and run until movement is done,
  1109.             // so movement is already complete when entering this task.
  1110.             TaskComplete();
  1111.         }
  1112.         break;
  1113.  
  1114.     case TASK_WALK_PATH:
  1115.     case TASK_RUN_PATH:
  1116.         {
  1117.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1118.  
  1119.             if ( pPhysicsObject == NULL )
  1120.             {
  1121.                 assert(0);
  1122.                 TaskFail("Roller lost internal physics object?");
  1123.                 return;
  1124.             }
  1125.  
  1126.             pPhysicsObject->Wake();
  1127.         }
  1128.         break;
  1129.  
  1130.     case TASK_ROLLERMINE_CHARGE_ENEMY:
  1131.     case TASK_ROLLERMINE_RETURN_TO_PLAYER:
  1132.         {
  1133.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1134.            
  1135.             if ( pPhysicsObject == NULL )
  1136.             {
  1137.                 assert(0);
  1138.                 TaskFail("Roller lost internal physics object?");
  1139.                 return;
  1140.             }
  1141.            
  1142.             pPhysicsObject->Wake();
  1143.  
  1144.             m_flChargeTime = gpGlobals->curtime;
  1145.         }
  1146.  
  1147.         break;
  1148.  
  1149.     case TASK_ROLLERMINE_GET_PATH_TO_FLEE:
  1150.         {
  1151.             // Find the nearest thing we're afraid of, and move away from it.
  1152.             float flNearest = ROLLERMINE_FEAR_DISTANCE;
  1153.             EHANDLE hNearestEnemy = NULL;
  1154.             AIEnemiesIter_t iter;
  1155.             for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) )
  1156.             {
  1157.                 CBaseEntity *pEnemy = pEMemory->hEnemy;
  1158.                 if ( !pEnemy || !pEnemy->IsAlive() )
  1159.                     continue;
  1160.                 if ( IRelationType( pEnemy ) != D_FR )
  1161.                     continue;          
  1162.  
  1163.                 float flDist = (WorldSpaceCenter() - pEnemy->WorldSpaceCenter()).LengthSqr();
  1164.                 if ( flDist < flNearest )
  1165.                 {
  1166.                     flNearest = flDist;
  1167.                     hNearestEnemy = pEnemy;
  1168.                 }
  1169.             }
  1170.  
  1171.             if ( !hNearestEnemy )
  1172.             {
  1173.                 TaskFail("Couldn't find nearest feared object.");
  1174.                 break;
  1175.             }
  1176.  
  1177.             GetMotor()->SetIdealYawToTarget( hNearestEnemy->WorldSpaceCenter() );
  1178.             ChainStartTask( TASK_MOVE_AWAY_PATH, pTask->flTaskData );
  1179.         }
  1180.         break;
  1181.  
  1182.     case TASK_ROLLERMINE_NUDGE_TOWARDS_NODES:
  1183.         {
  1184.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1185.  
  1186.             if( pPhysicsObject )
  1187.             {
  1188.                 // Try a few times to find a direction to shove ourself
  1189.                 for( int i = 0 ; i < 4 ; i++ )
  1190.                 {
  1191.                     int x,y;
  1192.  
  1193.                     x = random->RandomInt( -1, 1 );
  1194.                     y = random->RandomInt( -1, 1 );
  1195.  
  1196.                     Vector vecNudge(x, y, 0.0f);
  1197.  
  1198.                     trace_t tr;
  1199.  
  1200.                     // Try to move in a direction with a couple of feet of clearance.
  1201.                     UTIL_TraceLine( WorldSpaceCenter(), WorldSpaceCenter() + vecNudge * 24.0f, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1202.  
  1203.                     if( tr.fraction == 1.0 )
  1204.                     {
  1205.                         vecNudge *= (pPhysicsObject->GetMass() * 75.0f);
  1206.                         vecNudge += Vector(0,0,pPhysicsObject->GetMass() * 75.0f);
  1207.                         pPhysicsObject->ApplyForceCenter( vecNudge );
  1208.                         break;
  1209.                     }
  1210.                 }
  1211.             }
  1212.  
  1213.             TaskComplete();
  1214.         }
  1215.         break;
  1216.  
  1217.     case TASK_ROLLERMINE_POWERDOWN:
  1218.         break;
  1219.  
  1220.     default:
  1221.         BaseClass::StartTask( pTask );
  1222.         break;
  1223.     }
  1224. }
  1225.  
  1226.  
  1227. //-----------------------------------------------------------------------------
  1228. // Purpose:
  1229. // Input  :
  1230. // Output :
  1231. //-----------------------------------------------------------------------------
  1232. void CNPC_RollerMine::RunTask( const Task_t *pTask )
  1233. {
  1234.     switch( pTask->iTask )
  1235.     {
  1236.     case TASK_ROLLERMINE_UNBURROW:
  1237.         {  
  1238.             Vector  vCenter = WorldSpaceCenter();
  1239.  
  1240.             // Robin: HACK: Bloat the rollermine check to catch the model switch (roller.mdl->roller_spikes.mdl)
  1241.             trace_t tr;
  1242.             AI_TraceHull( vCenter, vCenter, Vector(-16,-16,-16), Vector(16,16,16), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1243.  
  1244.             if ( tr.fraction == 1 && tr.allsolid != 1 && (tr.startsolid != 1) )
  1245.             {
  1246.                 if ( BecomePhysical() )
  1247.                 {
  1248.                     Hop( 256 );
  1249.                     m_bBuried = false;
  1250.                     TaskComplete();
  1251.                     SetIdealState( NPC_STATE_ALERT );
  1252.                 }
  1253.             }
  1254.         }
  1255.  
  1256.         return;
  1257.         break;
  1258.  
  1259.     case TASK_ROLLERMINE_BURIED_WAIT:
  1260.         if ( HasCondition( COND_SEE_ENEMY ) || HasCondition( COND_LIGHT_DAMAGE ) )
  1261.         {
  1262.             TaskComplete();
  1263.         }
  1264.         break;
  1265.  
  1266.     case TASK_ROLLERMINE_GET_PATH_TO_FLEE:
  1267.         {
  1268.             ChainRunTask( TASK_MOVE_AWAY_PATH, pTask->flTaskData );
  1269.         }
  1270.         break;
  1271.  
  1272.     case TASK_WAIT_FOR_MOVEMENT:
  1273.         {
  1274.             // TASK_RUN_PATH and TASK_WALK_PATH work different on the rollermine and run until movement is done,
  1275.             // so movement is already complete when entering this task.
  1276.             TaskComplete();
  1277.         }
  1278.         break;
  1279.  
  1280.     case TASK_RUN_PATH:
  1281.     case TASK_WALK_PATH:
  1282.  
  1283.         if ( m_bHeld || m_hVehicleStuckTo )
  1284.         {
  1285.             TaskFail( "Player interrupted by grabbing" );
  1286.             break;
  1287.         }
  1288.  
  1289.         // If we were fleeing, but we've lost sight of the thing scaring us, stop
  1290.         if ( IsCurSchedule(SCHED_ROLLERMINE_FLEE) && !HasCondition( COND_SEE_FEAR ) )
  1291.         {
  1292.             TaskComplete();
  1293.             break;
  1294.         }
  1295.  
  1296.         if ( !GetNavigator()->IsGoalActive() )
  1297.         {
  1298.             TaskComplete();
  1299.             return;
  1300.         }
  1301.  
  1302.         // Start turning early
  1303.         if( (GetLocalOrigin() - GetNavigator()->GetCurWaypointPos() ).Length() <= 64 )
  1304.         {
  1305.             if( GetNavigator()->CurWaypointIsGoal() )
  1306.             {
  1307.                 // Hit the brakes a bit.
  1308.                 float yaw = UTIL_VecToYaw( GetNavigator()->GetCurWaypointPos() - GetLocalOrigin() );
  1309.                 Vector vecRight;
  1310.                 AngleVectors( QAngle( 0, yaw, 0 ), NULL, &vecRight, NULL );
  1311.  
  1312.                 m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, -m_flForwardSpeed * 5 );
  1313.  
  1314.                 TaskComplete();
  1315.                 return;
  1316.             }
  1317.  
  1318.             GetNavigator()->AdvancePath(); 
  1319.         }
  1320.  
  1321.         {
  1322.             float yaw = UTIL_VecToYaw( GetNavigator()->GetCurWaypointPos() - GetLocalOrigin() );
  1323.  
  1324.             Vector vecRight;
  1325.             Vector vecToPath; // points at the path
  1326.             AngleVectors( QAngle( 0, yaw, 0 ), &vecToPath, &vecRight, NULL );
  1327.  
  1328.             // figure out if the roller is turning. If so, cut the throttle a little.
  1329.             float flDot;
  1330.             Vector vecVelocity;
  1331.  
  1332.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1333.            
  1334.             if ( pPhysicsObject == NULL )
  1335.             {
  1336.                 assert(0);
  1337.                 TaskFail("Roller lost internal physics object?");
  1338.                 return;
  1339.             }
  1340.            
  1341.             pPhysicsObject->GetVelocity( &vecVelocity, NULL );
  1342.  
  1343.             VectorNormalize( vecVelocity );
  1344.  
  1345.             vecVelocity.z = 0;
  1346.  
  1347.             flDot = DotProduct( vecVelocity, vecToPath );
  1348.  
  1349.             m_RollerController.m_vecAngular = vec3_origin;
  1350.  
  1351.             if( flDot > 0.25 && flDot < 0.7 )
  1352.             {
  1353.                 // Feed a little torque backwards into the axis perpendicular to the velocity.
  1354.                 // This will help get rid of momentum that would otherwise make us overshoot our goal.
  1355.                 Vector vecCompensate;
  1356.  
  1357.                 vecCompensate.x = vecVelocity.y;
  1358.                 vecCompensate.y = -vecVelocity.x;
  1359.                 vecCompensate.z = 0;
  1360.  
  1361.                 m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 );
  1362.             }
  1363.  
  1364.             if( m_bHackedByAlyx )
  1365.             {
  1366.                 // Move faster.
  1367.                 m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed * 2.0f );
  1368.             }
  1369.             else
  1370.             {
  1371.                 m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed );
  1372.             }
  1373.         }
  1374.         break;
  1375.  
  1376.     case TASK_ROLLERMINE_CHARGE_ENEMY:
  1377.         {
  1378.             if ( !GetEnemy() )
  1379.             {
  1380.                 TaskFail( FAIL_NO_ENEMY );
  1381.                 break;
  1382.             }
  1383.  
  1384.             if ( m_bHeld || m_hVehicleStuckTo )
  1385.             {
  1386.                 TaskComplete();
  1387.                 break;
  1388.             }
  1389.  
  1390.             CBaseEntity *pEnemy = GetEnemy();
  1391.             Vector vecTargetPosition = pEnemy->GetAbsOrigin();
  1392.  
  1393.             // If we're chasing a vehicle, try and get ahead of it
  1394.             if ( EnemyInVehicle() )
  1395.             {
  1396.                 CBaseCombatCharacter *pCCEnemy = pEnemy->MyCombatCharacterPointer();
  1397.                 float flT;
  1398.  
  1399.                 // Project it's velocity and find our closest point on that line. Do it all in 2d space.
  1400.                 Vector vecVehicleVelocity = pCCEnemy->GetVehicleEntity()->GetSmoothedVelocity();
  1401.                 Vector vecProjected = vecTargetPosition + (vecVehicleVelocity * 1.0);
  1402.                 Vector2D vecProjected2D( vecProjected.x, vecProjected.y );
  1403.                 Vector2D vecTargetPosition2D( vecTargetPosition.x, vecTargetPosition.y );
  1404.                 Vector2D vecOrigin2D( GetAbsOrigin().x, GetAbsOrigin().y );
  1405.                 Vector2D vecIntercept2D;
  1406.  
  1407.                 CalcClosestPointOnLine2D( vecOrigin2D, vecTargetPosition2D, vecProjected2D, vecIntercept2D, &flT );
  1408.                 Vector vecIntercept( vecIntercept2D.x, vecIntercept2D.y, GetAbsOrigin().z );
  1409.                
  1410.                 //NDebugOverlay::Line( vecTargetPosition, vecProjected, 0,255,0, true, 0.1 );
  1411.  
  1412.                 // If we're ahead of the line somewhere, try to intercept
  1413.                 if ( flT > 0 )
  1414.                 {
  1415.                     // If it's beyond the end of the intercept line, just move towards the end of the line
  1416.                     if ( flT > 1 )
  1417.                     {
  1418.                         vecIntercept.x = vecProjected.x;
  1419.                         vecIntercept.y = vecProjected.y;
  1420.                     }
  1421.  
  1422.                     // If we're closer to the intercept point than to the vehicle, move towards the intercept
  1423.                     if ( (GetAbsOrigin() - vecTargetPosition).LengthSqr() > (GetAbsOrigin() - vecIntercept).LengthSqr() )
  1424.                     {
  1425.                         //NDebugOverlay::Box( vecIntercept, -Vector(20,20,20), Vector(20,20,20), 255,0,0, 0.1, 0.1 );
  1426.  
  1427.                         // Only use this position if it's clear
  1428.                         if ( enginetrace->GetPointContents( vecIntercept ) != CONTENTS_SOLID )
  1429.                         {
  1430.                             vecTargetPosition = vecIntercept;
  1431.                         }
  1432.                     }
  1433.                 }
  1434.  
  1435.                 //NDebugOverlay::Box( vecTargetPosition, -Vector(20,20,20), Vector(20,20,20), 255,255,255, 0.1, 0.1 );
  1436.             }
  1437.  
  1438.             float flTorqueFactor;
  1439.             Vector vecToTarget = vecTargetPosition - GetLocalOrigin();
  1440.             float yaw = UTIL_VecToYaw( vecToTarget );
  1441.             Vector vecRight;
  1442.  
  1443.             AngleVectors( QAngle( 0, yaw, 0 ), NULL, &vecRight, NULL );
  1444.  
  1445.             //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + (GetEnemy()->GetLocalOrigin() - GetLocalOrigin()), 0,255,0, true, 0.1 );
  1446.  
  1447.             float flDot;
  1448.  
  1449.             // Figure out whether to continue the charge.
  1450.             // (Have I overrun the target?)        
  1451.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1452.  
  1453.             if ( pPhysicsObject == NULL )
  1454.             {
  1455. //              Assert(0);
  1456.                 TaskFail("Roller lost internal physics object?");
  1457.                 return;
  1458.             }
  1459.  
  1460.             Vector vecVelocity;
  1461.             pPhysicsObject->GetVelocity( &vecVelocity, NULL );
  1462.             VectorNormalize( vecVelocity );
  1463.  
  1464.             VectorNormalize( vecToTarget );
  1465.  
  1466.             flDot = DotProduct( vecVelocity, vecToTarget );
  1467.  
  1468.             // more torque the longer the roller has been going.
  1469.             flTorqueFactor = 1 + (gpGlobals->curtime - m_flChargeTime) * 2;
  1470.  
  1471.             float flMaxTorque = ROLLERMINE_MAX_TORQUE_FACTOR;
  1472.            
  1473.             // Friendly rollermines go a little slower
  1474.             if ( HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
  1475.             {
  1476.                 flMaxTorque *= 0.75;
  1477.             }
  1478.  
  1479.             if( flTorqueFactor < 1 )
  1480.             {
  1481.                 flTorqueFactor = 1;
  1482.             }
  1483.             else if( flTorqueFactor > flMaxTorque)
  1484.             {
  1485.                 flTorqueFactor = flMaxTorque;
  1486.             }
  1487.  
  1488.             Vector vecCompensate;
  1489.  
  1490.             vecCompensate.x = vecVelocity.y;
  1491.             vecCompensate.y = -vecVelocity.x;
  1492.             vecCompensate.z = 0;
  1493.             VectorNormalize( vecCompensate );
  1494.  
  1495.             m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 );
  1496.             m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed  * flTorqueFactor );
  1497.        
  1498.             // Taunt when I get closer
  1499.             if( !(m_iSoundEventFlags & ROLLERMINE_SE_TAUNT) && UTIL_DistApprox( GetLocalOrigin(), vecTargetPosition ) <= 400 )
  1500.             {
  1501.                 m_iSoundEventFlags |= ROLLERMINE_SE_TAUNT; // Don't repeat.
  1502.  
  1503.                 EmitSound( "NPC_RollerMine.Taunt" );
  1504.             }
  1505.  
  1506.             // Jump earlier when chasing a vehicle
  1507.             float flThreshold = ROLLERMINE_OPEN_THRESHOLD;
  1508.             if ( EnemyInVehicle() )
  1509.             {
  1510.                 flThreshold = ROLLERMINE_VEHICLE_OPEN_THRESHOLD;
  1511.             }
  1512.  
  1513.             // Open the spikes if i'm close enough to cut the enemy!!
  1514.             if( ( m_bIsOpen == false ) && ( ( UTIL_DistApprox( GetAbsOrigin(), GetEnemy()->GetAbsOrigin() ) <= flThreshold ) || !IsActive() ) )
  1515.             {
  1516.                 Open();
  1517.             }
  1518.             else if ( m_bIsOpen )
  1519.             {
  1520.                 float flDistance = UTIL_DistApprox( GetAbsOrigin(), GetEnemy()->GetAbsOrigin() );
  1521.                 if ( flDistance >= flThreshold )
  1522.                 {
  1523.                     // Otherwise close them if the enemy is getting away!
  1524.                     Close();
  1525.                 }
  1526.                 else if ( EnemyInVehicle() && flDistance < ROLLERMINE_VEHICLE_HOP_THRESHOLD )
  1527.                 {
  1528.                     // Keep trying to hop when we're ramming a vehicle, so we're visible to the player
  1529.                     if ( vecVelocity.x != 0 && vecVelocity.y != 0 && flTorqueFactor > 3 && flDot > 0.0 )
  1530.                     {
  1531.                         Hop( 300 );
  1532.                     }
  1533.                 }
  1534.             }
  1535.  
  1536.             // If we drive past, close the blades and make a new plan.
  1537.             if ( !EnemyInVehicle() )
  1538.             {
  1539.                 if( vecVelocity.x != 0 && vecVelocity.y != 0 )
  1540.                 {
  1541.                     if( gpGlobals->curtime - m_flChargeTime > 1.0 && flTorqueFactor > 1 &&  flDot < 0.0 )
  1542.                     {
  1543.                         if( m_bIsOpen )
  1544.                         {
  1545.                             Close();
  1546.                         }
  1547.  
  1548.                         TaskComplete();
  1549.                     }
  1550.                 }
  1551.             }
  1552.         }
  1553.         break;
  1554.  
  1555.     case TASK_ROLLERMINE_RETURN_TO_PLAYER:
  1556.         {
  1557.             if ( ConditionsGathered() && !HasCondition(COND_SEE_PLAYER) )
  1558.             {
  1559.                 TaskFail( FAIL_NO_PLAYER );
  1560.                 return;
  1561.             }
  1562.  
  1563.             CBaseEntity *pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  1564.             if ( !pPlayer || m_bHeld || m_hVehicleStuckTo )
  1565.             {
  1566.                 TaskFail( FAIL_NO_TARGET );
  1567.                 return;
  1568.             }
  1569.  
  1570.             Vector vecTargetPosition = pPlayer->GetAbsOrigin();
  1571.             float flTorqueFactor;
  1572.             Vector vecToTarget = vecTargetPosition - GetLocalOrigin();
  1573.             float yaw = UTIL_VecToYaw( vecToTarget );
  1574.             Vector vecRight;
  1575.  
  1576.             AngleVectors( QAngle( 0, yaw, 0 ), NULL, &vecRight, NULL );
  1577.  
  1578.             float flDot;
  1579.  
  1580.             IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  1581.             if ( pPhysicsObject == NULL )
  1582.             {
  1583.                 TaskFail("Roller lost internal physics object?");
  1584.                 return;
  1585.             }
  1586.  
  1587.             Vector vecVelocity;
  1588.             pPhysicsObject->GetVelocity( &vecVelocity, NULL );
  1589.             VectorNormalize( vecVelocity );
  1590.             VectorNormalize( vecToTarget );
  1591.  
  1592.             flDot = DotProduct( vecVelocity, vecToTarget );
  1593.  
  1594.             // more torque the longer the roller has been going.
  1595.             flTorqueFactor = 1 + (gpGlobals->curtime - m_flChargeTime) * 2;
  1596.  
  1597.             float flMaxTorque = ROLLERMINE_MAX_TORQUE_FACTOR * 0.75;
  1598.             if( flTorqueFactor < 1 )
  1599.             {
  1600.                 flTorqueFactor = 1;
  1601.             }
  1602.             else if( flTorqueFactor > flMaxTorque)
  1603.             {
  1604.                 flTorqueFactor = flMaxTorque;
  1605.             }
  1606.  
  1607.             Vector vecCompensate;
  1608.  
  1609.             vecCompensate.x = vecVelocity.y;
  1610.             vecCompensate.y = -vecVelocity.x;
  1611.             vecCompensate.z = 0;
  1612.             VectorNormalize( vecCompensate );
  1613.  
  1614.             m_RollerController.m_vecAngular = WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecCompensate, m_flForwardSpeed * -0.75 );
  1615.             m_RollerController.m_vecAngular += WorldToLocalRotation( SetupMatrixAngles(GetLocalAngles()), vecRight, m_flForwardSpeed  * flTorqueFactor );
  1616.  
  1617.             // Once we're near the player, slow & stop
  1618.             if ( GetAbsOrigin().DistToSqr( vecTargetPosition ) < (ROLLERMINE_RETURN_TO_PLAYER_DIST*2.0) )
  1619.             {
  1620.                 TaskComplete();
  1621.             }
  1622.         }
  1623.         break;
  1624.  
  1625.     case TASK_ROLLERMINE_POWERDOWN:
  1626.         {
  1627.             if ( m_flPowerDownTime <= gpGlobals->curtime )
  1628.             {
  1629.                 m_flNextHop = gpGlobals->curtime;
  1630.                 m_flPowerDownTime = gpGlobals->curtime + RandomFloat( 0.3, 0.9 );
  1631.                 EmitSound( "NPC_RollerMine.Hurt" );
  1632.  
  1633.                 CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 0.5f, this );
  1634.  
  1635.                 if ( m_bIsOpen == false )
  1636.                 {
  1637.                     Open();
  1638.                 }
  1639.                 else
  1640.                 {
  1641.                     Close();
  1642.                 }
  1643.             }
  1644.  
  1645.             if ( m_flPowerDownDetonateTime <= gpGlobals->curtime )
  1646.             {
  1647.                 SetThink( &CNPC_RollerMine::PreDetonate );
  1648.                 SetNextThink( gpGlobals->curtime + 0.5f );
  1649.             }
  1650.  
  1651.             // No TaskComplete() here, because the task will never complete. The rollermine will explode.
  1652.         }
  1653.         break; 
  1654.  
  1655.     default:
  1656.         BaseClass::RunTask( pTask );
  1657.         break;
  1658.     }
  1659. }
  1660.  
  1661. //-----------------------------------------------------------------------------
  1662. // Purpose:
  1663. // Input  :
  1664. // Output :
  1665. //-----------------------------------------------------------------------------
  1666. void CNPC_RollerMine::Open( void )
  1667. {
  1668.     // Friendly rollers cannot open
  1669.     if ( HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
  1670.         return;
  1671.  
  1672.     if ( m_bIsOpen == false )
  1673.     {
  1674.         SetModel( "models/roller_spikes.mdl" );
  1675.         SetRollerSkin();
  1676.  
  1677.         EmitSound( "NPC_RollerMine.OpenSpikes" );
  1678.  
  1679.         SetTouch( &CNPC_RollerMine::ShockTouch );
  1680.         m_bIsOpen = true;
  1681.  
  1682.         // Don't hop if we're constrained
  1683.         if ( !m_pConstraint )
  1684.         {
  1685.             if ( EnemyInVehicle() )
  1686.             {
  1687.                 Hop( 256 );
  1688.             }
  1689.             else if ( !GetEnemy() || GetEnemy()->Classify() != CLASS_BULLSEYE )     // Don't hop when attacking bullseyes
  1690.             {
  1691.                 Hop( 128 );
  1692.             }
  1693.         }
  1694.     }
  1695. }
  1696.  
  1697. //-----------------------------------------------------------------------------
  1698. // Purpose:
  1699. // Input  :
  1700. // Output :
  1701. //-----------------------------------------------------------------------------
  1702. void CNPC_RollerMine::SetRollerSkin( void )
  1703. {
  1704.     if ( m_bPowerDown == true )
  1705.     {
  1706.         m_nSkin = (int)ROLLER_SKIN_DETONATE;
  1707.     }
  1708.     else if ( m_bHackedByAlyx == true )
  1709.     {
  1710.         m_nSkin = (int)ROLLER_SKIN_FRIENDLY;
  1711.     }
  1712.     else
  1713.     {
  1714.         m_nSkin = (int)ROLLER_SKIN_REGULAR;
  1715.     }
  1716. }
  1717.  
  1718.  
  1719. //-----------------------------------------------------------------------------
  1720. // Purpose:
  1721. // Input  :
  1722. // Output :
  1723. //-----------------------------------------------------------------------------
  1724. void CNPC_RollerMine::Close( void )
  1725. {
  1726.     // Not allowed to close while primed, because we're going to detonate on touch
  1727.     if ( m_bIsPrimed )
  1728.         return;
  1729.  
  1730.     if ( m_bIsOpen && !IsShocking() )
  1731.     {
  1732.         SetModel( "models/roller.mdl" );
  1733.  
  1734.         SetRollerSkin();
  1735.  
  1736.         SetTouch( NULL );
  1737.         m_bIsOpen = false;
  1738.  
  1739.         m_iSoundEventFlags = ROLLERMINE_SE_CLEAR;
  1740.     }
  1741. }
  1742.  
  1743. //-----------------------------------------------------------------------------
  1744. // Purpose:
  1745. // Input  :
  1746. // Output :
  1747. //-----------------------------------------------------------------------------
  1748. void CNPC_RollerMine::SpikeTouch( CBaseEntity *pOther )
  1749. {
  1750.     /*
  1751.     if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  1752.         return;
  1753.  
  1754.     if ( m_bHeld )
  1755.         return;
  1756.  
  1757.     if ( pOther->IsPlayer() )
  1758.         return;
  1759.  
  1760.     if ( pOther->m_takedamage != DAMAGE_YES )
  1761.         return;
  1762.  
  1763.     // If we just hit a breakable glass object, don't explode. We want to blow through it.
  1764.     CBreakable *pBreakable = dynamic_cast<CBreakable*>(pOther);
  1765.     if ( pBreakable && pBreakable->GetMaterialType() == matGlass )
  1766.         return;
  1767.  
  1768.     Explode();
  1769.     EmitSound( "NPC_RollerMine.Warn" );
  1770.     */
  1771.  
  1772.     //FIXME: Either explode within certain rules, never explode, or just shock the hit victim
  1773. }
  1774.  
  1775. //-----------------------------------------------------------------------------
  1776. // Purpose:
  1777. //-----------------------------------------------------------------------------
  1778. void CNPC_RollerMine::CloseTouch( CBaseEntity *pOther )
  1779. {
  1780.     if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  1781.         return;
  1782.  
  1783.     if ( IsShocking() )
  1784.         return;
  1785.  
  1786.     bool bOtherIsDead = ( pOther->MyNPCPointer() && !pOther->MyNPCPointer()->IsAlive() );
  1787.     bool bOtherIsNotarget = ( ( pOther->GetFlags() & FL_NOTARGET ) != 0 );
  1788.  
  1789.     if ( !bOtherIsDead && !bOtherIsNotarget )
  1790.     {
  1791.         Disposition_t disp = IRelationType(pOther);
  1792.  
  1793.         if ( (disp == D_HT || disp == D_FR) )
  1794.         {
  1795.             ShockTouch( pOther );
  1796.             return;
  1797.         }
  1798.     }
  1799.  
  1800.     Close();
  1801. }
  1802.  
  1803. //-----------------------------------------------------------------------------
  1804. // Purpose:
  1805. //-----------------------------------------------------------------------------
  1806. void CNPC_RollerMine::EmbedTouch( CBaseEntity *pOther )
  1807. {
  1808.     if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  1809.         return;
  1810.  
  1811.     m_bEmbedOnGroundImpact = false;
  1812.  
  1813.     // Did we hit the world?
  1814.     if ( pOther->entindex() == 0 )
  1815.     {
  1816.         m_bBuried = true;
  1817.         trace_t tr;
  1818.         Bury( &tr );
  1819.  
  1820.         // Destroy out physics object and become static
  1821.         VPhysicsDestroyObject();
  1822.         CreateVPhysics();
  1823.  
  1824.         // Drop a decal on the ground where we impacted
  1825.         UTIL_DecalTrace( &tr, "Rollermine.Crater" );
  1826.  
  1827.         // Make some dust
  1828.         UTIL_CreateAntlionDust( tr.endpos, GetLocalAngles() );
  1829.     }
  1830.  
  1831.     // Don't try and embed again
  1832.     SetTouch( NULL );
  1833. }
  1834.  
  1835. //-----------------------------------------------------------------------------
  1836. // Purpose:
  1837. //-----------------------------------------------------------------------------
  1838. bool CNPC_RollerMine::IsPlayerVehicle( CBaseEntity *pEntity )
  1839. {
  1840.     IServerVehicle *pVehicle = pEntity->GetServerVehicle();
  1841.     if ( pVehicle )
  1842.     {
  1843.         CBasePlayer *pPlayer = ToBasePlayer( pVehicle->GetPassenger() );
  1844.         if ( pPlayer != NULL )
  1845.         {
  1846.             Disposition_t disp = IRelationType(pPlayer);
  1847.  
  1848.             if ( disp == D_HT || disp == D_FR )
  1849.                 return true;
  1850.         }
  1851.     }
  1852.  
  1853.     return false;
  1854. }
  1855.  
  1856. //-----------------------------------------------------------------------------
  1857. // Purpose:
  1858. // Input  : *pVictim -
  1859. // Output : float
  1860. //-----------------------------------------------------------------------------
  1861. float CNPC_RollerMine::GetAttackDamageScale( CBaseEntity *pVictim )
  1862. {
  1863.     // If we're friendly, don't damage players or player-friendly NPCs, even with collisions
  1864.     if ( HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
  1865.     {
  1866.         if ( pVictim->IsPlayer() )
  1867.             return 0;
  1868.        
  1869.         if ( pVictim->MyNPCPointer() )
  1870.         {
  1871.             // If we don't hate the player, we're immune
  1872.             CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
  1873.             if ( pPlayer && pVictim->MyNPCPointer()->IRelationType( pPlayer ) != D_HT )
  1874.                 return 0.0;
  1875.         }
  1876.     }
  1877.  
  1878.     return BaseClass::GetAttackDamageScale( pVictim );
  1879. }
  1880.  
  1881. //-----------------------------------------------------------------------------
  1882. // Purpose:
  1883. // Input  : *pOther -
  1884. //-----------------------------------------------------------------------------
  1885. void CNPC_RollerMine::ShockTarget( CBaseEntity *pOther )
  1886. {
  1887.     CBeam *pBeam;
  1888.  
  1889.     if( m_bHackedByAlyx )
  1890.     {
  1891.         pBeam = CBeam::BeamCreate( "sprites/rollermine_shock_yellow.vmt", 4 );
  1892.     }
  1893.     else
  1894.     {
  1895.         pBeam = CBeam::BeamCreate( "sprites/rollermine_shock.vmt", 4 );
  1896.     }
  1897.  
  1898.     int startAttach = -1;
  1899.  
  1900.     CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pOther);
  1901.  
  1902.     if ( pBeam != NULL )
  1903.     {
  1904.         pBeam->EntsInit( pOther, this );
  1905.  
  1906.         if ( pAnimating && pAnimating->GetModel() )
  1907.         {
  1908.             startAttach = pAnimating->LookupAttachment("beam_damage" );
  1909.             pBeam->SetStartAttachment( startAttach );
  1910.         }
  1911.  
  1912.         // Change this up a little for first person hits
  1913.         if ( pOther->IsPlayer() )
  1914.         {
  1915.             pBeam->SetEndWidth( 8 );
  1916.             pBeam->SetNoise( 4 );
  1917.             pBeam->LiveForTime( 0.2f );
  1918.         }
  1919.         else
  1920.         {
  1921.             pBeam->SetEndWidth( 16 );
  1922.             pBeam->SetNoise( 16 );
  1923.             pBeam->LiveForTime( 0.5f );
  1924.         }
  1925.        
  1926.         pBeam->SetEndAttachment( 1 );
  1927.         pBeam->SetWidth( 1 );
  1928.         pBeam->SetBrightness( 255 );
  1929.         pBeam->SetColor( 255, 255, 255 );
  1930.         pBeam->RelinkBeam();
  1931.     }
  1932.    
  1933.     Vector shockPos = pOther->WorldSpaceCenter();
  1934.  
  1935.     if ( startAttach > 0 && pAnimating )
  1936.     {
  1937.         pAnimating->GetAttachment( startAttach, shockPos );
  1938.     }
  1939.  
  1940.     Vector shockDir = ( GetAbsOrigin() - shockPos );
  1941.     VectorNormalize( shockDir );
  1942.  
  1943.     CPVSFilter filter( shockPos );
  1944.     te->GaussExplosion( filter, 0.0f, shockPos, shockDir, 0 );
  1945. }
  1946.  
  1947. //-----------------------------------------------------------------------------
  1948. // Purpose:
  1949. //-----------------------------------------------------------------------------
  1950. void CNPC_RollerMine::NotifyInteraction( CAI_BaseNPC *pUser )
  1951. {
  1952.     // For now, turn green so we can tell who is hacked.
  1953.     m_bHackedByAlyx = true;
  1954.     SetRollerSkin();
  1955.     GetEnemies()->SetFreeKnowledgeDuration( 30.0f );
  1956.  
  1957.     // Play the hax0red sound
  1958.     EmitSound( "NPC_RollerMine.Reprogram" );
  1959.  
  1960.     // Force the rollermine open here. At very least, this ensures that the
  1961.     // correct, smaller bounding box is recomputed around it.
  1962.     Open();
  1963. }
  1964.  
  1965. //-----------------------------------------------------------------------------
  1966. // Purpose:
  1967. //-----------------------------------------------------------------------------
  1968. void CNPC_RollerMine::ShockTouch( CBaseEntity *pOther )
  1969. {
  1970.     if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) )
  1971.         return;
  1972.  
  1973.     if ( m_bHeld || m_hVehicleStuckTo || gpGlobals->curtime < m_flShockTime )
  1974.         return;
  1975.  
  1976.     // error?
  1977.     Assert( !m_bIsPrimed );
  1978.  
  1979.     Disposition_t disp = IRelationType(pOther);
  1980.  
  1981.     // Ignore anyone that I'm friendly or neutral to.
  1982.     if( disp != D_HT && disp != D_FR)
  1983.         return;
  1984.  
  1985.     IPhysicsObject *pPhysics = VPhysicsGetObject();
  1986.  
  1987.     // Calculate a collision force
  1988.     Vector impulse = WorldSpaceCenter() - pOther->WorldSpaceCenter();
  1989.     impulse.z = 0;
  1990.     VectorNormalize( impulse );
  1991.     impulse.z = 0.75;
  1992.     VectorNormalize( impulse );
  1993.     impulse *= 600;
  1994.  
  1995.     // Stun the roller
  1996.     m_flActiveTime = gpGlobals->curtime + GetStunDelay();
  1997.  
  1998.     // If we're a 'friendly' rollermine, just push the player a bit
  1999.     if ( HasSpawnFlags( SF_ROLLERMINE_FRIENDLY ) )
  2000.     {
  2001.         if ( pOther->IsPlayer() )
  2002.         {
  2003.             Vector vecForce = -impulse * 0.5;
  2004.             pOther->ApplyAbsVelocityImpulse( vecForce );
  2005.         }
  2006.         return;
  2007.     }
  2008.  
  2009.     // jump up at a 30 degree angle away from the guy we hit
  2010.     SetTouch( &CNPC_RollerMine::CloseTouch );
  2011.     Vector vel;
  2012.     pPhysics->SetVelocity( &impulse, NULL );
  2013.     EmitSound( "NPC_RollerMine.Shock" );
  2014.     // Do a shock effect
  2015.     ShockTarget( pOther );
  2016.  
  2017.     m_flShockTime = gpGlobals->curtime + 1.25;
  2018.  
  2019.     // Calculate physics force
  2020.     Vector out;
  2021.     pOther->CollisionProp()->CalcNearestPoint( WorldSpaceCenter(), &out );
  2022.  
  2023.     Vector vecForce = ( -impulse * pPhysics->GetMass() * 10 );
  2024.     CTakeDamageInfo info( this, this, vecForce, out, sk_rollermine_shock.GetFloat(), DMG_SHOCK );
  2025.  
  2026.     if( FClassnameIs( pOther, "npc_combine_s" ) )
  2027.     {
  2028.         if( pOther->GetHealth() <= (pOther->GetMaxHealth() / 2) )
  2029.         {
  2030.             // Instant special death for a combine soldier who has less than half health.
  2031.             Vector vecDamageForce = pOther->WorldSpaceCenter() - WorldSpaceCenter();
  2032.             VectorNormalize( vecDamageForce );
  2033.  
  2034.             IPhysicsObject *pPhysics = pOther->VPhysicsGetObject();
  2035.  
  2036.             if( pPhysics )
  2037.             {
  2038.                 vecDamageForce *= (pPhysics->GetMass() * 200.0f);
  2039.  
  2040.                 // Slam Z component with some good, reliable upwards velocity.
  2041.                 vecDamageForce.z = pPhysics->GetMass() * 200.0f;
  2042.             }
  2043.  
  2044.             pOther->MyCombatCharacterPointer()->BecomeRagdollBoogie( this, vecDamageForce, 5.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL );
  2045.             return;
  2046.         }
  2047.         else
  2048.         {
  2049.             info.SetDamage( pOther->GetMaxHealth()/2 );
  2050.         }
  2051.     }
  2052.  
  2053.     pOther->TakeDamage( info );
  2054.  
  2055.     // Knock players back a bit
  2056.     if ( pOther->IsPlayer() )
  2057.     {
  2058.         vecForce = -impulse;
  2059.         pOther->ApplyAbsVelocityImpulse( vecForce );
  2060.     }
  2061. }
  2062.  
  2063. //-----------------------------------------------------------------------------
  2064. // Purpose:
  2065. //-----------------------------------------------------------------------------
  2066. void CNPC_RollerMine::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  2067. {
  2068.     // Make sure we don't keep hitting the same entity
  2069.     int otherIndex = !index;
  2070.     CBaseEntity *pOther = pEvent->pEntities[otherIndex];
  2071.     if ( pEvent->deltaCollisionTime < 0.5 && (pOther == this) )
  2072.         return;
  2073.  
  2074.     BaseClass::VPhysicsCollision( index, pEvent );
  2075.  
  2076.     // If we've just hit a vehicle, we want to stick to it
  2077.     if ( m_bHeld || m_hVehicleStuckTo || !IsPlayerVehicle( pOther ) )
  2078.     {
  2079.         // Are we supposed to be embedding ourselves?
  2080.         if ( m_bEmbedOnGroundImpact )
  2081.         {
  2082.             // clear the flag so we don't queue more than once
  2083.             m_bEmbedOnGroundImpact = false;
  2084.             // call this when physics is done
  2085.             g_PostSimulationQueue.QueueCall( this, &CNPC_RollerMine::EmbedTouch, pOther );
  2086.         }
  2087.         return;
  2088.     }
  2089.  
  2090.     StickToVehicle( pOther );
  2091. }
  2092.  
  2093. //-----------------------------------------------------------------------------
  2094. // Purpose:
  2095. // Input  : *pOther -
  2096. //-----------------------------------------------------------------------------
  2097. void CNPC_RollerMine::StickToVehicle( CBaseEntity *pOther )
  2098. {
  2099.     IPhysicsObject *pOtherPhysics = pOther->VPhysicsGetObject();
  2100.     if ( !pOtherPhysics )
  2101.         return;
  2102.  
  2103.     // Don't stick to the wheels
  2104.     if ( pOtherPhysics->GetCallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL )
  2105.         return;
  2106.  
  2107.     // Destroy our constraint. This can happen if we had our constraint broken
  2108.     // and we still haven't cleaned up our constraint.
  2109.     UnstickFromVehicle();
  2110.  
  2111.     // We've hit the vehicle that the player's in.
  2112.     // Stick to it and slow it down.
  2113.     m_hVehicleStuckTo = pOther;
  2114.  
  2115.     IPhysicsObject *pPhysics = VPhysicsGetObject();
  2116.  
  2117.     // Constrain us to the vehicle
  2118.     constraint_fixedparams_t fixed;
  2119.     fixed.Defaults();
  2120.     fixed.InitWithCurrentObjectState( pOtherPhysics, pPhysics );
  2121.     fixed.constraint.Defaults();
  2122.     fixed.constraint.forceLimit = ImpulseScale( pPhysics->GetMass(), 200 );
  2123.     fixed.constraint.torqueLimit = ImpulseScale( pPhysics->GetMass(), 800 );
  2124.     m_pConstraint = physenv->CreateFixedConstraint( pOtherPhysics, pPhysics, NULL, fixed );
  2125.     m_pConstraint->SetGameData( (void *)this );
  2126.  
  2127.     // Kick the vehicle so the player knows we've arrived
  2128.     Vector impulse = pOther->GetAbsOrigin() - GetAbsOrigin();
  2129.     VectorNormalize( impulse );
  2130.     impulse.z = -0.75;
  2131.     VectorNormalize( impulse );
  2132.     impulse *= 600;
  2133.     Vector vecForce = impulse * pPhysics->GetMass() * 10;
  2134.     pOtherPhysics->ApplyForceOffset( vecForce, GetAbsOrigin() );
  2135.  
  2136.     // Get the velocity at the point we're sticking to
  2137.     Vector vecVelocity;
  2138.     pOtherPhysics->GetVelocityAtPoint( GetAbsOrigin(), &vecVelocity );
  2139.     AngularImpulse angNone( 0.0f, 0.0f, 0.0f );
  2140.     pPhysics->SetVelocity( &vecVelocity, &angNone );
  2141.  
  2142.     // Make sure we're spiky
  2143.     Open();
  2144.  
  2145.     AnnounceArrivalToOthers( pOther );
  2146.  
  2147.     // Also, jolt the vehicle sometime in the future
  2148.     g_EventQueue.AddEvent( this, "JoltVehicle", RandomFloat(3,6), NULL, NULL );
  2149. }
  2150.  
  2151. //-----------------------------------------------------------------------------
  2152. // Purpose:
  2153. //-----------------------------------------------------------------------------
  2154. int CNPC_RollerMine::CountRollersOnMyVehicle( CUtlVector<CNPC_RollerMine*> *pRollerList )
  2155. {
  2156.     CBaseEntity *entityList[64];
  2157.     Vector range(256,256,256);
  2158.     pRollerList->AddToTail( this );
  2159.     int boxCount = UTIL_EntitiesInBox( entityList, ARRAYSIZE(entityList), GetAbsOrigin()-range, GetAbsOrigin()+range, FL_NPC );
  2160.     for ( int i = 0; i < boxCount; i++ )
  2161.     {
  2162.         CAI_BaseNPC *pNPC = entityList[i]->MyNPCPointer();
  2163.         if ( pNPC && pNPC->m_iClassname == m_iClassname && pNPC != this )
  2164.         {
  2165.             // Found another rollermine
  2166.             CNPC_RollerMine *pMine = dynamic_cast<CNPC_RollerMine*>(pNPC);
  2167.             Assert( pMine );
  2168.  
  2169.             // Is he stuck to the same vehicle?
  2170.             if ( pMine->GetVehicleStuckTo() == GetVehicleStuckTo() )
  2171.             {
  2172.                 pRollerList->AddToTail( pMine );
  2173.             }
  2174.         }
  2175.     }
  2176.  
  2177.     return pRollerList->Count();
  2178. }
  2179.  
  2180. //-----------------------------------------------------------------------------
  2181. // Purpose: Tell other rollermines on the vehicle that I've just arrived
  2182. // Input  : *pOther -
  2183. //-----------------------------------------------------------------------------
  2184. void CNPC_RollerMine::AnnounceArrivalToOthers( CBaseEntity *pOther )
  2185. {
  2186.     // Now talk to any other rollermines stuck to the same vehicle
  2187.     CUtlVector<CNPC_RollerMine*> aRollersOnVehicle;
  2188.     int iRollers = CountRollersOnMyVehicle( &aRollersOnVehicle );
  2189.  
  2190.     // Stop all rollers on the vehicle falling off due to the force of the arriving one
  2191.     for ( int i = 0; i < iRollers; i++ )
  2192.     {
  2193.         aRollersOnVehicle[i]->PreventUnstickUntil( gpGlobals->curtime + 1 );
  2194.     }
  2195.  
  2196.     // See if we've got enough rollers on the vehicle to start being mean
  2197.     /*
  2198.     if ( iRollers >= ROLLERMINE_REQUIRED_TO_EXPLODE_VEHICLE )
  2199.     {
  2200.         // Alert the others
  2201.         EmitSound( "NPC_RollerMine.ExplodeChirp" );
  2202.  
  2203.         // Tell everyone to explode shortly
  2204.         for ( int i = 0; i < iRollers; i++ )
  2205.         {
  2206.             variant_t emptyVariant;
  2207.             g_EventQueue.AddEvent( aRollersOnVehicle[i], "RespondToExplodeChirp", RandomFloat(2,5), NULL, NULL );
  2208.         }
  2209.     }
  2210.     else
  2211.     {
  2212.     */
  2213.     // If there's other rollers on the vehicle, talk to them
  2214.     if ( iRollers > 1 )
  2215.     {
  2216.         // Chirp to the others
  2217.         EmitSound( "NPC_RollerMine.Chirp" );
  2218.  
  2219.         // Tell the others to respond (skip first slot, because that's me)
  2220.         for ( int i = 1; i < iRollers; i++ )
  2221.         {
  2222.             variant_t emptyVariant;
  2223.             g_EventQueue.AddEvent( aRollersOnVehicle[i], "RespondToChirp", RandomFloat(2,3), NULL, NULL );
  2224.         }
  2225.     }
  2226. //  }
  2227. }
  2228.  
  2229. //-----------------------------------------------------------------------------
  2230. // Purpose: Physics system has just told us our constraint has been broken
  2231. //-----------------------------------------------------------------------------
  2232. void CNPC_RollerMine::InputConstraintBroken( inputdata_t &inputdata )
  2233. {
  2234.     // Prevent rollermines being dislodged right as they stick
  2235.     if ( m_flPreventUnstickUntil > gpGlobals->curtime )
  2236.         return;
  2237.  
  2238.     // We can't delete it here safely
  2239.     UnstickFromVehicle();
  2240.     Close();
  2241.  
  2242.     // dazed
  2243.     m_RollerController.m_vecAngular.Init();
  2244.     m_flActiveTime = gpGlobals->curtime + GetStunDelay();
  2245. }
  2246.  
  2247. //-----------------------------------------------------------------------------
  2248. // Purpose: Respond to another rollermine that's chirped at us
  2249. // Input  : &inputdata -
  2250. //-----------------------------------------------------------------------------
  2251. void CNPC_RollerMine::InputRespondToChirp( inputdata_t &inputdata )
  2252. {
  2253.     EmitSound( "NPC_RollerMine.ChirpRespond" );
  2254. }
  2255.  
  2256. //-----------------------------------------------------------------------------
  2257. // Purpose: Respond to another rollermine's signal to detonate
  2258. // Input  : &inputdata -
  2259. //-----------------------------------------------------------------------------
  2260. void CNPC_RollerMine::InputRespondToExplodeChirp( inputdata_t &inputdata )
  2261. {
  2262.     EmitSound( "NPC_RollerMine.ExplodeChirpRespond" );
  2263.  
  2264.     Explode();
  2265. }
  2266.  
  2267. //-----------------------------------------------------------------------------
  2268. // Purpose: Apply a physics force to the vehicle we're in
  2269. // Input  : &inputdata -
  2270. //-----------------------------------------------------------------------------
  2271. void CNPC_RollerMine::InputJoltVehicle( inputdata_t &inputdata )
  2272. {
  2273.     Assert( GetVehicleStuckTo() );
  2274.  
  2275.     // First, tell all rollers on the vehicle not to fall off
  2276.     CUtlVector<CNPC_RollerMine*> aRollersOnVehicle;
  2277.     int iRollers = CountRollersOnMyVehicle( &aRollersOnVehicle );
  2278.     for ( int i = 0; i < iRollers; i++ )
  2279.     {
  2280.         aRollersOnVehicle[i]->PreventUnstickUntil( gpGlobals->curtime + 1 );
  2281.     }
  2282.  
  2283.     // Now smack the vehicle
  2284.     Vector impulse = GetVehicleStuckTo()->GetAbsOrigin() - GetAbsOrigin();
  2285.     VectorNormalize( impulse );
  2286.     // Randomly apply a little vertical lift, to get the wheels off the ground
  2287.     impulse.z = RandomFloat( 0.5, 1.0 );
  2288.     VectorNormalize( impulse );
  2289.     IPhysicsObject *pVehiclePhysics = GetVehicleStuckTo()->VPhysicsGetObject();
  2290.     Vector vecForce = impulse * ImpulseScale( pVehiclePhysics->GetMass(), RandomFloat(150,250) );
  2291.     pVehiclePhysics->ApplyForceOffset( vecForce, GetAbsOrigin() );
  2292.  
  2293.     // Play sounds & effects
  2294.     EmitSound( "NPC_RollerMine.JoltVehicle" );
  2295.  
  2296.     // UNDONE: Good Zap effects
  2297.     /*
  2298.     CBeam *pBeam = CBeam::BeamCreate( "sprites/rollermine_shock.vmt", 4 );
  2299.     if ( pBeam )
  2300.     {
  2301.         pBeam->EntsInit( GetVehicleStuckTo(), this );
  2302.         CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>( GetVehicleStuckTo() );
  2303.         if ( pAnimating )
  2304.         {
  2305.             int startAttach = pAnimating->LookupAttachment("beam_damage" );
  2306.             pBeam->SetStartAttachment( startAttach );
  2307.         }
  2308.         pBeam->SetEndAttachment( 1 );
  2309.         pBeam->SetWidth( 8 );
  2310.         pBeam->SetEndWidth( 8 );
  2311.         pBeam->SetBrightness( 255 );
  2312.         pBeam->SetColor( 255, 255, 255 );
  2313.         pBeam->LiveForTime( 0.5f );
  2314.         pBeam->RelinkBeam();
  2315.         pBeam->SetNoise( 30 );
  2316.     }
  2317.     */
  2318.  
  2319.     ShockTarget( GetVehicleStuckTo() );
  2320.  
  2321.     // Jolt again soon
  2322.     g_EventQueue.AddEvent( this, "JoltVehicle", RandomFloat(3,6), NULL, NULL );
  2323. }
  2324.  
  2325. //-----------------------------------------------------------------------------
  2326. // Purpose:
  2327. // Input  : &inputdata -
  2328. //-----------------------------------------------------------------------------
  2329. void CNPC_RollerMine::InputTurnOn( inputdata_t &inputdata )
  2330. {
  2331.     m_RollerController.On();
  2332.     m_bTurnedOn = true;
  2333. }
  2334.  
  2335. //-----------------------------------------------------------------------------
  2336. // Purpose:
  2337. // Input  : &inputdata -
  2338. //-----------------------------------------------------------------------------
  2339. void CNPC_RollerMine::InputTurnOff( inputdata_t &inputdata )
  2340. {
  2341.     m_RollerController.Off();
  2342.     m_bTurnedOn = false;
  2343.     StopLoopingSounds();
  2344. }
  2345.  
  2346. //-----------------------------------------------------------------------------
  2347. // Purpose:
  2348. // Input  : &inputdata -
  2349. //-----------------------------------------------------------------------------
  2350. void CNPC_RollerMine::InputPowerdown( inputdata_t &inputdata )
  2351. {
  2352.     m_bPowerDown = true;
  2353.     m_flPowerDownTime = gpGlobals->curtime + RandomFloat( 0.1, 0.5 );
  2354.     m_flPowerDownDetonateTime = m_flPowerDownTime + RandomFloat( 1.5, 4.0 );
  2355.  
  2356.     ClearSchedule( "Received power down input" );
  2357. }
  2358.  
  2359. //-----------------------------------------------------------------------------
  2360. // Purpose: If we were stuck to a vehicle, remove ourselves
  2361. //-----------------------------------------------------------------------------
  2362. void CNPC_RollerMine::UnstickFromVehicle( void )
  2363. {
  2364.     if ( m_pConstraint )
  2365.     {
  2366.         physenv->DestroyConstraint( m_pConstraint );
  2367.         m_pConstraint = NULL;
  2368.     }
  2369.  
  2370.     // Cancel any pending jolt events
  2371.     g_EventQueue.CancelEventOn( this, "JoltVehicle" );
  2372.  
  2373.     m_hVehicleStuckTo = NULL;
  2374. }
  2375.  
  2376. //-----------------------------------------------------------------------------
  2377. // Purpose:
  2378. //-----------------------------------------------------------------------------
  2379. CBaseEntity *CNPC_RollerMine::GetVehicleStuckTo( void )
  2380. {
  2381.     if ( !m_pConstraint )
  2382.         return NULL;
  2383.  
  2384.     return m_hVehicleStuckTo;
  2385. }
  2386.  
  2387. //-----------------------------------------------------------------------------
  2388. // Purpose:
  2389. // Input  : *pPhysGunUser -
  2390. //-----------------------------------------------------------------------------
  2391. void CNPC_RollerMine::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  2392. {
  2393.     // Are we just being punted?
  2394.     if ( reason == PUNTED_BY_CANNON )
  2395.     {
  2396.         // Be stunned
  2397.         m_flActiveTime = gpGlobals->curtime + GetStunDelay();
  2398.         return;
  2399.     }
  2400.  
  2401.     //Stop turning
  2402.     m_RollerController.m_vecAngular = vec3_origin;
  2403.  
  2404.     UnstickFromVehicle();
  2405.  
  2406.     m_OnPhysGunPickup.FireOutput( pPhysGunUser, this );
  2407.     m_bHeld = true;
  2408.     m_RollerController.Off();
  2409.     EmitSound( "NPC_RollerMine.Held" );
  2410. }
  2411.  
  2412. //-----------------------------------------------------------------------------
  2413. // Purpose:
  2414. // Input  : *pPhysGunUser -
  2415. //-----------------------------------------------------------------------------
  2416. void CNPC_RollerMine::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  2417. {
  2418.     m_bHeld = false;
  2419.     m_flActiveTime = gpGlobals->curtime + GetStunDelay();
  2420.     m_RollerController.On();
  2421.    
  2422.     // explode on contact if launched from the physgun
  2423.     if ( Reason == LAUNCHED_BY_CANNON )
  2424.     {
  2425.         if ( m_bIsOpen )
  2426.         {
  2427.             //m_bIsPrimed = true;
  2428.             SetTouch( &CNPC_RollerMine::SpikeTouch );
  2429.             // enable world/prop touch too
  2430.             VPhysicsGetObject()->SetCallbackFlags( VPhysicsGetObject()->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH|CALLBACK_GLOBAL_TOUCH_STATIC );
  2431.         }
  2432.         EmitSound( "NPC_RollerMine.Tossed" );
  2433.     }
  2434.  
  2435.     m_OnPhysGunDrop.FireOutput( pPhysGunUser, this );
  2436. }
  2437.  
  2438. //-----------------------------------------------------------------------------
  2439. // Purpose:
  2440. // Input  : &info -
  2441. // Output : float
  2442. //-----------------------------------------------------------------------------
  2443. int CNPC_RollerMine::OnTakeDamage( const CTakeDamageInfo &info )
  2444. {
  2445.     if ( !(info.GetDamageType() & DMG_BURN) )
  2446.     {
  2447.         if ( GetMoveType() == MOVETYPE_VPHYSICS )
  2448.         {
  2449.             AngularImpulse  angVel;
  2450.             angVel.Random( -400.0f, 400.0f );
  2451.             VPhysicsGetObject()->AddVelocity( NULL, &angVel );
  2452.             m_RollerController.m_vecAngular *= 0.8f;
  2453.  
  2454.             VPhysicsTakeDamage( info );
  2455.         }
  2456.         SetCondition( COND_LIGHT_DAMAGE );
  2457.     }
  2458.  
  2459.     if ( info.GetDamageType() & (DMG_BURN|DMG_BLAST) )
  2460.     {
  2461.         if ( info.GetAttacker() && info.GetAttacker()->m_iClassname != m_iClassname )
  2462.         {
  2463.             SetThink( &CNPC_RollerMine::PreDetonate );
  2464.             SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.5f ) );
  2465.         }
  2466.         else
  2467.         {
  2468.             // dazed
  2469.             m_RollerController.m_vecAngular.Init();
  2470.             m_flActiveTime = gpGlobals->curtime + GetStunDelay();
  2471.             Hop( 300 );
  2472.         }
  2473.     }
  2474.  
  2475.     return 0;
  2476. }
  2477.  
  2478. //-----------------------------------------------------------------------------
  2479. // Purpose: Causes the roller to hop into the air
  2480. //-----------------------------------------------------------------------------
  2481. void CNPC_RollerMine::Hop( float height )
  2482. {
  2483.     if ( m_flNextHop > gpGlobals->curtime )
  2484.         return;
  2485.  
  2486.     if ( GetMoveType() == MOVETYPE_VPHYSICS )
  2487.     {
  2488.         IPhysicsObject *pPhysObj = VPhysicsGetObject();
  2489.         pPhysObj->ApplyForceCenter( Vector(0,0,1) * height * pPhysObj->GetMass() );
  2490.        
  2491.         AngularImpulse  angVel;
  2492.         angVel.Random( -400.0f, 400.0f );
  2493.         pPhysObj->AddVelocity( NULL, &angVel );
  2494.  
  2495.         m_flNextHop = gpGlobals->curtime + ROLLERMINE_HOP_DELAY;
  2496.     }
  2497. }
  2498.  
  2499. //-----------------------------------------------------------------------------
  2500. // Purpose: Makes warning noise before actual explosion occurs
  2501. //-----------------------------------------------------------------------------
  2502. void CNPC_RollerMine::PreDetonate( void )
  2503. {
  2504.     Hop( 300 );
  2505.  
  2506.     SetTouch( NULL );
  2507.     SetThink( &CNPC_RollerMine::Explode );
  2508.     SetNextThink( gpGlobals->curtime + 0.5f );
  2509.  
  2510.     EmitSound( "NPC_RollerMine.Hurt" );
  2511. }
  2512.  
  2513. //-----------------------------------------------------------------------------
  2514. // Purpose:
  2515. //-----------------------------------------------------------------------------
  2516. void CNPC_RollerMine::Explode( void )
  2517. {
  2518.     m_takedamage = DAMAGE_NO;
  2519.  
  2520.     //FIXME: Hack to make thrown mines more deadly and fun
  2521.     float expDamage = m_bIsPrimed ? 100 : 25;
  2522.  
  2523.     //If we've been hacked and we're blowing up cause we've been shut down then do moderate damage.
  2524.     if ( m_bPowerDown == true )
  2525.     {
  2526.         expDamage = 50;
  2527.     }
  2528.  
  2529.     // Underwater explosion?
  2530.     if ( UTIL_PointContents( GetAbsOrigin() ) & MASK_WATER )
  2531.     {
  2532.         CEffectData data;
  2533.         data.m_vOrigin = WorldSpaceCenter();
  2534.         data.m_flMagnitude = expDamage;
  2535.         data.m_flScale = 128;
  2536.         data.m_fFlags = ( SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE );
  2537.         DispatchEffect( "WaterSurfaceExplosion", data );
  2538.     }
  2539.     else
  2540.     {
  2541.         ExplosionCreate( WorldSpaceCenter(), GetLocalAngles(), this, (int)expDamage, 128, true );
  2542.     }
  2543.  
  2544.     CTakeDamageInfo info( this, this, 1, DMG_GENERIC );
  2545.     Event_Killed( info );
  2546.  
  2547.     // Remove myself a frame from now to avoid doing it in the middle of running AI
  2548.     SetThink( &CNPC_RollerMine::SUB_Remove );
  2549.     SetNextThink( gpGlobals->curtime );
  2550. }
  2551.  
  2552. const float MAX_ROLLING_SPEED = 720;
  2553.  
  2554. //-----------------------------------------------------------------------------
  2555. // Purpose:
  2556. //-----------------------------------------------------------------------------
  2557. float CNPC_RollerMine::RollingSpeed()
  2558. {
  2559.     IPhysicsObject *pPhysics = VPhysicsGetObject();
  2560.     if ( !m_hVehicleStuckTo && !m_bHeld && pPhysics && !pPhysics->IsAsleep() )
  2561.     {
  2562.         AngularImpulse angVel;
  2563.         pPhysics->GetVelocity( NULL, &angVel );
  2564.         float rollingSpeed = angVel.Length() - 90;
  2565.         rollingSpeed = clamp( rollingSpeed, 1, MAX_ROLLING_SPEED );
  2566.         rollingSpeed *= (1/MAX_ROLLING_SPEED);
  2567.         return rollingSpeed;
  2568.     }
  2569.     return 0;
  2570. }
  2571.  
  2572. //-----------------------------------------------------------------------------
  2573. //-----------------------------------------------------------------------------
  2574. float CNPC_RollerMine::GetStunDelay()
  2575. {
  2576.     if( m_bHackedByAlyx )
  2577.     {
  2578.         return 0.1f;
  2579.     }
  2580.     else
  2581.     {
  2582.         return sk_rollermine_stun_delay.GetFloat();
  2583.     }
  2584. }
  2585.  
  2586. //-----------------------------------------------------------------------------
  2587. // Purpose: We've been dropped by a dropship. Embed in the ground if we land on it.
  2588. //-----------------------------------------------------------------------------
  2589. void CNPC_RollerMine::EmbedOnGroundImpact()
  2590. {
  2591.     m_bEmbedOnGroundImpact = true;
  2592.  
  2593.     SetTouch( &CNPC_RollerMine::EmbedTouch );
  2594. }
  2595.  
  2596. //-----------------------------------------------------------------------------
  2597. // Purpose:
  2598. //-----------------------------------------------------------------------------
  2599. void CNPC_RollerMine::PrescheduleThink()
  2600. {
  2601.     // Are we underwater?
  2602.     if ( UTIL_PointContents( GetAbsOrigin() ) & MASK_WATER )
  2603.     {
  2604.         // As soon as we're far enough underwater, detonate
  2605.         Vector vecAboveMe = GetAbsOrigin() + Vector(0,0,64);
  2606.         if ( UTIL_PointContents( vecAboveMe ) & MASK_WATER )
  2607.         {
  2608.             Explode();
  2609.             return;
  2610.         }
  2611.     }
  2612.  
  2613.     UpdateRollingSound();
  2614.     UpdatePingSound();
  2615.     BaseClass::PrescheduleThink();
  2616. }
  2617.  
  2618. //-----------------------------------------------------------------------------
  2619. // Purpose:
  2620. //-----------------------------------------------------------------------------
  2621. void CNPC_RollerMine::UpdateRollingSound()
  2622. {
  2623.     if ( m_rollingSoundState == ROLL_SOUND_NOT_READY )
  2624.         return;
  2625.  
  2626.     rollingsoundstate_t soundState = ROLL_SOUND_OFF;
  2627.     float rollingSpeed = RollingSpeed();
  2628.     if ( rollingSpeed > 0 )
  2629.     {
  2630.         soundState = m_bIsOpen ? ROLL_SOUND_OPEN : ROLL_SOUND_CLOSED;
  2631.     }
  2632.  
  2633.  
  2634.     CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2635.     CSoundParameters params;
  2636.     switch( soundState )
  2637.     {
  2638.     case ROLL_SOUND_CLOSED:
  2639.         CBaseEntity::GetParametersForSound( "NPC_RollerMine.Roll", params, NULL );
  2640.         break;
  2641.     case ROLL_SOUND_OPEN:
  2642.         CBaseEntity::GetParametersForSound( "NPC_RollerMine.RollWithSpikes", params, NULL );
  2643.         break;
  2644.  
  2645.     case ROLL_SOUND_OFF:
  2646.         // no sound
  2647.         break;
  2648.  
  2649.     default:
  2650.         break;
  2651.     }
  2652.  
  2653.     // start the new sound playing if necessary
  2654.     if ( m_rollingSoundState != soundState )
  2655.     {
  2656.         StopRollingSound();
  2657.  
  2658.         m_rollingSoundState = soundState;
  2659.  
  2660.         if ( m_rollingSoundState == ROLL_SOUND_OFF )
  2661.             return;
  2662.  
  2663.         CPASAttenuationFilter filter( this );
  2664.         m_pRollSound = controller.SoundCreate( filter, entindex(), params.channel, params.soundname, params.soundlevel );
  2665.         controller.Play( m_pRollSound, params.volume, params.pitch );
  2666.         m_rollingSoundState = soundState;
  2667.     }
  2668.  
  2669.     if ( m_pRollSound )
  2670.     {
  2671.         // for tuning
  2672.         //DevMsg("SOUND: %s, VOL: %.1f\n", m_rollingSoundState == ROLL_SOUND_CLOSED ? "CLOSED" : "OPEN ", rollingSpeed );
  2673.         controller.SoundChangePitch( m_pRollSound, params.pitchlow + (params.pitchhigh - params.pitchlow) * rollingSpeed, 0.1 );
  2674.         controller.SoundChangeVolume( m_pRollSound, params.volume * rollingSpeed, 0.1 );
  2675.     }
  2676. }
  2677.  
  2678.  
  2679. void CNPC_RollerMine::StopRollingSound()
  2680. {
  2681.     CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2682.     controller.SoundDestroy( m_pRollSound );
  2683.     m_pRollSound = NULL;
  2684. }
  2685.  
  2686. void CNPC_RollerMine::UpdatePingSound()
  2687. {
  2688.     float pingSpeed = 0;
  2689.     if ( m_bIsOpen && !IsShocking() && !m_bHeld )
  2690.     {
  2691.         CBaseEntity *pEnemy = GetEnemy();
  2692.         if ( pEnemy )
  2693.         {
  2694.             pingSpeed = EnemyDistance( pEnemy );
  2695.             pingSpeed = clamp( pingSpeed, 1, ROLLERMINE_OPEN_THRESHOLD );
  2696.             pingSpeed *= (1.0f/ROLLERMINE_OPEN_THRESHOLD);
  2697.         }
  2698.     }
  2699.  
  2700.     if ( pingSpeed > 0 )
  2701.     {
  2702.         pingSpeed = 1-pingSpeed;
  2703.         CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2704.         CSoundParameters params;
  2705.         CBaseEntity::GetParametersForSound( "NPC_RollerMine.Ping", params, NULL );
  2706.         if ( !m_pPingSound )
  2707.         {
  2708.             CPASAttenuationFilter filter( this );
  2709.             m_pPingSound = controller.SoundCreate( filter, entindex(), params.channel, params.soundname, params.soundlevel );
  2710.             controller.Play( m_pPingSound, params.volume, 101 );
  2711.         }
  2712.  
  2713.         controller.SoundChangePitch( m_pPingSound, params.pitchlow + (params.pitchhigh - params.pitchlow) * pingSpeed, 0.1 );
  2714.         controller.SoundChangeVolume( m_pPingSound, params.volume, 0.1 );
  2715.         //DevMsg("PING: %.1f\n", pingSpeed );
  2716.  
  2717.     }
  2718.     else
  2719.     {
  2720.         StopPingSound();
  2721.     }
  2722. }
  2723.  
  2724.  
  2725. void CNPC_RollerMine::StopPingSound()
  2726. {
  2727.     CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2728.     controller.SoundDestroy( m_pPingSound );
  2729.     m_pPingSound = NULL;
  2730. }
  2731.  
  2732. //-----------------------------------------------------------------------------
  2733. // Purpose:
  2734. //-----------------------------------------------------------------------------
  2735. void CNPC_RollerMine::StopLoopingSounds( void )
  2736. {
  2737.     StopRollingSound();
  2738.     StopPingSound();
  2739.     BaseClass::StopLoopingSounds();
  2740. }
  2741.  
  2742. //-----------------------------------------------------------------------------
  2743. // Purpose:
  2744. // Input  : *pEnemy -
  2745. // Output : Returns true on success, false on failure.
  2746. //-----------------------------------------------------------------------------
  2747. bool CNPC_RollerMine::IsValidEnemy( CBaseEntity *pEnemy )
  2748. {
  2749.     // If the enemy's over the vehicle detection range, and it's not a player in a vehicle, ignore it
  2750.     if ( pEnemy )
  2751.     {
  2752.         float flDistance = GetAbsOrigin().DistTo( pEnemy->GetAbsOrigin() );
  2753.         if ( flDistance >= m_flSeeVehiclesOnlyBeyond )
  2754.         {
  2755.             // Handle vehicles
  2756.             CBaseCombatCharacter *pCCEnemy = pEnemy->MyCombatCharacterPointer();
  2757.             if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() )
  2758.             {
  2759.                 // If we're buried, we only care when they're heading directly towards us
  2760.                 if ( m_bBuried )
  2761.                     return ( VehicleHeading( pCCEnemy->GetVehicle()->GetVehicleEnt() ) > DOT_20DEGREE );
  2762.  
  2763.                 // If we're not buried, chase him as long as he's not heading away from us
  2764.                 return ( VehicleHeading( pCCEnemy->GetVehicleEntity() ) > 0 );
  2765.             }
  2766.  
  2767.             return false;
  2768.         }
  2769.  
  2770.         // Never pick something I fear
  2771.         if ( IRelationType( pEnemy ) == D_FR )
  2772.             return false;
  2773.  
  2774.         // Don't attack flying things.
  2775.         if ( pEnemy->GetMoveType() == MOVETYPE_FLY )
  2776.             return false;
  2777.     }
  2778.  
  2779.     return BaseClass::IsValidEnemy( pEnemy );
  2780. }
  2781.  
  2782. //-----------------------------------------------------------------------------
  2783. // Purpose:
  2784. //-----------------------------------------------------------------------------
  2785. bool CNPC_RollerMine::EnemyInVehicle( void )
  2786. {
  2787.     // Clearly the enemy is not...
  2788.     if ( GetEnemy() == NULL )
  2789.         return false;
  2790.  
  2791.     // If the target is in a vehicle, let the convar choose
  2792.     CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer();
  2793.     if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() )
  2794.         return ( sk_rollermine_vehicle_intercept.GetBool() );
  2795.  
  2796.     return false;
  2797. }
  2798.  
  2799. //-----------------------------------------------------------------------------
  2800. // Purpose:
  2801. //-----------------------------------------------------------------------------
  2802. float CNPC_RollerMine::VehicleHeading( CBaseEntity *pVehicle )
  2803. {
  2804.     Vector vecVelocity = pVehicle->GetSmoothedVelocity();
  2805.     float flSpeed = VectorNormalize( vecVelocity );
  2806.     Vector vecToMine = GetAbsOrigin() - pVehicle->GetAbsOrigin();
  2807.     VectorNormalize( vecToMine );
  2808.  
  2809.     // If it's not moving, consider it moving towards us, but not directly
  2810.     // This will enable already active rollers to chase the vehicle if it's stationary.
  2811.     if ( flSpeed < 10 )
  2812.         return 0.1;
  2813.  
  2814.     return DotProduct( vecVelocity, vecToMine );
  2815. }
  2816.  
  2817. //-----------------------------------------------------------------------------
  2818. // Purpose:
  2819. // Input  : &info -
  2820. //          &vecDir -
  2821. //          *ptr -
  2822. //-----------------------------------------------------------------------------
  2823. void CNPC_RollerMine::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  2824. {
  2825.     if ( info.GetDamageType() & (DMG_BULLET | DMG_CLUB) )
  2826.     {
  2827.         CTakeDamageInfo newInfo( info );
  2828.  
  2829.         // If we're stuck to the car, increase it even more
  2830.         if ( GetVehicleStuckTo() )
  2831.         {
  2832.             newInfo.SetDamageForce( info.GetDamageForce() * 40 );
  2833.         }
  2834.         else
  2835.         {
  2836.             newInfo.SetDamageForce( info.GetDamageForce() * 20 );
  2837.         }
  2838.  
  2839.         BaseClass::TraceAttack( newInfo, vecDir, ptr );
  2840.         return;
  2841.     }
  2842.  
  2843.     BaseClass::TraceAttack( info, vecDir, ptr );
  2844. }
  2845.  
  2846. //-----------------------------------------------------------------------------
  2847. //
  2848. // Schedules
  2849. //
  2850. //-----------------------------------------------------------------------------
  2851.  
  2852. AI_BEGIN_CUSTOM_NPC( npc_rollermine, CNPC_RollerMine )
  2853.  
  2854.     //Tasks
  2855.     DECLARE_TASK( TASK_ROLLERMINE_CHARGE_ENEMY )
  2856.     DECLARE_TASK( TASK_ROLLERMINE_BURIED_WAIT )
  2857.     DECLARE_TASK( TASK_ROLLERMINE_UNBURROW )
  2858.     DECLARE_TASK( TASK_ROLLERMINE_GET_PATH_TO_FLEE )
  2859.     DECLARE_TASK( TASK_ROLLERMINE_NUDGE_TOWARDS_NODES )
  2860.     DECLARE_TASK( TASK_ROLLERMINE_RETURN_TO_PLAYER )
  2861.     DECLARE_TASK( TASK_ROLLERMINE_POWERDOWN )
  2862.  
  2863.     //Schedules
  2864.  
  2865.     DEFINE_SCHEDULE
  2866.     (
  2867.     SCHED_ROLLERMINE_BURIED_WAIT,
  2868.  
  2869.         "   Tasks"
  2870.         "       TASK_ROLLERMINE_BURIED_WAIT     0"
  2871.         "   "
  2872.         "   Interrupts"
  2873.         "       COND_NEW_ENEMY"
  2874.         "       COND_LIGHT_DAMAGE"
  2875.     )
  2876.  
  2877.     DEFINE_SCHEDULE
  2878.     (
  2879.     SCHED_ROLLERMINE_BURIED_UNBURROW,
  2880.  
  2881.         "   Tasks"
  2882.         "       TASK_ROLLERMINE_UNBURROW        0"
  2883.         "   "
  2884.         "   Interrupts"
  2885.     )
  2886.    
  2887.     DEFINE_SCHEDULE
  2888.     (
  2889.     SCHED_ROLLERMINE_RANGE_ATTACK1,
  2890.  
  2891.         "   Tasks"
  2892.         "       TASK_SET_FAIL_SCHEDULE          SCHEDULE:SCHED_CHASE_ENEMY"
  2893.         "       TASK_ROLLERMINE_CHARGE_ENEMY    0"
  2894.         "   "
  2895.         "   Interrupts"
  2896.         "       COND_ENEMY_DEAD"
  2897.         "       COND_NEW_ENEMY"
  2898.         "       COND_ENEMY_OCCLUDED"
  2899.         "       COND_ENEMY_TOO_FAR"
  2900.     )
  2901.    
  2902.     DEFINE_SCHEDULE
  2903.     (
  2904.     SCHED_ROLLERMINE_CHASE_ENEMY,
  2905.  
  2906.         "   Tasks"
  2907.         "       TASK_SET_FAIL_SCHEDULE          SCHEDULE:SCHED_ROLLERMINE_RANGE_ATTACK1"
  2908.         "       TASK_SET_TOLERANCE_DISTANCE     24"
  2909.         "       TASK_GET_PATH_TO_ENEMY          0"
  2910.         "       TASK_RUN_PATH                   0"
  2911.         "       TASK_WAIT_FOR_MOVEMENT          0"
  2912.         "   "
  2913.         "   Interrupts"
  2914.         "       COND_ENEMY_DEAD"
  2915.         "       COND_ENEMY_UNREACHABLE"
  2916.         "       COND_ENEMY_TOO_FAR"
  2917.         "       COND_CAN_RANGE_ATTACK1"
  2918.         "       COND_TASK_FAILED"
  2919.         "       COND_SEE_FEAR"
  2920.     )
  2921.  
  2922.     DEFINE_SCHEDULE
  2923.     (
  2924.     SCHED_ROLLERMINE_FLEE,
  2925.  
  2926.         "   Tasks"
  2927.         "       TASK_SET_FAIL_SCHEDULE              SCHEDULE:SCHED_IDLE_STAND"
  2928.         "       TASK_ROLLERMINE_GET_PATH_TO_FLEE    300"
  2929.         "       TASK_RUN_PATH                       0"
  2930.         "       TASK_STOP_MOVING                    0"
  2931.         "   "
  2932.         "   Interrupts"
  2933.         "       COND_NEW_ENEMY"
  2934.         "       COND_TASK_FAILED"
  2935.     )
  2936.  
  2937.     DEFINE_SCHEDULE
  2938.     (
  2939.     SCHED_ROLLERMINE_ALERT_STAND,
  2940.  
  2941.     "   Tasks"
  2942.     "       TASK_STOP_MOVING            0"
  2943.     "       TASK_FACE_REASONABLE        0"
  2944.     "       TASK_SET_ACTIVITY           ACTIVITY:ACT_IDLE"
  2945.     "       TASK_WAIT                   2"
  2946.     ""
  2947.     "   Interrupts"
  2948.     "       COND_NEW_ENEMY"
  2949.     "       COND_SEE_ENEMY"
  2950.     "       COND_SEE_FEAR"
  2951.     "       COND_LIGHT_DAMAGE"
  2952.     "       COND_HEAVY_DAMAGE"
  2953.     "       COND_PROVOKED"
  2954.     "       COND_SMELL"
  2955.     "       COND_HEAR_COMBAT"       // sound flags
  2956.     "       COND_HEAR_WORLD"
  2957.     "       COND_HEAR_PLAYER"
  2958.     "       COND_HEAR_DANGER"
  2959.     "       COND_HEAR_BULLET_IMPACT"
  2960.     "       COND_IDLE_INTERRUPT"
  2961.     )
  2962.  
  2963.     DEFINE_SCHEDULE
  2964.     (
  2965.     SCHED_ROLLERMINE_NUDGE_TOWARDS_NODES,
  2966.  
  2967.     "   Tasks"
  2968.     "       TASK_ROLLERMINE_NUDGE_TOWARDS_NODES     0"
  2969.     "       TASK_WAIT                               1.5"
  2970.     ""
  2971.     "   Interrupts"
  2972.     ""
  2973.     )
  2974.  
  2975.     DEFINE_SCHEDULE
  2976.     (
  2977.         SCHED_ROLLERMINE_PATH_TO_PLAYER,
  2978.  
  2979.         "   Tasks"
  2980.         "       TASK_SET_FAIL_SCHEDULE          SCHEDULE:SCHED_ROLLERMINE_ALERT_STAND"
  2981.         "       TASK_SET_TOLERANCE_DISTANCE     200"
  2982.         "       TASK_GET_PATH_TO_PLAYER         0"
  2983.         "       TASK_RUN_PATH                   0"
  2984.         "       TASK_WAIT_FOR_MOVEMENT          0"
  2985.         ""
  2986.         "   Interrupts"
  2987.         "       COND_NEW_ENEMY"
  2988.         "       COND_SEE_ENEMY"
  2989.         "       COND_SEE_FEAR"
  2990.         "       COND_LIGHT_DAMAGE"
  2991.         "       COND_HEAVY_DAMAGE"
  2992.         "       COND_PROVOKED"
  2993.         "       COND_SMELL"
  2994.         "       COND_HEAR_COMBAT"       // sound flags
  2995.         "       COND_HEAR_WORLD"
  2996.         "       COND_HEAR_PLAYER"
  2997.         "       COND_HEAR_DANGER"
  2998.         "       COND_HEAR_BULLET_IMPACT"
  2999.         "       COND_IDLE_INTERRUPT"
  3000.         "       COND_SEE_PLAYER"
  3001.     )
  3002.  
  3003.     DEFINE_SCHEDULE
  3004.     (
  3005.         SCHED_ROLLERMINE_ROLL_TO_PLAYER,
  3006.  
  3007.         "   Tasks"
  3008.         "       TASK_SET_FAIL_SCHEDULE              SCHEDULE:SCHED_ROLLERMINE_ALERT_STAND"
  3009.         "       TASK_SET_TOLERANCE_DISTANCE         200"
  3010.         "       TASK_ROLLERMINE_RETURN_TO_PLAYER    0"
  3011.         ""
  3012.         "   Interrupts"
  3013.         "       COND_NEW_ENEMY"
  3014.         "       COND_SEE_ENEMY"
  3015.         "       COND_SEE_FEAR"
  3016.         "       COND_LIGHT_DAMAGE"
  3017.         "       COND_HEAVY_DAMAGE"
  3018.         "       COND_PROVOKED"
  3019.         "       COND_SMELL"
  3020.         "       COND_HEAR_COMBAT"       // sound flags
  3021.         "       COND_HEAR_WORLD"
  3022.         "       COND_HEAR_PLAYER"
  3023.         "       COND_HEAR_DANGER"
  3024.         "       COND_HEAR_BULLET_IMPACT"
  3025.         "       COND_IDLE_INTERRUPT"
  3026.     )
  3027.  
  3028.     DEFINE_SCHEDULE
  3029.     (
  3030.         SCHED_ROLLERMINE_POWERDOWN,
  3031.  
  3032.         "   Tasks"
  3033.         "       TASK_STOP_MOVING            0"
  3034.         "       TASK_SET_ACTIVITY           ACTIVITY:ACT_IDLE"
  3035.         "       TASK_ROLLERMINE_POWERDOWN   0"
  3036.         ""
  3037.         "   Interrupts"
  3038.         ""
  3039.     );
  3040.  
  3041. AI_END_CUSTOM_NPC()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement