Advertisement
Guest User

npc_manhack.cpp

a guest
Apr 28th, 2013
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 95.54 KB | None | 0 0
  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6.  
  7. #include "cbase.h"
  8. #include "soundenvelope.h"
  9. #include "npc_manhack.h"
  10. #include "ai_default.h"
  11. #include "ai_node.h"
  12. #include "ai_navigator.h"
  13. #include "ai_pathfinder.h"
  14. #include "ai_moveprobe.h"
  15. #include "ai_memory.h"
  16. #include "ai_squad.h"
  17. #include "ai_route.h"
  18. #include "explode.h"
  19. #include "basegrenade_shared.h"
  20. #include "ndebugoverlay.h"
  21. #include "decals.h"
  22. #include "gib.h"
  23. #include "game.h"          
  24. #include "ai_interactions.h"
  25. #include "IEffects.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "movevars_shared.h"
  29. #include "npcevent.h"
  30. #include "props.h"
  31. #include "te_effect_dispatch.h"
  32. #include "ai_squadslot.h"
  33. #include "world.h"
  34. #include "smoke_trail.h"
  35. #include "func_break.h"
  36. #include "physics_impact_damage.h"
  37. #include "weapon_physcannon.h"
  38. #include "physics_prop_ragdoll.h"
  39. #include "soundent.h"
  40. #include "ammodef.h"
  41.  
  42. // memdbgon must be the last include file in a .cpp file!!!
  43. #include "tier0/memdbgon.h"
  44.  
  45. // When the engine is running and the manhack is operating under power
  46. // we don't let gravity affect him.
  47. #define MANHACK_GRAVITY 0.000
  48.  
  49. #define MANHACK_GIB_COUNT           5
  50. #define MANHACK_INGORE_WATER_DIST   384
  51.  
  52. // Sound stuff
  53. #define MANHACK_PITCH_DIST1     512
  54. #define MANHACK_MIN_PITCH1      (100)
  55. #define MANHACK_MAX_PITCH1      (160)
  56. #define MANHACK_WATER_PITCH1    (85)
  57. #define MANHACK_VOLUME1         0.55
  58.  
  59. #define MANHACK_PITCH_DIST2     400
  60. #define MANHACK_MIN_PITCH2      (85)
  61. #define MANHACK_MAX_PITCH2      (190)
  62. #define MANHACK_WATER_PITCH2    (90)
  63.  
  64. #define MANHACK_NOISEMOD_HIDE 5000
  65.  
  66. #define MANHACK_BODYGROUP_BLADE 1
  67. #define MANHACK_BODYGROUP_BLUR  2
  68. #define MANHACK_BODYGROUP_OFF   0
  69. #define MANHACK_BODYGROUP_ON    1
  70.  
  71. // ANIMATION EVENTS
  72. #define MANHACK_AE_START_ENGINE         50
  73. #define MANHACK_AE_DONE_UNPACKING       51
  74. #define MANHACK_AE_OPEN_BLADE           52
  75.  
  76. //#define MANHACK_GLOW_SPRITE   "sprites/laserdot.vmt"
  77. #define MANHACK_GLOW_SPRITE "sprites/glow1.vmt"
  78.  
  79. #define MANHACK_CHARGE_MIN_DIST 200
  80.  
  81. ConVar  sk_manhack_health( "sk_manhack_health","0");
  82. ConVar  sk_manhack_melee_dmg( "sk_manhack_melee_dmg","0");
  83. ConVar  sk_manhack_v2( "sk_manhack_v2","1");
  84.  
  85. extern void     SpawnBlood(Vector vecSpot, const Vector &vAttackDir, int bloodColor, float flDamage);
  86. extern float    GetFloorZ(const Vector &origin);
  87.  
  88. //-----------------------------------------------------------------------------
  89. // Private activities.
  90. //-----------------------------------------------------------------------------
  91. Activity ACT_MANHACK_UNPACK;
  92.  
  93. //-----------------------------------------------------------------------------
  94. // Manhack Conditions
  95. //-----------------------------------------------------------------------------
  96. enum ManhackConditions
  97. {
  98.     COND_MANHACK_START_ATTACK = LAST_SHARED_CONDITION,  // We are able to do a bombing run on the current enemy.
  99. };
  100.  
  101. //-----------------------------------------------------------------------------
  102. // Manhack schedules.
  103. //-----------------------------------------------------------------------------
  104. enum ManhackSchedules
  105. {
  106.     SCHED_MANHACK_ATTACK_HOVER = LAST_SHARED_SCHEDULE,
  107.     SCHED_MANHACK_DEPLOY,
  108.     SCHED_MANHACK_REGROUP,
  109.     SCHED_MANHACK_SWARM_IDLE,
  110.     SCHED_MANHACK_SWARM,
  111.     SCHED_MANHACK_SWARM_FAILURE,
  112. };
  113.  
  114.  
  115. //-----------------------------------------------------------------------------
  116. // Manhack tasks.
  117. //-----------------------------------------------------------------------------
  118. enum ManhackTasks
  119. {
  120.     TASK_MANHACK_HOVER = LAST_SHARED_TASK,
  121.     TASK_MANHACK_UNPACK,
  122.     TASK_MANHACK_FIND_SQUAD_CENTER,
  123.     TASK_MANHACK_FIND_SQUAD_MEMBER,
  124.     TASK_MANHACK_MOVEAT_SAVEPOSITION,
  125. };
  126.  
  127. BEGIN_DATADESC( CNPC_Manhack )
  128.  
  129.     DEFINE_FIELD( m_vForceVelocity,         FIELD_VECTOR),
  130.  
  131.     DEFINE_FIELD( m_vTargetBanking,         FIELD_VECTOR),
  132.     DEFINE_FIELD( m_vForceMoveTarget,           FIELD_POSITION_VECTOR),
  133.     DEFINE_FIELD( m_fForceMoveTime,         FIELD_TIME),
  134.     DEFINE_FIELD( m_vSwarmMoveTarget,           FIELD_POSITION_VECTOR),
  135.     DEFINE_FIELD( m_fSwarmMoveTime,         FIELD_TIME),
  136.     DEFINE_FIELD( m_fEnginePowerScale,      FIELD_FLOAT),
  137.  
  138.     DEFINE_FIELD( m_flNextEngineSoundTime,  FIELD_TIME),
  139.     DEFINE_FIELD( m_flEngineStallTime,      FIELD_TIME),
  140.     DEFINE_FIELD( m_flNextBurstTime,            FIELD_TIME ),
  141.     DEFINE_FIELD( m_flWaterSuspendTime,     FIELD_TIME),
  142.     DEFINE_FIELD( m_nLastSpinSound,         FIELD_INTEGER ),
  143.  
  144.     // Death
  145.     DEFINE_FIELD( m_fSparkTime,             FIELD_TIME),
  146.     DEFINE_FIELD( m_fSmokeTime,             FIELD_TIME),
  147.  
  148.     DEFINE_FIELD( m_bDirtyPitch,            FIELD_BOOLEAN ),
  149.     DEFINE_FIELD( m_bGib,                   FIELD_BOOLEAN),
  150.     DEFINE_FIELD( m_bHeld,                  FIELD_BOOLEAN),
  151.    
  152.     DEFINE_FIELD( m_bHackedByAlyx,          FIELD_BOOLEAN),
  153.     DEFINE_FIELD( m_vecLoiterPosition,      FIELD_POSITION_VECTOR),
  154.     DEFINE_FIELD( m_fTimeNextLoiterPulse,   FIELD_TIME),
  155.  
  156.     DEFINE_FIELD( m_flBumpSuppressTime,     FIELD_TIME ),
  157.  
  158.     DEFINE_FIELD( m_bBladesActive,          FIELD_BOOLEAN),
  159.     DEFINE_FIELD( m_flBladeSpeed,               FIELD_FLOAT),
  160.     DEFINE_KEYFIELD( m_bIgnoreClipbrushes,  FIELD_BOOLEAN, "ignoreclipbrushes" ),
  161.     DEFINE_FIELD( m_hSmokeTrail,                FIELD_EHANDLE),
  162.  
  163.     // DEFINE_FIELD( m_pLightGlow,              FIELD_CLASSPTR ),
  164.     // DEFINE_FIELD( m_pEyeGlow,                    FIELD_CLASSPTR ),
  165.  
  166.     DEFINE_FIELD( m_iPanel1, FIELD_INTEGER ),
  167.     DEFINE_FIELD( m_iPanel2, FIELD_INTEGER ),
  168.     DEFINE_FIELD( m_iPanel3, FIELD_INTEGER ),
  169.     DEFINE_FIELD( m_iPanel4, FIELD_INTEGER ),
  170.  
  171.     DEFINE_FIELD( m_nLastWaterLevel,            FIELD_INTEGER ),
  172.     DEFINE_FIELD( m_bDoSwarmBehavior,           FIELD_BOOLEAN ),
  173.  
  174.     DEFINE_FIELD( m_nEnginePitch1,              FIELD_INTEGER ),
  175.     DEFINE_FIELD( m_flEnginePitch1Time,         FIELD_TIME ),
  176.     DEFINE_FIELD( m_nEnginePitch2,              FIELD_INTEGER ),
  177.     DEFINE_FIELD( m_flEnginePitch2Time,         FIELD_TIME ),
  178.  
  179.     // Physics Influence
  180.     DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  181.     DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  182.  
  183.     DEFINE_FIELD( m_flBurstDuration,    FIELD_FLOAT ),
  184.     DEFINE_FIELD( m_vecBurstDirection,  FIELD_VECTOR ),
  185.     DEFINE_FIELD( m_bShowingHostile,    FIELD_BOOLEAN ),
  186.  
  187.     // Function Pointers
  188.     DEFINE_INPUTFUNC( FIELD_VOID,   "DisableSwarm", InputDisableSwarm ),
  189.     DEFINE_INPUTFUNC( FIELD_VOID,   "Unpack",       InputUnpack ),
  190.  
  191.     DEFINE_ENTITYFUNC( CrashTouch ),
  192.  
  193.     DEFINE_BASENPCINTERACTABLE_DATADESC(),
  194.  
  195. END_DATADESC()
  196.  
  197.  
  198. LINK_ENTITY_TO_CLASS( npc_manhack, CNPC_Manhack );
  199.  
  200. IMPLEMENT_SERVERCLASS_ST(CNPC_Manhack, DT_NPC_Manhack)
  201.     SendPropIntWithMinusOneFlag (SENDINFO(m_nEnginePitch1), 8 ),
  202.     SendPropFloat(SENDINFO(m_flEnginePitch1Time), 0, SPROP_NOSCALE),
  203.     SendPropIntWithMinusOneFlag(SENDINFO(m_nEnginePitch2), 8 )
  204. END_SEND_TABLE()
  205.  
  206.  
  207.  
  208. //------------------------------------------------------------------------------
  209. // Purpose :
  210. // Input   :
  211. // Output  :
  212. //------------------------------------------------------------------------------
  213. CNPC_Manhack::CNPC_Manhack()
  214. {
  215. #ifdef _DEBUG
  216.     m_vForceMoveTarget.Init();
  217.     m_vSwarmMoveTarget.Init();
  218.     m_vTargetBanking.Init();
  219.     m_vForceVelocity.Init();
  220. #endif
  221.     m_bDirtyPitch = true;
  222.     m_nLastWaterLevel = 0;
  223.     m_nEnginePitch1 = -1;
  224.     m_nEnginePitch2 = -1;
  225.     m_flEnginePitch1Time = 0;
  226.     m_flEnginePitch1Time = 0;
  227.     m_bDoSwarmBehavior = true;
  228.     m_flBumpSuppressTime = 0;
  229. }
  230.  
  231. //------------------------------------------------------------------------------
  232. // Purpose:
  233. //------------------------------------------------------------------------------
  234. CNPC_Manhack::~CNPC_Manhack()
  235. {
  236. }
  237.  
  238. //-----------------------------------------------------------------------------
  239. // Purpose: Indicates this NPC's place in the relationship table.
  240. //-----------------------------------------------------------------------------
  241. Class_T CNPC_Manhack::Classify(void)
  242. {
  243.     return (m_bHeld||m_bHackedByAlyx) ? CLASS_PLAYER_ALLY : CLASS_MANHACK;
  244. }
  245.  
  246.  
  247.  
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Turns the manhack into a physics corpse when dying.
  250. //-----------------------------------------------------------------------------
  251. void CNPC_Manhack::Event_Dying(void)
  252. {
  253.     DestroySmokeTrail();
  254.     SetHullSizeNormal();
  255.     BaseClass::Event_Dying();
  256. }
  257.  
  258.  
  259. //-----------------------------------------------------------------------------
  260. //-----------------------------------------------------------------------------
  261. void CNPC_Manhack::GatherConditions()
  262. {
  263.     BaseClass::GatherConditions();
  264.  
  265.     if( IsLoitering() && GetEnemy() )
  266.     {
  267.         StopLoitering();
  268.     }
  269. }
  270.  
  271. //-----------------------------------------------------------------------------
  272. // Purpose:
  273. // Input  :
  274. // Output :
  275. //-----------------------------------------------------------------------------
  276. void CNPC_Manhack::PrescheduleThink( void )
  277. {
  278.     BaseClass::PrescheduleThink();
  279.  
  280.     UpdatePanels();
  281.  
  282.     if( m_flWaterSuspendTime > gpGlobals->curtime )
  283.     {
  284.         // Stuck in water!
  285.  
  286.         // Reduce engine power so that the manhack lifts out of the water slowly.
  287.         m_fEnginePowerScale = 0.75;
  288.     }
  289.  
  290.     // ----------------------------------------
  291.     //  Am I in water?
  292.     // ----------------------------------------
  293.     if ( GetWaterLevel() > 0 )
  294.     {
  295.         if( m_nLastWaterLevel == 0 )
  296.         {
  297.             Splash( WorldSpaceCenter() );
  298.         }
  299.  
  300.         if( IsAlive() )
  301.         {
  302.             // If I've been out of water for 2 seconds or more, I'm eligible to be stuck in water again.
  303.             if( gpGlobals->curtime - m_flWaterSuspendTime > 2.0 )
  304.             {
  305.                 m_flWaterSuspendTime = gpGlobals->curtime + 1.0;
  306.             }
  307.         }
  308.     }
  309.     else
  310.     {
  311.         if( m_nLastWaterLevel != 0 )
  312.         {
  313.             Splash( WorldSpaceCenter() );
  314.         }
  315.     }
  316.  
  317.     m_nLastWaterLevel = GetWaterLevel();
  318. }
  319.  
  320.  
  321. //-----------------------------------------------------------------------------
  322. // Purpose:
  323. // Input  :
  324. // Output :
  325. //-----------------------------------------------------------------------------
  326. void CNPC_Manhack::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  327. {
  328.     g_vecAttackDir = vecDir;
  329.  
  330.     if ( info.GetDamageType() & DMG_BULLET)
  331.     {
  332.         g_pEffects->Ricochet(ptr->endpos,ptr->plane.normal);
  333.     }
  334.  
  335.     if ( info.GetDamageType() & DMG_CLUB )
  336.     {
  337.         // Clubbed!
  338. //      UTIL_Smoke(GetAbsOrigin(), random->RandomInt(10, 15), 10);
  339.         g_pEffects->Sparks( ptr->endpos, 1, 1, &ptr->plane.normal );
  340.     }
  341.  
  342.     BaseClass::TraceAttack( info, vecDir, ptr );
  343. }
  344.  
  345. //-----------------------------------------------------------------------------
  346. // Purpose:
  347. //-----------------------------------------------------------------------------
  348. void CNPC_Manhack::DeathSound( const CTakeDamageInfo &info )
  349. {
  350.     StopSound("NPC_Manhack.Stunned");
  351.     CPASAttenuationFilter filter2( this, "NPC_Manhack.Die" );
  352.     EmitSound( filter2, entindex(), "NPC_Manhack.Die" );
  353. }
  354.  
  355. //-----------------------------------------------------------------------------
  356. // Purpose:
  357. // Output : Returns true on success, false on failure.
  358. //-----------------------------------------------------------------------------
  359. bool CNPC_Manhack::ShouldGib( const CTakeDamageInfo &info )
  360. {
  361.     return ( m_bGib );
  362. }
  363.  
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. // Input  :
  367. // Output :
  368. //-----------------------------------------------------------------------------
  369. void CNPC_Manhack::Event_Killed( const CTakeDamageInfo &info )
  370. {
  371.     // turn off the blur!
  372.     SetBodygroup( MANHACK_BODYGROUP_BLUR, MANHACK_BODYGROUP_OFF );
  373.  
  374.     // Sparks
  375.     for (int i = 0; i < 3; i++)
  376.     {
  377.         Vector sparkPos = GetAbsOrigin();
  378.         sparkPos.x += random->RandomFloat(-12,12);
  379.         sparkPos.y += random->RandomFloat(-12,12);
  380.         sparkPos.z += random->RandomFloat(-12,12);
  381.         g_pEffects->Sparks( sparkPos, 2 );
  382.     }
  383.  
  384.     // Light
  385.     CBroadcastRecipientFilter filter;
  386.     te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 255, 180, 100, 0, 100, 0.1, 0 );
  387.  
  388.     if ( m_nEnginePitch1 < 0 )
  389.     {
  390.         // Probably this manhack was killed immediately after spawning. Turn the sound
  391.         // on right now so that we can pitch it up for the crash!
  392.         SoundInit();
  393.     }
  394.  
  395.     // Always gib when clubbed or blasted or crushed, or just randomly
  396.     if ( ( info.GetDamageType() & (DMG_CLUB|DMG_CRUSH|DMG_BLAST) ) || ( random->RandomInt( 0, 1 ) ) )
  397.     {
  398.         m_bGib = true;
  399.     }
  400.     else
  401.     {
  402.         m_bGib = false;
  403.        
  404.         //FIXME: These don't stay with the ragdolls currently -- jdw
  405.         // Long fadeout on the sprites!!
  406.         KillSprites( 0.0f );
  407.     }
  408.  
  409.     BaseClass::Event_Killed( info );
  410. }
  411.  
  412. void CNPC_Manhack::HitPhysicsObject( CBaseEntity *pOther )
  413. {
  414.     IPhysicsObject *pOtherPhysics = pOther->VPhysicsGetObject();
  415.     Vector pos, posOther;
  416.     // Put the force on the line between the manhack origin and hit object origin
  417.     VPhysicsGetObject()->GetPosition( &pos, NULL );
  418.     pOtherPhysics->GetPosition( &posOther, NULL );
  419.     Vector dir = posOther - pos;
  420.     VectorNormalize(dir);
  421.     // size/2 is approx radius
  422.     pos += dir * WorldAlignSize().x * 0.5;
  423.     Vector cross;
  424.  
  425.     // UNDONE: Use actual manhack up vector so the fake blade is
  426.     // in the right plane?
  427.     // Get a vector in the x/y plane in the direction of blade spin (clockwise)
  428.     CrossProduct( dir, Vector(0,0,1), cross );
  429.     VectorNormalize( cross );
  430.     // force is a 30kg object going 100 in/s
  431.     pOtherPhysics->ApplyForceOffset( cross * 30 * 100, pos );
  432. }
  433.  
  434.  
  435. //-----------------------------------------------------------------------------
  436. // Take damage from being thrown by a physcannon
  437. //-----------------------------------------------------------------------------
  438. #define MANHACK_SMASH_SPEED 500.0   // How fast a manhack must slam into something to take full damage
  439. void CNPC_Manhack::TakeDamageFromPhyscannon( CBasePlayer *pPlayer )
  440. {
  441.     CTakeDamageInfo info;
  442.     info.SetDamageType( DMG_GENERIC );
  443.     info.SetInflictor( this );
  444.     info.SetAttacker( pPlayer );
  445.     info.SetDamagePosition( GetAbsOrigin() );
  446.     info.SetDamageForce( Vector( 1.0, 1.0, 1.0 ) );
  447.  
  448.     // Convert velocity into damage.
  449.     Vector vel;
  450.     VPhysicsGetObject()->GetVelocity( &vel, NULL );
  451.     float flSpeed = vel.Length();
  452.  
  453.     float flFactor = flSpeed / MANHACK_SMASH_SPEED;
  454.  
  455.     // Clamp. Don't inflict negative damage or massive damage!
  456.     flFactor = clamp( flFactor, 0.0f, 2.0f );
  457.     float flDamage = m_iMaxHealth * flFactor;
  458.  
  459. #if 0
  460.     Msg("Doing %f damage for %f speed!\n", flDamage, flSpeed );
  461. #endif
  462.  
  463.     info.SetDamage( flDamage );
  464.     TakeDamage( info );
  465. }
  466.  
  467.  
  468. //-----------------------------------------------------------------------------
  469. // Take damage from a vehicle; it's like a really big crowbar
  470. //-----------------------------------------------------------------------------
  471. void CNPC_Manhack::TakeDamageFromVehicle( int index, gamevcollisionevent_t *pEvent )
  472. {
  473.     // Use the vehicle velocity to determine the damage
  474.     int otherIndex = !index;
  475.     CBaseEntity *pOther = pEvent->pEntities[otherIndex];
  476.  
  477.     float flSpeed = pEvent->preVelocity[ otherIndex ].Length();
  478.     flSpeed = clamp( flSpeed, 300.0f, 600.0f );
  479.     float flDamage = SimpleSplineRemapVal( flSpeed, 300.0f, 600.0f, 0.0f, 1.0f );
  480.     if ( flDamage == 0.0f )
  481.         return;
  482.  
  483.     flDamage *= 20.0f;
  484.  
  485.     Vector damagePos;
  486.     pEvent->pInternalData->GetContactPoint( damagePos );
  487.  
  488.     Vector damageForce = 2.0f * pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  489.     if ( damageForce == vec3_origin )
  490.     {
  491.         // This can happen if this entity is a func_breakable, and can't move.
  492.         // Use the velocity of the entity that hit us instead.
  493.         damageForce = 2.0f * pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
  494.     }
  495.     Assert( damageForce != vec3_origin );
  496.     CTakeDamageInfo dmgInfo( pOther, pOther, damageForce, damagePos, flDamage, DMG_CRUSH );
  497.     TakeDamage( dmgInfo );
  498. }
  499.  
  500.  
  501. //-----------------------------------------------------------------------------
  502. // Take damage from combine ball
  503. //-----------------------------------------------------------------------------
  504. void CNPC_Manhack::TakeDamageFromPhysicsImpact( int index, gamevcollisionevent_t *pEvent )
  505. {
  506.     CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  507.  
  508.     // NOTE: Bypass the normal impact energy scale here.
  509.     float flDamageScale = PlayerHasMegaPhysCannon() ? 10.0f : 1.0f;
  510.     int damageType = 0;
  511.     float damage = CalculateDefaultPhysicsDamage( index, pEvent, flDamageScale, true, damageType );
  512.     if ( damage == 0 )
  513.         return;
  514.  
  515.     Vector damagePos;
  516.     pEvent->pInternalData->GetContactPoint( damagePos );
  517.     Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  518.     if ( damageForce == vec3_origin )
  519.     {
  520.         // This can happen if this entity is motion disabled, and can't move.
  521.         // Use the velocity of the entity that hit us instead.
  522.         damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
  523.     }
  524.  
  525.     // FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
  526.     PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
  527. }
  528.  
  529.  
  530. //-----------------------------------------------------------------------------
  531. // Purpose:
  532. //-----------------------------------------------------------------------------
  533. #define MANHACK_SMASH_TIME  0.35        // How long after being thrown from a physcannon that a manhack is eligible to die from impact
  534. void CNPC_Manhack::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  535. {
  536.     BaseClass::VPhysicsCollision( index, pEvent );
  537.  
  538.     // Take no impact damage while being carried.
  539.     if ( IsHeldByPhyscannon() )
  540.         return;
  541.  
  542.     // Wake us up
  543.     if ( m_spawnflags & SF_MANHACK_PACKED_UP )
  544.     {
  545.         SetCondition( COND_LIGHT_DAMAGE );
  546.     }
  547.  
  548.     int otherIndex = !index;
  549.     CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
  550.  
  551.     CBasePlayer *pPlayer = HasPhysicsAttacker( MANHACK_SMASH_TIME );
  552.     if( pPlayer )
  553.     {
  554.         if (!pHitEntity)
  555.         {
  556.             TakeDamageFromPhyscannon( pPlayer );
  557.             StopBurst( true );
  558.             return;
  559.         }
  560.  
  561.         // Don't take damage from NPCs or server ragdolls killed by the manhack
  562.         CRagdollProp *pRagdollProp = dynamic_cast<CRagdollProp*>(pHitEntity);
  563.         if (!pHitEntity->IsNPC() && (!pRagdollProp || pRagdollProp->GetKiller() != this))
  564.         {
  565.             TakeDamageFromPhyscannon( pPlayer );
  566.             StopBurst( true );
  567.             return;
  568.         }
  569.     }
  570.  
  571.     if ( pHitEntity )
  572.     {
  573.         // It can take physics damage if it rams into a vehicle
  574.         if ( pHitEntity->GetServerVehicle() )
  575.         {
  576.             TakeDamageFromVehicle( index, pEvent );
  577.         }
  578.         else if ( pHitEntity->HasPhysicsAttacker( 0.5f ) )
  579.         {
  580.             // It also can take physics damage from things thrown by the player.
  581.             TakeDamageFromPhysicsImpact( index, pEvent );
  582.         }
  583.         else if ( FClassnameIs( pHitEntity, "prop_combine_ball" ) )
  584.         {
  585.             // It also can take physics damage from a combine ball.
  586.             TakeDamageFromPhysicsImpact( index, pEvent );
  587.         }
  588.         else if ( m_iHealth <= 0 )
  589.         {
  590.             TakeDamageFromPhysicsImpact( index, pEvent );
  591.         }
  592.  
  593.         StopBurst( true );
  594.     }
  595. }
  596.  
  597.  
  598. void CNPC_Manhack::VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent )
  599. {
  600.     int otherIndex = !index;
  601.     CBaseEntity *pOther = pEvent->pEntities[otherIndex];
  602.  
  603.     if ( pOther->GetMoveType() == MOVETYPE_VPHYSICS )
  604.     {
  605.         HitPhysicsObject( pOther );
  606.     }
  607.     BaseClass::VPhysicsShadowCollision( index, pEvent );
  608. }
  609.  
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Manhack is out of control! (dying) Just explode as soon as you touch anything!
  612. // Input  :
  613. // Output :
  614. //-----------------------------------------------------------------------------
  615. void CNPC_Manhack::CrashTouch( CBaseEntity *pOther )
  616. {
  617.     CTakeDamageInfo info( GetWorldEntity(), GetWorldEntity(), 25, DMG_CRUSH );
  618.  
  619.     CorpseGib( info );
  620. }
  621.  
  622.  
  623. //-----------------------------------------------------------------------------
  624. // Create smoke trail!
  625. //-----------------------------------------------------------------------------
  626. void CNPC_Manhack::CreateSmokeTrail()
  627. {
  628.     if ( HasSpawnFlags( SF_MANHACK_NO_DAMAGE_EFFECTS ) )
  629.         return;
  630.  
  631.     if ( m_hSmokeTrail != NULL )
  632.         return;
  633.  
  634.     SmokeTrail *pSmokeTrail =  SmokeTrail::CreateSmokeTrail();
  635.     if( !pSmokeTrail )
  636.         return;
  637.  
  638.     pSmokeTrail->m_SpawnRate = 20;
  639.     pSmokeTrail->m_ParticleLifetime = 0.5f;
  640.     pSmokeTrail->m_StartSize    = 8;
  641.     pSmokeTrail->m_EndSize      = 32;
  642.     pSmokeTrail->m_SpawnRadius  = 5;
  643.     pSmokeTrail->m_MinSpeed     = 15;
  644.     pSmokeTrail->m_MaxSpeed     = 25;
  645.    
  646.     pSmokeTrail->m_StartColor.Init( 0.4f, 0.4f, 0.4f );
  647.     pSmokeTrail->m_EndColor.Init( 0, 0, 0 );
  648.    
  649.     pSmokeTrail->SetLifetime(-1);
  650.     pSmokeTrail->FollowEntity(this);
  651.  
  652.     m_hSmokeTrail = pSmokeTrail;
  653. }
  654.  
  655. void CNPC_Manhack::DestroySmokeTrail()
  656. {
  657.     if ( m_hSmokeTrail.Get() )
  658.     {
  659.         UTIL_Remove( m_hSmokeTrail );
  660.         m_hSmokeTrail = NULL;
  661.     }
  662. }
  663.  
  664. //-----------------------------------------------------------------------------
  665. // Purpose:
  666. // Input  :
  667. // Output :
  668. //-----------------------------------------------------------------------------
  669. int CNPC_Manhack::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  670. {
  671.     // Hafta make a copy of info cause we might need to scale damage.(sjb)
  672.     CTakeDamageInfo tdInfo = info;
  673.  
  674.     if( tdInfo.GetAmmoType() == GetAmmoDef()->Index("SniperRound") )
  675.     {
  676.         // Unfortunately, this is the easiest way to stop the sniper killing manhacks in one shot.
  677.         tdInfo.SetDamage( m_iMaxHealth>>1 );
  678.     }
  679.  
  680.     if (info.GetDamageType() & DMG_PHYSGUN )
  681.     {
  682.         m_flBladeSpeed = 20.0;
  683.  
  684.         // respond to physics
  685.         // FIXME: shouldn't this happen in a base class?  Anyway to prevent it from happening twice?
  686.         VPhysicsTakeDamage( info );
  687.  
  688.         // reduce damage to nothing
  689.         tdInfo.SetDamage( 1.0 );
  690.  
  691.         StopBurst( true );
  692.     }
  693.     else if ( info.GetDamageType() & DMG_AIRBOAT )
  694.     {
  695.         // Airboat gun kills me instantly.
  696.         tdInfo.SetDamage( GetHealth() );
  697.     }
  698.     else if (info.GetDamageType() & DMG_CLUB)
  699.     {
  700.         // Being hit by a club means a couple of things:
  701.         //
  702.         //      -I'm going to be knocked away from the person that clubbed me.
  703.         //       if fudging this vector a little bit could help me slam into a physics object,
  704.         //       then make that adjustment. This is a simple heuristic. The manhack will be
  705.         //       directed towards the physics object that is closest to g_vecAttackDir
  706.         //
  707.         //      -Take 150% damage from club attacks. This makes crowbar duels take two hits.
  708.         //      tdInfo.ScaleDamage( 1.50 );
  709.  
  710. #define MANHACK_PHYS_SEARCH_SIZE        64
  711. #define MANHACK_PHYSICS_SEARCH_RADIUS   128
  712.  
  713.         CBaseEntity *pList[ MANHACK_PHYS_SEARCH_SIZE ];
  714.  
  715.         Vector attackDir = info.GetDamageForce();
  716.         VectorNormalize( attackDir );
  717.  
  718.         Vector testCenter = GetAbsOrigin() + ( attackDir * MANHACK_PHYSICS_SEARCH_RADIUS );
  719.         Vector vecDelta( MANHACK_PHYSICS_SEARCH_RADIUS, MANHACK_PHYSICS_SEARCH_RADIUS, MANHACK_PHYSICS_SEARCH_RADIUS );
  720.  
  721.         int count = UTIL_EntitiesInBox( pList, MANHACK_PHYS_SEARCH_SIZE, testCenter - vecDelta, testCenter + vecDelta, 0 );
  722.  
  723.         Vector          vecBestDir = g_vecAttackDir;
  724.         float           flBestDot = 0.90;
  725.         IPhysicsObject  *pPhysObj;
  726.  
  727.         int i;
  728.         for( i = 0 ; i < count ; i++ )
  729.         {
  730.             pPhysObj = pList[ i ]->VPhysicsGetObject();
  731.  
  732.             if( !pPhysObj || pPhysObj->GetMass() > 200 )
  733.             {
  734.                 // Not physics.
  735.                 continue;
  736.             }
  737.  
  738.             Vector center = pList[ i ]->WorldSpaceCenter();
  739.  
  740.             Vector vecDirToObject;
  741.             VectorSubtract( center, WorldSpaceCenter(), vecDirToObject );
  742.             VectorNormalize( vecDirToObject );
  743.  
  744.             float flDot;
  745.  
  746.             flDot = DotProduct( g_vecAttackDir, vecDirToObject );
  747.            
  748.  
  749.             if( flDot > flBestDot )
  750.             {
  751.                 flBestDot = flDot;
  752.                 vecBestDir = vecDirToObject;
  753.             }
  754.         }
  755.  
  756.         tdInfo.SetDamageForce( vecBestDir * info.GetDamage() * 200 );
  757.  
  758.         // FIXME: shouldn't this happen in a base class?  Anyway to prevent it from happening twice?
  759.         VPhysicsTakeDamage( tdInfo );
  760.  
  761.         // Force us away (no more residual speed hits!)
  762.         m_vForceVelocity = vecBestDir * info.GetDamage() * 0.5f;
  763.         m_flBladeSpeed = 10.0;
  764.  
  765.         EmitSound( "NPC_Manhack.Bat" );
  766.  
  767.         // tdInfo.SetDamage( 1.0 );
  768.  
  769.         m_flEngineStallTime = gpGlobals->curtime + 0.5f;
  770.         StopBurst( true );
  771.     }
  772.     else
  773.     {
  774.         m_flBladeSpeed = 20.0;
  775.  
  776.         Vector vecDamageDir = tdInfo.GetDamageForce();
  777.         VectorNormalize( vecDamageDir );
  778.  
  779.         m_flEngineStallTime = gpGlobals->curtime + 0.25f;
  780.         m_vForceVelocity = vecDamageDir * info.GetDamage() * 20.0f;
  781.  
  782.         tdInfo.SetDamageForce( tdInfo.GetDamageForce() * 20 );
  783.  
  784.         VPhysicsTakeDamage( info );
  785.     }
  786.  
  787.     int nRetVal = BaseClass::OnTakeDamage_Alive( tdInfo );
  788.     if ( nRetVal )
  789.     {
  790.         if ( m_iHealth > 0 )
  791.         {
  792.             if ( info.GetDamageType() & DMG_CLUB )
  793.             {
  794.                 SetEyeState( MANHACK_EYE_STATE_STUNNED );
  795.             }
  796.  
  797.             if ( m_iHealth <= ( m_iMaxHealth / 2 ) )
  798.             {
  799.                 CreateSmokeTrail();
  800.             }
  801.         }
  802.         else
  803.         {
  804.             DestroySmokeTrail();
  805.         }
  806.     }
  807.  
  808.     return nRetVal;
  809. }
  810.  
  811.  
  812. //------------------------------------------------------------------------------
  813. // Purpose:
  814. //------------------------------------------------------------------------------
  815. bool CNPC_Manhack::CorpseGib( const CTakeDamageInfo &info )
  816. {
  817.     Vector          vecGibVelocity;
  818.     AngularImpulse  vecGibAVelocity;
  819.  
  820.     if( info.GetDamageType() & DMG_CLUB )
  821.     {
  822.         // If clubbed to death, break apart before the attacker's eyes!
  823.         vecGibVelocity = g_vecAttackDir * -150;
  824.  
  825.         vecGibAVelocity.x = random->RandomFloat( -2000, 2000 );
  826.         vecGibAVelocity.y = random->RandomFloat( -2000, 2000 );
  827.         vecGibAVelocity.z = random->RandomFloat( -2000, 2000 );
  828.     }
  829.     else
  830.     {
  831.         // Shower the pieces with my velocity.
  832.         vecGibVelocity = GetCurrentVelocity();
  833.  
  834.         vecGibAVelocity.x = random->RandomFloat( -500, 500 );
  835.         vecGibAVelocity.y = random->RandomFloat( -500, 500 );
  836.         vecGibAVelocity.z = random->RandomFloat( -500, 500 );
  837.     }
  838.  
  839.     PropBreakableCreateAll( GetModelIndex(), NULL, GetAbsOrigin(), GetAbsAngles(), vecGibVelocity, vecGibAVelocity, 1.0, 60, COLLISION_GROUP_DEBRIS );
  840.  
  841.     RemoveDeferred();
  842.  
  843.     KillSprites( 0.0f );
  844.  
  845.     return true;
  846. }
  847.  
  848.  
  849. //-----------------------------------------------------------------------------
  850. // Purpose: Explode the manhack if it's damaged while crashing
  851. // Input  :
  852. // Output :
  853. //-----------------------------------------------------------------------------
  854. int CNPC_Manhack::OnTakeDamage_Dying( const CTakeDamageInfo &info )
  855. {
  856.     // Ignore damage for the first 1 second of crashing behavior.
  857.     // If we don't do this, manhacks always just explode under
  858.     // sustained fire.
  859.     VPhysicsTakeDamage( info );
  860.    
  861.     return 0;
  862. }
  863.  
  864. //-----------------------------------------------------------------------------
  865. // Turn on the engine sound if we're gagged!
  866. //-----------------------------------------------------------------------------
  867. void CNPC_Manhack::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  868. {
  869.     if( m_vNoiseMod.z == MANHACK_NOISEMOD_HIDE && !(m_spawnflags & SF_NPC_WAIT_FOR_SCRIPT) && !(m_spawnflags & SF_MANHACK_PACKED_UP) )
  870.     {
  871.         // This manhack should get a normal noisemod now.
  872.         float flNoiseMod = random->RandomFloat( 1.7, 2.3 );
  873.        
  874.         // Just bob up and down.
  875.         SetNoiseMod( 0, 0, flNoiseMod );
  876.     }
  877.  
  878.     if( NewState != NPC_STATE_IDLE && (m_spawnflags & SF_NPC_GAG) && (m_nEnginePitch1 < 0) )
  879.     {
  880.         m_spawnflags &= ~SF_NPC_GAG;
  881.         SoundInit();
  882.     }
  883. }
  884.  
  885. //-----------------------------------------------------------------------------
  886. // Purpose:
  887. // Input  : Type -
  888. //-----------------------------------------------------------------------------
  889. void CNPC_Manhack::HandleAnimEvent( animevent_t *pEvent )
  890. {
  891.     Vector vecNewVelocity;
  892.     switch( pEvent->event )
  893.     {
  894.     case MANHACK_AE_START_ENGINE:
  895.         StartEye();
  896.         StartEngine( true );
  897.         m_spawnflags &= ~SF_MANHACK_PACKED_UP;
  898.  
  899.         // No bursts until fully unpacked!
  900.         m_flNextBurstTime = gpGlobals->curtime + FLT_MAX;
  901.         break;
  902.  
  903.     case MANHACK_AE_DONE_UNPACKING:
  904.         m_flNextBurstTime = gpGlobals->curtime + 2.0;
  905.         break;
  906.  
  907.     case MANHACK_AE_OPEN_BLADE:
  908.         m_bBladesActive = true;
  909.         break;
  910.  
  911.     default:
  912.         BaseClass::HandleAnimEvent( pEvent );
  913.         break;
  914.     }
  915. }
  916.  
  917.  
  918. //-----------------------------------------------------------------------------
  919. // Purpose: Returns whether or not the given activity would translate to flying.
  920. //-----------------------------------------------------------------------------
  921. bool CNPC_Manhack::IsFlyingActivity( Activity baseAct )
  922. {
  923.     return ((baseAct == ACT_FLY || baseAct == ACT_IDLE || baseAct == ACT_RUN || baseAct == ACT_WALK) && m_bBladesActive);
  924. }
  925.  
  926.  
  927. //-----------------------------------------------------------------------------
  928. // Purpose:
  929. // Input  : Type -
  930. //-----------------------------------------------------------------------------
  931. Activity CNPC_Manhack::NPC_TranslateActivity( Activity baseAct )
  932. {
  933.     if (IsFlyingActivity( baseAct ))
  934.     {
  935.         return (Activity)ACT_FLY;
  936.     }
  937.  
  938.     return BaseClass::NPC_TranslateActivity( baseAct );
  939. }
  940.  
  941. //-----------------------------------------------------------------------------
  942. // Purpose:
  943. // Input  : Type -
  944. //-----------------------------------------------------------------------------
  945. int CNPC_Manhack::TranslateSchedule( int scheduleType )
  946. {
  947.     // Fail-safe for deployment if packed up and interrupted
  948.     if ( m_spawnflags & SF_MANHACK_PACKED_UP )
  949.     {
  950.         if ( scheduleType != SCHED_WAIT_FOR_SCRIPT )
  951.             return SCHED_MANHACK_DEPLOY;
  952.     }
  953.  
  954.     switch ( scheduleType )
  955.     {
  956.     case SCHED_MELEE_ATTACK1:
  957.         {
  958.             return SCHED_MANHACK_ATTACK_HOVER;
  959.             break;
  960.         }
  961.     case SCHED_BACK_AWAY_FROM_ENEMY:
  962.         {
  963.             return SCHED_MANHACK_REGROUP;
  964.             break;
  965.         }
  966.     case SCHED_CHASE_ENEMY:
  967.         {
  968.             // If we're waiting for our next attack opportunity, just swarm
  969.             if ( m_flNextBurstTime > gpGlobals->curtime )
  970.             {
  971.                 return SCHED_MANHACK_SWARM;
  972.             }
  973.  
  974.             if ( !m_bDoSwarmBehavior || OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  975.             {
  976.                 return SCHED_CHASE_ENEMY;
  977.             }
  978.             else
  979.             {
  980.                 return SCHED_MANHACK_SWARM;
  981.             }
  982.         }
  983.     case SCHED_COMBAT_FACE:
  984.         {
  985.             // Don't care about facing enemy, handled automatically
  986.             return TranslateSchedule( SCHED_CHASE_ENEMY );
  987.             break;
  988.         }
  989.     case SCHED_WAKE_ANGRY:
  990.         {
  991.             if( m_spawnflags & SF_MANHACK_PACKED_UP )
  992.             {
  993.                 return SCHED_MANHACK_DEPLOY;
  994.             }
  995.             else
  996.             {
  997.                 return TranslateSchedule( SCHED_CHASE_ENEMY );
  998.             }
  999.             break;
  1000.         }
  1001.  
  1002.     case SCHED_IDLE_STAND:
  1003.     case SCHED_ALERT_STAND:
  1004.     case SCHED_ALERT_FACE:
  1005.         {
  1006.             if ( m_pSquad && m_bDoSwarmBehavior )
  1007.             {
  1008.                 return SCHED_MANHACK_SWARM_IDLE;
  1009.             }
  1010.             else
  1011.             {
  1012.                 return BaseClass::TranslateSchedule(scheduleType);
  1013.             }
  1014.         }
  1015.  
  1016.     case SCHED_CHASE_ENEMY_FAILED:
  1017.         {
  1018.             // Relentless bastard! Doesn't fail (fail not valid anyway)
  1019.             return TranslateSchedule( SCHED_CHASE_ENEMY );
  1020.             break;
  1021.         }
  1022.  
  1023.     }
  1024.     return BaseClass::TranslateSchedule(scheduleType);
  1025. }
  1026.  
  1027. #define MAX_LOITER_DIST_SQR 144 // (12 inches sqr)
  1028. void CNPC_Manhack::Loiter()
  1029. {
  1030.     //NDebugOverlay::Line( GetAbsOrigin(), m_vecLoiterPosition, 255, 255, 255, false, 0.1 );
  1031.  
  1032.     // Friendly manhack is loitering.
  1033.     if( !m_bHeld )
  1034.     {
  1035.         float distSqr = m_vecLoiterPosition.DistToSqr(GetAbsOrigin());
  1036.  
  1037.         if( distSqr > MAX_LOITER_DIST_SQR )
  1038.         {
  1039.             Vector vecDir = m_vecLoiterPosition - GetAbsOrigin();
  1040.             VectorNormalize( vecDir );
  1041.  
  1042.             // Move back to our loiter position.
  1043.             if( gpGlobals->curtime > m_fTimeNextLoiterPulse )
  1044.             {
  1045.                 // Apply a pulse of force if allowed right now.
  1046.                 if( distSqr > MAX_LOITER_DIST_SQR * 4.0f )
  1047.                 {
  1048.                     //Msg("Big Pulse\n");
  1049.                     m_vForceVelocity = vecDir * 12.0f;
  1050.                 }
  1051.                 else
  1052.                 {
  1053.                     //Msg("Small Pulse\n");
  1054.                     m_vForceVelocity = vecDir * 6.0f;
  1055.                 }
  1056.  
  1057.                 m_fTimeNextLoiterPulse = gpGlobals->curtime + 1.0f;
  1058.             }
  1059.             else
  1060.             {
  1061.                 m_vForceVelocity = vec3_origin;
  1062.             }
  1063.         }
  1064.         else
  1065.         {
  1066.             // Counteract velocity to slow down.
  1067.             Vector velocity = GetCurrentVelocity();
  1068.             m_vForceVelocity = velocity * -0.5;
  1069.         }
  1070.     }
  1071. }
  1072.  
  1073. //-----------------------------------------------------------------------------
  1074. // Purpose:
  1075. //-----------------------------------------------------------------------------
  1076. void CNPC_Manhack::MaintainGroundHeight( void )
  1077. {
  1078.     float zSpeed = GetCurrentVelocity().z;
  1079.  
  1080.     if ( zSpeed > 32.0f )
  1081.         return;
  1082.  
  1083.     const float minGroundHeight = 52.0f;
  1084.  
  1085.     trace_t tr;
  1086.     AI_TraceHull(   GetAbsOrigin(),
  1087.         GetAbsOrigin() - Vector( 0, 0, minGroundHeight ),
  1088.         GetHullMins(),
  1089.         GetHullMaxs(),
  1090.         (MASK_NPCSOLID_BRUSHONLY),
  1091.         this,
  1092.         COLLISION_GROUP_NONE,
  1093.         &tr );
  1094.  
  1095.     if ( tr.fraction != 1.0f )
  1096.     {
  1097.         float speedAdj = max( 16, (-zSpeed*0.5f) );
  1098.  
  1099.         m_vForceVelocity += Vector(0,0,1) * ( speedAdj * ( 1.0f - tr.fraction ) );
  1100.     }
  1101. }
  1102.  
  1103. //-----------------------------------------------------------------------------
  1104. // Purpose: Handles movement towards the last move target.
  1105. // Input  : flInterval -
  1106. //-----------------------------------------------------------------------------
  1107. bool CNPC_Manhack::OverrideMove( float flInterval )
  1108. {
  1109.     SpinBlades( flInterval );
  1110.        
  1111.     // Don't execute any move code if packed up.
  1112.     if( HasSpawnFlags(SF_MANHACK_PACKED_UP|SF_MANHACK_CARRIED) )
  1113.         return true;
  1114.  
  1115.     if( IsLoitering() )
  1116.     {
  1117.         Loiter();
  1118.     }
  1119.     else
  1120.     {
  1121.         MaintainGroundHeight();
  1122.     }
  1123.  
  1124.     // So cops, etc. will try to avoid them
  1125.     if ( !HasSpawnFlags( SF_MANHACK_NO_DANGER_SOUNDS ) && !m_bHeld )
  1126.     {
  1127.         CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), 75, flInterval, this );
  1128.     }
  1129.  
  1130.     // -----------------------------------------------------------------
  1131.     //  If I'm being forced to move somewhere
  1132.     // ------------------------------------------------------------------
  1133.     if (m_fForceMoveTime > gpGlobals->curtime)
  1134.     {
  1135.         MoveToTarget(flInterval, m_vForceMoveTarget);
  1136.     }
  1137.     // -----------------------------------------------------------------
  1138.     // If I have a route, keep it updated and move toward target
  1139.     // ------------------------------------------------------------------
  1140.     else if (GetNavigator()->IsGoalActive())
  1141.     {
  1142.         bool bReducible = GetNavigator()->GetPath()->GetCurWaypoint()->IsReducible();
  1143.         const float strictTolerance = 64.0;
  1144.         //NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, 10 ), 255, 0, 0, true, 0.1);
  1145.         if ( ProgressFlyPath( flInterval, GetEnemy(), MoveCollisionMask(), bReducible, strictTolerance ) == AINPP_COMPLETE )
  1146.             return true;
  1147.     }
  1148.     // -----------------------------------------------------------------
  1149.     // If I'm supposed to swarm somewhere, try to go there
  1150.     // ------------------------------------------------------------------
  1151.     else if (m_fSwarmMoveTime > gpGlobals->curtime)
  1152.     {
  1153.         MoveToTarget(flInterval, m_vSwarmMoveTarget);
  1154.     }
  1155.     // -----------------------------------------------------------------
  1156.     // If I don't have anything better to do, just decelerate
  1157.     // -------------------------------------------------------------- ----
  1158.     else
  1159.     {
  1160.         float   myDecay  = 9.5;
  1161.         Decelerate( flInterval, myDecay );
  1162.  
  1163.         m_vTargetBanking = vec3_origin;
  1164.  
  1165.         // -------------------------------------
  1166.         // If I have an enemy turn to face him
  1167.         // -------------------------------------
  1168.         if (GetEnemy())
  1169.         {
  1170.             TurnHeadToTarget(flInterval, GetEnemy()->EyePosition() );
  1171.         }
  1172.     }
  1173.  
  1174.     if ( m_iHealth <= 0 )
  1175.     {
  1176.         // Crashing!!
  1177.         MoveExecute_Dead(flInterval);
  1178.     }
  1179.     else
  1180.     {
  1181.         // Alive!
  1182.         MoveExecute_Alive(flInterval);
  1183.     }
  1184.  
  1185.     return true;
  1186. }
  1187.  
  1188.  
  1189. //-----------------------------------------------------------------------------
  1190. // Purpose:
  1191. // Input  :
  1192. // Output :
  1193. //-----------------------------------------------------------------------------
  1194. void CNPC_Manhack::TurnHeadRandomly(float flInterval )
  1195. {
  1196.     float desYaw = random->RandomFloat(0,360);
  1197.  
  1198.     float   iRate    = 0.8;
  1199.     // Make frame rate independent
  1200.     float timeToUse = flInterval;
  1201.     while (timeToUse > 0)
  1202.     {
  1203.         m_fHeadYaw     = (iRate * m_fHeadYaw) + (1-iRate)*desYaw;
  1204.         timeToUse =- 0.1;
  1205.     }
  1206. }
  1207.  
  1208. //-----------------------------------------------------------------------------
  1209. // Purpose:
  1210. // Input  :
  1211. // Output :
  1212. //-----------------------------------------------------------------------------
  1213. void CNPC_Manhack::MoveToTarget(float flInterval, const Vector &vMoveTarget)
  1214. {
  1215.     if (flInterval <= 0)
  1216.     {
  1217.         return;
  1218.     }
  1219.  
  1220.     // -----------------------------------------
  1221.     // Don't steer if engine's have stalled
  1222.     // -----------------------------------------
  1223.     if ( gpGlobals->curtime < m_flEngineStallTime || m_iHealth <= 0 )
  1224.         return;
  1225.  
  1226.     if ( GetEnemy() != NULL )
  1227.     {
  1228.         TurnHeadToTarget( flInterval, GetEnemy()->EyePosition() );
  1229.     }
  1230.     else
  1231.     {
  1232.         TurnHeadToTarget( flInterval, vMoveTarget );
  1233.     }
  1234.  
  1235.     // -------------------------------------
  1236.     // Move towards our target
  1237.     // -------------------------------------
  1238.     float   myAccel;
  1239.     float   myZAccel = 300.0f;
  1240.     float   myDecay  = 0.3f;
  1241.  
  1242.     Vector targetDir;
  1243.     float flDist;
  1244.  
  1245.     // If we're bursting, just head straight
  1246.     if ( m_flBurstDuration > gpGlobals->curtime )
  1247.     {
  1248.         float zDist = 500;
  1249.  
  1250.         // Steer towards our enemy if we're able to
  1251.         if ( GetEnemy() != NULL )
  1252.         {
  1253.             Vector steerDir = ( GetEnemy()->EyePosition() - GetAbsOrigin() );
  1254.             zDist = fabs( steerDir.z );
  1255.             VectorNormalize( steerDir );
  1256.  
  1257.             float useTime = flInterval;
  1258.             while ( useTime > 0.0f )
  1259.             {
  1260.                 m_vecBurstDirection += ( steerDir * 4.0f );
  1261.                 useTime -= 0.1f;
  1262.             }
  1263.  
  1264.             m_vecBurstDirection.z = steerDir.z;
  1265.  
  1266.             VectorNormalize( m_vecBurstDirection );
  1267.         }
  1268.  
  1269.         // Debug visualizations
  1270.         /*
  1271.         NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( targetDir * 64.0f ), 255, 0, 0, true, 2.1f );
  1272.         NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( steerDir * 64.0f ), 0, 255, 0, true, 2.1f );
  1273.         NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( m_vecBurstDirection * 64.0f ), 0, 0, 255, true, 2.1f );
  1274.         NDebugOverlay::Cross3D( GetAbsOrigin() , -Vector(8,8,8), Vector(8,8,8), 255, 0, 0, true, 2.1f );
  1275.         */
  1276.  
  1277.         targetDir = m_vecBurstDirection;
  1278.  
  1279.         flDist  = FLT_MAX;
  1280.         myDecay  = 0.3f;
  1281. #ifdef _XBOX
  1282.         myAccel  = 500;
  1283. #else
  1284.         myAccel  = 400;
  1285. #endif // _XBOX
  1286.         myZAccel = min( 500, zDist / flInterval );
  1287.     }
  1288.     else
  1289.     {
  1290.         Vector vecCurrentDir = GetCurrentVelocity();
  1291.         VectorNormalize( vecCurrentDir );
  1292.  
  1293.         targetDir = vMoveTarget - GetAbsOrigin();
  1294.         flDist = VectorNormalize( targetDir );
  1295.        
  1296.         float flDot = DotProduct( targetDir, vecCurrentDir );
  1297.  
  1298.         // Otherwise we should steer towards our goal
  1299.         if( flDot > 0.25 )
  1300.         {
  1301.             // If my target is in front of me, my flight model is a bit more accurate.
  1302.             myAccel = 300;
  1303.         }
  1304.         else
  1305.         {
  1306.             // Have a harder time correcting my course if I'm currently flying away from my target.
  1307.             myAccel = 200;
  1308.         }
  1309.     }
  1310.  
  1311.     // Clamp lateral acceleration
  1312.     if ( myAccel > ( flDist / flInterval ) )
  1313.     {
  1314.         myAccel = flDist / flInterval;
  1315.     }
  1316.  
  1317.     /*
  1318.     // Boost vertical movement
  1319.     if ( targetDir.z > 0 )
  1320.     {
  1321.         // Z acceleration is faster when we thrust upwards.
  1322.         // This is to help keep manhacks out of water.
  1323.         myZAccel *= 5.0;
  1324.     }
  1325.     */
  1326.  
  1327.     // Clamp vertical movement
  1328.     if ( myZAccel > flDist / flInterval )
  1329.     {
  1330.         myZAccel = flDist / flInterval;
  1331.     }
  1332.  
  1333.     // Scale by our engine force
  1334.     myAccel *= m_fEnginePowerScale;
  1335.     myZAccel *= m_fEnginePowerScale;
  1336.    
  1337.     MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay );
  1338.  
  1339.     // calc relative banking targets
  1340.     Vector forward, right;
  1341.     GetVectors( &forward, &right, NULL );
  1342.     m_vTargetBanking.x  = 40 * DotProduct( forward, targetDir );
  1343.     m_vTargetBanking.z  = 40 * DotProduct( right, targetDir );
  1344.     m_vTargetBanking.y  = 0.0;
  1345. }
  1346.  
  1347.  
  1348. //-----------------------------------------------------------------------------
  1349. // Purpose: Ignore water if I'm close to my enemy
  1350. // Input  :
  1351. // Output :
  1352. //-----------------------------------------------------------------------------
  1353. int CNPC_Manhack::MoveCollisionMask(void)
  1354. {
  1355.     return MASK_NPCSOLID;
  1356. }
  1357.  
  1358.  
  1359. //-----------------------------------------------------------------------------
  1360. // Purpose: Make a splash effect
  1361. // Input  :
  1362. // Output :
  1363. //-----------------------------------------------------------------------------
  1364. void CNPC_Manhack::Splash( const Vector &vecSplashPos )
  1365. {
  1366.     CEffectData data;
  1367.  
  1368.     data.m_fFlags = 0;
  1369.     data.m_vOrigin = vecSplashPos;
  1370.     data.m_vNormal = Vector( 0, 0, 1 );
  1371.  
  1372.     data.m_flScale = 8.0f;
  1373.  
  1374.     int contents = GetWaterType();
  1375.  
  1376.     // Verify we have valid contents
  1377.     if ( !( contents & (CONTENTS_SLIME|CONTENTS_WATER)))
  1378.     {
  1379.         // We're leaving the water so we have to reverify what it was
  1380.         trace_t tr;
  1381.         UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 256 ), (CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &tr );
  1382.  
  1383.         // Re-validate this
  1384.         if ( !(tr.contents&(CONTENTS_WATER|CONTENTS_SLIME)) )
  1385.         {
  1386.             //NOTENOTE: We called a splash but we don't seem to be near water?
  1387.             Assert( 0 );
  1388.             return;
  1389.         }
  1390.  
  1391.         contents = tr.contents;
  1392.     }
  1393.    
  1394.     // Mark us if we're in slime
  1395.     if ( contents & CONTENTS_SLIME )
  1396.     {
  1397.         data.m_fFlags |= FX_WATER_IN_SLIME;
  1398.     }
  1399.  
  1400.     DispatchEffect( "watersplash", data );
  1401. }
  1402.  
  1403. //-----------------------------------------------------------------------------
  1404. // Computes the slice bounce velocity
  1405. //-----------------------------------------------------------------------------
  1406. void CNPC_Manhack::ComputeSliceBounceVelocity( CBaseEntity *pHitEntity, trace_t &tr )
  1407. {
  1408.     if( pHitEntity->IsAlive() && FClassnameIs( pHitEntity, "func_breakable_surf" ) )
  1409.     {
  1410.         // We want to see if the manhack hits a breakable pane of glass. To keep from checking
  1411.         // The classname of the HitEntity on each impact, we only do this check if we hit
  1412.         // something that's alive. Anyway, prevent the manhack bouncing off the pane of glass,
  1413.         // since this impact will shatter the glass and let the manhack through.
  1414.         return;
  1415.     }
  1416.  
  1417.     Vector vecDir;
  1418.    
  1419.     // If the manhack isn't bouncing away from whatever he sliced, force it.
  1420.     VectorSubtract( WorldSpaceCenter(), pHitEntity->WorldSpaceCenter(), vecDir );
  1421.     VectorNormalize( vecDir );
  1422.     vecDir *= 200;
  1423.     vecDir[2] = 0.0f;
  1424.    
  1425.     // Knock it away from us
  1426.     if ( VPhysicsGetObject() != NULL )
  1427.     {
  1428.         VPhysicsGetObject()->ApplyForceOffset( vecDir * 4, GetAbsOrigin() );
  1429.     }
  1430.  
  1431.     // Also set our velocity
  1432.     SetCurrentVelocity( vecDir );
  1433. }
  1434.  
  1435.  
  1436. //-----------------------------------------------------------------------------
  1437. // Is the manhack being held?
  1438. //-----------------------------------------------------------------------------
  1439. bool CNPC_Manhack::IsHeldByPhyscannon( )
  1440. {
  1441.     return VPhysicsGetObject() && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD);
  1442. }
  1443.  
  1444.    
  1445. //-----------------------------------------------------------------------------
  1446. // Purpose: We've touched something that we can hurt. Slice it!
  1447. // Input  :
  1448. // Output :
  1449. //-----------------------------------------------------------------------------
  1450. void CNPC_Manhack::Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr )
  1451. {
  1452.     // Don't hurt the player if I'm in water
  1453.     if( GetWaterLevel() > 0 && pHitEntity->IsPlayer() )
  1454.         return;
  1455.  
  1456.     // Can't slice players holding it with the phys cannon
  1457.     if ( IsHeldByPhyscannon() )
  1458.     {
  1459.         if ( pHitEntity && (pHitEntity == HasPhysicsAttacker( FLT_MAX )) )
  1460.             return;
  1461.     }
  1462.  
  1463.     if ( pHitEntity->m_takedamage == DAMAGE_NO )
  1464.         return;
  1465.  
  1466.     // Damage must be scaled by flInterval so framerate independent
  1467.     float flDamage = sk_manhack_melee_dmg.GetFloat() * flInterval;
  1468.  
  1469.     if ( pHitEntity->IsPlayer() )
  1470.     {
  1471.         flDamage *= 2.0f;
  1472.     }
  1473.    
  1474.     // Held manhacks do more damage
  1475.     if ( IsHeldByPhyscannon() )
  1476.     {
  1477.         // Deal 100 damage/sec
  1478.         flDamage = 100.0f * flInterval;
  1479.     }
  1480.     else if ( pHitEntity->IsNPC() && HasPhysicsAttacker( MANHACK_SMASH_TIME ) )
  1481.     {
  1482.         extern ConVar sk_combine_guard_health;
  1483.         // NOTE: The else here is essential.
  1484.         // The physics attacker *will* be set even when the manhack is held
  1485.         flDamage = sk_combine_guard_health.GetFloat(); // the highest healthed fleshy enemy
  1486.     }
  1487.     else if ( dynamic_cast<CBaseProp*>(pHitEntity) || dynamic_cast<CBreakable*>(pHitEntity) )
  1488.     {
  1489.         // If we hit a prop, we want it to break immediately
  1490.         flDamage = pHitEntity->GetHealth();
  1491.     }
  1492.     else if ( pHitEntity->IsNPC() && IRelationType( pHitEntity ) == D_HT  && FClassnameIs( pHitEntity, "npc_combine_s" ) )
  1493.     {
  1494.         flDamage *= 6.0f;
  1495.     }
  1496.  
  1497.     if (flDamage < 1.0f)
  1498.     {
  1499.         flDamage = 1.0f;
  1500.     }
  1501.  
  1502.     CTakeDamageInfo info( this, this, flDamage, DMG_SLASH );
  1503.  
  1504.     // check for actual "ownership" of damage
  1505.     CBasePlayer *pPlayer = HasPhysicsAttacker( MANHACK_SMASH_TIME );
  1506.     if (pPlayer)
  1507.     {
  1508.         info.SetAttacker( pPlayer );
  1509.     }
  1510.  
  1511.     Vector dir = (tr.endpos - tr.startpos);
  1512.     if ( dir == vec3_origin )
  1513.     {
  1514.         dir = tr.m_pEnt->GetAbsOrigin() - GetAbsOrigin();
  1515.     }
  1516.     CalculateMeleeDamageForce( &info, dir, tr.endpos );
  1517.     pHitEntity->TakeDamage( info );
  1518.  
  1519.     // Spawn some extra blood where we hit
  1520.     if ( pHitEntity->BloodColor() == DONT_BLEED )
  1521.     {
  1522.         CEffectData data;
  1523.         Vector velocity = GetCurrentVelocity();
  1524.  
  1525.         data.m_vOrigin = tr.endpos;
  1526.         data.m_vAngles = GetAbsAngles();
  1527.  
  1528.         VectorNormalize( velocity );
  1529.        
  1530.         data.m_vNormal = ( tr.plane.normal + velocity ) * 0.5;;
  1531.  
  1532.         DispatchEffect( "ManhackSparks", data );
  1533.  
  1534.         EmitSound( "NPC_Manhack.Grind" );
  1535.  
  1536.         //TODO: What we really want to do is get a material reference and emit the proper sprayage! - jdw
  1537.     }
  1538.     else
  1539.     {
  1540.         SpawnBlood(tr.endpos, g_vecAttackDir, pHitEntity->BloodColor(), 6 );
  1541.         EmitSound( "NPC_Manhack.Slice" );
  1542.     }
  1543.  
  1544.     // Pop back a little bit after hitting the player
  1545.     ComputeSliceBounceVelocity( pHitEntity, tr );
  1546.  
  1547.     // Save off when we last hit something
  1548.     m_flLastDamageTime = gpGlobals->curtime;
  1549.  
  1550.     // Reset our state and give the player time to react
  1551.     StopBurst( true );
  1552. }
  1553.  
  1554. //-----------------------------------------------------------------------------
  1555. // Purpose: We've touched something solid. Just bump it.
  1556. // Input  :
  1557. // Output :
  1558. //-----------------------------------------------------------------------------
  1559. void CNPC_Manhack::Bump( CBaseEntity *pHitEntity, float flInterval, trace_t &tr )
  1560. {
  1561.     if ( !VPhysicsGetObject() )
  1562.         return;
  1563.  
  1564.     // Surpressing this behavior
  1565.     if ( m_flBumpSuppressTime > gpGlobals->curtime )
  1566.         return;
  1567.  
  1568.     if ( pHitEntity->GetMoveType() == MOVETYPE_VPHYSICS && pHitEntity->Classify()!=CLASS_MANHACK )
  1569.     {
  1570.         HitPhysicsObject( pHitEntity );
  1571.     }
  1572.  
  1573.     // We've hit something so deflect our velocity based on the surface
  1574.     // norm of what we've hit
  1575.     if (flInterval > 0)
  1576.     {
  1577.         float moveLen = ( (GetCurrentVelocity() * flInterval) * (1.0 - tr.fraction) ).Length();
  1578.  
  1579.         Vector moveVec  = moveLen*tr.plane.normal/flInterval;
  1580.  
  1581.         // If I'm totally dead, don't bounce me up
  1582.         if (m_iHealth <=0 && moveVec.z > 0)
  1583.         {
  1584.             moveVec.z = 0;
  1585.         }
  1586.  
  1587.         // If I'm right over the ground don't push down
  1588.         if (moveVec.z < 0)
  1589.         {
  1590.             float floorZ = GetFloorZ(GetAbsOrigin());
  1591.             if (abs(GetAbsOrigin().z - floorZ) < 36)
  1592.             {
  1593.                 moveVec.z = 0;
  1594.             }
  1595.         }
  1596.  
  1597.         Vector myUp;
  1598.         VPhysicsGetObject()->LocalToWorldVector( &myUp, Vector( 0.0, 0.0, 1.0 ) );
  1599.  
  1600.         // plane must be something that could hit the blades
  1601.         if (fabs( DotProduct( myUp, tr.plane.normal ) ) < 0.25 )
  1602.         {
  1603.             CEffectData data;
  1604.             Vector velocity = GetCurrentVelocity();
  1605.  
  1606.             data.m_vOrigin = tr.endpos;
  1607.             data.m_vAngles = GetAbsAngles();
  1608.  
  1609.             VectorNormalize( velocity );
  1610.            
  1611.             data.m_vNormal = ( tr.plane.normal + velocity ) * 0.5;;
  1612.  
  1613.             DispatchEffect( "ManhackSparks", data );
  1614.  
  1615.             CBroadcastRecipientFilter filter;
  1616.  
  1617.             te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 255, 180, 100, 0, 50, 0.3, 150 );
  1618.            
  1619.             // add some spin, but only if we're not already going fast..
  1620.             Vector vecVelocity;
  1621.             AngularImpulse vecAngVelocity;
  1622.             VPhysicsGetObject()->GetVelocity( &vecVelocity, &vecAngVelocity );
  1623.             float flDot = DotProduct( myUp, vecAngVelocity );
  1624.             if ( fabs(flDot) < 100 )
  1625.             {
  1626.                 //AngularImpulse torque = myUp * (1000 - flDot * 10);
  1627.                 AngularImpulse torque = myUp * (1000 - flDot * 2);
  1628.                 VPhysicsGetObject()->ApplyTorqueCenter( torque );
  1629.             }
  1630.            
  1631.             if (!(m_spawnflags  & SF_NPC_GAG))
  1632.             {
  1633.                 EmitSound( "NPC_Manhack.Grind" );
  1634.             }
  1635.  
  1636.             // For decals and sparks we must trace a line in the direction of the surface norm
  1637.             // that we hit.
  1638.             trace_t decalTrace;
  1639.             AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() - (tr.plane.normal * 24),MASK_SOLID, this, COLLISION_GROUP_NONE, &decalTrace );
  1640.  
  1641.             if ( decalTrace.fraction != 1.0 )
  1642.             {
  1643.                 // Leave decal only if colliding horizontally
  1644.                 if ((DotProduct(Vector(0,0,1),decalTrace.plane.normal)<0.5) && (DotProduct(Vector(0,0,-1),decalTrace.plane.normal)<0.5))
  1645.                 {
  1646.                     UTIL_DecalTrace( &decalTrace, "ManhackCut" );
  1647.                 }
  1648.             }
  1649.         }
  1650.        
  1651.         // See if we will not have a valid surface
  1652.         if ( tr.allsolid || tr.startsolid )
  1653.         {
  1654.             // Build a fake reflection back along our current velocity because we can't know how to reflect off
  1655.             // a non-existant surface! -- jdw
  1656.  
  1657.             Vector vecRandomDir = RandomVector( -1.0f, 1.0f );
  1658.             SetCurrentVelocity( vecRandomDir * 50.0f );
  1659.             m_flBumpSuppressTime = gpGlobals->curtime + 0.5f;
  1660.         }
  1661.         else
  1662.         {
  1663.             // This is a valid hit and we can deflect properly
  1664.            
  1665.             VectorNormalize( moveVec );
  1666.             float hitAngle = -DotProduct( tr.plane.normal, -moveVec );
  1667.  
  1668.             Vector vReflection = 2.0 * tr.plane.normal * hitAngle + -moveVec;
  1669.  
  1670.             float flSpeed = GetCurrentVelocity().Length();
  1671.             SetCurrentVelocity( GetCurrentVelocity() + vReflection * flSpeed * 0.5f );
  1672.         }
  1673.     }
  1674.  
  1675.     // -------------------------------------------------------------
  1676.     // If I'm on a path check LOS to my next node, and fail on path
  1677.     // if I don't have LOS.  Note this is the only place I do this,
  1678.     // so the manhack has to collide before failing on a path
  1679.     // -------------------------------------------------------------
  1680.     if (GetNavigator()->IsGoalActive() && !(GetNavigator()->GetPath()->CurWaypointFlags() & bits_WP_TO_PATHCORNER) )
  1681.     {
  1682.         AIMoveTrace_t moveTrace;
  1683.         GetMoveProbe()->MoveLimit( NAV_GROUND, GetAbsOrigin(), GetNavigator()->GetCurWaypointPos(),
  1684.             MoveCollisionMask(), GetEnemy(), &moveTrace );
  1685.  
  1686.         if (IsMoveBlocked( moveTrace ) &&
  1687.             !moveTrace.pObstruction->ClassMatches( GetClassname() ))
  1688.         {
  1689.             TaskFail(FAIL_NO_ROUTE);
  1690.             GetNavigator()->ClearGoal();
  1691.             return;
  1692.         }
  1693.     }
  1694. }
  1695.  
  1696.  
  1697. //-----------------------------------------------------------------------------
  1698. // Purpose:
  1699. // Input  :
  1700. // Output :
  1701. //-----------------------------------------------------------------------------
  1702. void CNPC_Manhack::CheckCollisions(float flInterval)
  1703. {
  1704.     // Trace forward to see if I hit anything. But trace forward along the
  1705.     // owner's view direction if you're being carried.
  1706.     Vector vecTraceDir, vecCheckPos;
  1707.     VPhysicsGetObject()->GetVelocity( &vecTraceDir, NULL );
  1708.     vecTraceDir *= flInterval;
  1709.     if ( IsHeldByPhyscannon() )
  1710.     {
  1711.         CBasePlayer *pCarrier = HasPhysicsAttacker( FLT_MAX );
  1712.         if ( pCarrier )
  1713.         {
  1714.             if ( pCarrier->CollisionProp()->CalcDistanceFromPoint( WorldSpaceCenter() ) < 30 )
  1715.             {
  1716.                 AngleVectors( pCarrier->EyeAngles(), &vecTraceDir, NULL, NULL );
  1717.                 vecTraceDir *= 40.0f;
  1718.             }
  1719.         }
  1720.     }
  1721.  
  1722.     VectorAdd( GetAbsOrigin(), vecTraceDir, vecCheckPos );
  1723.    
  1724.     trace_t         tr;
  1725.     CBaseEntity*    pHitEntity = NULL;
  1726.    
  1727.     AI_TraceHull(   GetAbsOrigin(),
  1728.                     vecCheckPos,
  1729.                     GetHullMins(),
  1730.                     GetHullMaxs(),
  1731.                     MoveCollisionMask(),
  1732.                     this,
  1733.                     COLLISION_GROUP_NONE,
  1734.                     &tr );
  1735.  
  1736.     if ( (tr.fraction != 1.0 || tr.startsolid) && tr.m_pEnt)
  1737.     {
  1738.         PhysicsMarkEntitiesAsTouching( tr.m_pEnt, tr );
  1739.         pHitEntity = tr.m_pEnt;
  1740.  
  1741.         if( m_bHeld && tr.m_pEnt->MyNPCPointer() && tr.m_pEnt->MyNPCPointer()->IsPlayerAlly() )
  1742.         {
  1743.             // Don't slice Alyx when she approaches to hack. We need a better solution for this!!
  1744.             //Msg("Ignoring!\n");
  1745.             return;
  1746.         }
  1747.  
  1748.         if ( pHitEntity != NULL &&
  1749.              pHitEntity->m_takedamage == DAMAGE_YES &&
  1750.              pHitEntity->Classify() != CLASS_MANHACK &&
  1751.              gpGlobals->curtime > m_flWaterSuspendTime )
  1752.         {
  1753.             // Slice this thing
  1754.             Slice( pHitEntity, flInterval, tr );
  1755.             m_flBladeSpeed = 20.0;
  1756.         }
  1757.         else
  1758.         {
  1759.             // Just bump into this thing.
  1760.             Bump( pHitEntity, flInterval, tr );
  1761.             m_flBladeSpeed = 20.0;
  1762.         }
  1763.     }
  1764. }
  1765.  
  1766. //-----------------------------------------------------------------------------
  1767. // Purpose:
  1768. // Input  :
  1769. // Output :
  1770. //-----------------------------------------------------------------------------
  1771. #define tempTIME_STEP = 0.5;
  1772. void CNPC_Manhack::PlayFlySound(void)
  1773. {
  1774.     float flEnemyDist;
  1775.  
  1776.     if( GetEnemy() )
  1777.     {
  1778.         flEnemyDist = (GetAbsOrigin() - GetEnemy()->GetAbsOrigin()).Length();
  1779.     }
  1780.     else
  1781.     {
  1782.         flEnemyDist = FLT_MAX;
  1783.     }
  1784.  
  1785.     if( m_spawnflags & SF_NPC_GAG )
  1786.     {
  1787.         // Quiet!
  1788.         return;
  1789.     }
  1790.  
  1791.     if( m_flWaterSuspendTime > gpGlobals->curtime )
  1792.     {
  1793.         // Just went in water. Slow the motor!!
  1794.         if( m_bDirtyPitch )
  1795.         {
  1796.             m_nEnginePitch1 = MANHACK_WATER_PITCH1;
  1797.             m_flEnginePitch1Time = gpGlobals->curtime + 0.5f;
  1798.             m_nEnginePitch2 = MANHACK_WATER_PITCH2;
  1799.             m_flEnginePitch2Time = gpGlobals->curtime + 0.5f;
  1800.             m_bDirtyPitch = false;
  1801.         }
  1802.     }
  1803.     // Spin sound based on distance from enemy (unless we're crashing)
  1804.     else if (GetEnemy() && IsAlive() )
  1805.     {
  1806.         if( flEnemyDist < MANHACK_PITCH_DIST1 )
  1807.         {
  1808.             // recalculate pitch.
  1809.             int iPitch1, iPitch2;
  1810.             float flDistFactor;
  1811.  
  1812.             flDistFactor = min( 1.0, 1 - flEnemyDist / MANHACK_PITCH_DIST1 );
  1813.             iPitch1 = MANHACK_MIN_PITCH1 + ( ( MANHACK_MAX_PITCH1 - MANHACK_MIN_PITCH1 ) * flDistFactor);
  1814.  
  1815.             // NOTE: MANHACK_PITCH_DIST2 must be < MANHACK_PITCH_DIST1
  1816.             flDistFactor = min( 1.0, 1 - flEnemyDist / MANHACK_PITCH_DIST2 );
  1817.             iPitch2 = MANHACK_MIN_PITCH2 + ( ( MANHACK_MAX_PITCH2 - MANHACK_MIN_PITCH2 ) * flDistFactor);
  1818.  
  1819.             m_nEnginePitch1 = iPitch1;
  1820.             m_flEnginePitch1Time = gpGlobals->curtime + 0.1f;
  1821.             m_nEnginePitch2 = iPitch2;
  1822.             m_flEnginePitch2Time = gpGlobals->curtime + 0.1f;
  1823.  
  1824.             m_bDirtyPitch = true;
  1825.         }
  1826.         else if( m_bDirtyPitch )
  1827.         {
  1828.             m_nEnginePitch1 = MANHACK_MIN_PITCH1;
  1829.             m_flEnginePitch1Time = gpGlobals->curtime + 0.1f;
  1830.             m_nEnginePitch2 = MANHACK_MIN_PITCH2;
  1831.             m_flEnginePitch2Time = gpGlobals->curtime + 0.2f;
  1832.             m_bDirtyPitch = false;
  1833.         }
  1834.     }
  1835.     // If no enemy just play low sound
  1836.     else if( IsAlive() && m_bDirtyPitch )
  1837.     {
  1838.         m_nEnginePitch1 = MANHACK_MIN_PITCH1;
  1839.         m_flEnginePitch1Time = gpGlobals->curtime + 0.1f;
  1840.         m_nEnginePitch2 = MANHACK_MIN_PITCH2;
  1841.         m_flEnginePitch2Time = gpGlobals->curtime + 0.2f;
  1842.  
  1843.         m_bDirtyPitch = false;
  1844.     }
  1845.  
  1846.     // Play special engine every once in a while
  1847.     if (gpGlobals->curtime > m_flNextEngineSoundTime && flEnemyDist < 48)
  1848.     {
  1849.         m_flNextEngineSoundTime = gpGlobals->curtime + random->RandomFloat( 3.0, 10.0 );
  1850.  
  1851.         EmitSound( "NPC_Manhack.EngineNoise" );
  1852.     }
  1853. }
  1854.  
  1855. //-----------------------------------------------------------------------------
  1856. // Purpose:
  1857. // Input  :
  1858. // Output :
  1859. //-----------------------------------------------------------------------------
  1860. void CNPC_Manhack::MoveExecute_Alive(float flInterval)
  1861. {
  1862.     PhysicsCheckWaterTransition();
  1863.  
  1864.     Vector  vCurrentVelocity = GetCurrentVelocity();
  1865.  
  1866.     // FIXME: move this
  1867.     VPhysicsGetObject()->Wake();
  1868.  
  1869.     if( m_fEnginePowerScale < GetMaxEnginePower() && gpGlobals->curtime > m_flWaterSuspendTime )
  1870.     {
  1871.         // Power is low, and we're no longer stuck in water, so bring power up.
  1872.         m_fEnginePowerScale += 0.05;
  1873.     }
  1874.  
  1875.     // ----------------------------------------------------------------------------------------
  1876.     // Add time-coherent noise to the current velocity so that it never looks bolted in place.
  1877.     // ----------------------------------------------------------------------------------------
  1878.     float noiseScale = 7.0f;
  1879.  
  1880.     if ( (CBaseEntity*)GetEnemy() )
  1881.     {
  1882.         float flDist = (GetAbsOrigin() - GetEnemy()->GetAbsOrigin()).Length2D();
  1883.  
  1884.         if( flDist < MANHACK_CHARGE_MIN_DIST )
  1885.         {
  1886.             // Less noise up close.
  1887.             noiseScale = 2.0;
  1888.         }
  1889.  
  1890.         if ( IsInEffectiveTargetZone( GetEnemy() ) && flDist < MANHACK_CHARGE_MIN_DIST && gpGlobals->curtime > m_flNextBurstTime )
  1891.         {
  1892.             Vector vecCurrentDir = GetCurrentVelocity();
  1893.             VectorNormalize( vecCurrentDir );
  1894.  
  1895.             Vector vecToEnemy = ( GetEnemy()->EyePosition() - WorldSpaceCenter() );
  1896.             VectorNormalize( vecToEnemy );
  1897.  
  1898.             float flDot = DotProduct( vecCurrentDir, vecToEnemy );
  1899.  
  1900.             if ( flDot > 0.75 )
  1901.             {              
  1902.                 Vector offsetDir = ( vecToEnemy - vecCurrentDir );
  1903.                 VectorNormalize( offsetDir );
  1904.  
  1905.                 Vector offsetSpeed = GetCurrentVelocity() * flDot;
  1906.  
  1907.                 //FIXME: This code sucks -- jdw
  1908.  
  1909.                 offsetDir.z = 0.0f;
  1910.                 m_vForceVelocity += ( offsetDir * ( offsetSpeed.Length2D() * 0.25f ) );
  1911.  
  1912.                 // Commit to the attack- no steering for about a second
  1913.                 StartBurst( vecToEnemy );
  1914.                 SetEyeState( MANHACK_EYE_STATE_CHARGE );
  1915.             }
  1916.         }
  1917.        
  1918.         if ( gpGlobals->curtime > m_flBurstDuration )
  1919.         {
  1920.             ShowHostile( false );
  1921.         }
  1922.     }
  1923.  
  1924.     // ----------------------------------------------------------------------------------------
  1925.     // Add in any forced velocity
  1926.     // ----------------------------------------------------------------------------------------
  1927.     SetCurrentVelocity( vCurrentVelocity + m_vForceVelocity );
  1928.     m_vForceVelocity = vec3_origin;
  1929.  
  1930.     if( !m_bHackedByAlyx || GetEnemy() )
  1931.     {
  1932.         // If hacked and no enemy, don't drift!
  1933.         AddNoiseToVelocity( noiseScale );
  1934.     }
  1935.  
  1936.     LimitSpeed( 200, ManhackMaxSpeed() );
  1937.  
  1938.     if( m_flWaterSuspendTime > gpGlobals->curtime )
  1939.     {
  1940.         if( UTIL_PointContents( GetAbsOrigin() ) & (CONTENTS_WATER|CONTENTS_SLIME) )
  1941.         {
  1942.             // Ooops, we're submerged somehow. Move upwards until our origin is out of the water.
  1943.             m_vCurrentVelocity.z = 20.0;
  1944.         }
  1945.         else
  1946.         {
  1947.             // Skimming the surface. Forbid any movement on Z
  1948.             m_vCurrentVelocity.z = 0.0;
  1949.         }
  1950.     }
  1951.     else if( GetWaterLevel() > 0 )
  1952.     {
  1953.         // Allow the manhack to lift off, but not to go deeper.
  1954.         m_vCurrentVelocity.z = max( m_vCurrentVelocity.z, 0 );
  1955.     }
  1956.  
  1957.     CheckCollisions(flInterval);
  1958.  
  1959.     // Blend out desired velocity when launched by the physcannon
  1960.     if ( HasPhysicsAttacker( MANHACK_SMASH_TIME ) && (!IsHeldByPhyscannon()) && VPhysicsGetObject() )
  1961.     {
  1962.         Vector vecCurrentVelocity;
  1963.         VPhysicsGetObject()->GetVelocity( &vecCurrentVelocity, NULL );
  1964.         float flLerpFactor = (gpGlobals->curtime - m_flLastPhysicsInfluenceTime) / MANHACK_SMASH_TIME;
  1965.         flLerpFactor = clamp( flLerpFactor, 0.0f, 1.0f );
  1966.         flLerpFactor = SimpleSplineRemapVal( flLerpFactor, 0.0f, 1.0f, 0.0f, 1.0f );
  1967.         VectorLerp( vecCurrentVelocity, m_vCurrentVelocity, flLerpFactor, m_vCurrentVelocity );
  1968.     }
  1969.  
  1970.     QAngle angles = GetLocalAngles();
  1971.  
  1972.     // ------------------------------------------
  1973.     //  Stalling
  1974.     // ------------------------------------------
  1975.     if (gpGlobals->curtime < m_flEngineStallTime)
  1976.     {
  1977.         /*
  1978.         // If I'm stalled add random noise
  1979.         angles.x += -20+(random->RandomInt(-10,10));
  1980.         angles.z += -20+(random->RandomInt(0,40));
  1981.  
  1982.         TurnHeadRandomly(flInterval);
  1983.         */
  1984.     }
  1985.     else
  1986.     {
  1987.         // Make frame rate independent
  1988.         float   iRate    = 0.5;
  1989.         float timeToUse = flInterval;
  1990.         while (timeToUse > 0)
  1991.         {
  1992.             m_vCurrentBanking.x = (iRate * m_vCurrentBanking.x) + (1 - iRate)*(m_vTargetBanking.x);
  1993.             m_vCurrentBanking.z = (iRate * m_vCurrentBanking.z) + (1 - iRate)*(m_vTargetBanking.z);
  1994.             timeToUse =- 0.1;
  1995.         }
  1996.         angles.x = m_vCurrentBanking.x;
  1997.         angles.z = m_vCurrentBanking.z;
  1998.         angles.y = 0;
  1999.  
  2000. #if 0
  2001.         // Using our steering if we're not otherwise affecting our panels
  2002.         if ( m_flEngineStallTime < gpGlobals->curtime && m_flBurstDuration < gpGlobals->curtime )
  2003.         {
  2004.             Vector delta( 10 * AngleDiff( m_vTargetBanking.x, m_vCurrentBanking.x ), -10 * AngleDiff( m_vTargetBanking.z, m_vCurrentBanking.z ), 0 );
  2005.             //Vector delta( 3 * AngleNormalize( m_vCurrentBanking.x ), -4 * AngleNormalize( m_vCurrentBanking.z ), 0 );
  2006.             VectorYawRotate( delta, -m_fHeadYaw, delta );
  2007.  
  2008.             // DevMsg("%.0f %.0f\n", delta.x, delta.y );
  2009.  
  2010.             SetPoseParameter( m_iPanel1, -delta.x - delta.y * 2);
  2011.             SetPoseParameter( m_iPanel2, -delta.x + delta.y * 2);
  2012.             SetPoseParameter( m_iPanel3,  delta.x + delta.y * 2);
  2013.             SetPoseParameter( m_iPanel4,  delta.x - delta.y * 2);
  2014.  
  2015.             //SetPoseParameter( m_iPanel1, -delta.x );
  2016.             //SetPoseParameter( m_iPanel2, -delta.x );
  2017.             //SetPoseParameter( m_iPanel3, delta.x );
  2018.             //SetPoseParameter( m_iPanel4, delta.x );
  2019.         }
  2020. #endif
  2021.     }
  2022.  
  2023.     // SetLocalAngles( angles );
  2024.  
  2025.     if( m_lifeState != LIFE_DEAD )
  2026.     {
  2027.         PlayFlySound();
  2028.         // SpinBlades( flInterval );
  2029.         // WalkMove( GetCurrentVelocity() * flInterval, MASK_NPCSOLID );
  2030.     }
  2031.  
  2032. //   NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -10 ), 0, 255, 0, true, 0.1);
  2033. }
  2034.  
  2035.  
  2036. //-----------------------------------------------------------------------------
  2037. // Purpose:
  2038. // Input  :
  2039. // Output :
  2040. //-----------------------------------------------------------------------------
  2041. void CNPC_Manhack::SpinBlades(float flInterval)
  2042. {
  2043.     if (!m_bBladesActive)
  2044.     {
  2045.         SetBodygroup( MANHACK_BODYGROUP_BLADE, MANHACK_BODYGROUP_OFF );
  2046.         SetBodygroup( MANHACK_BODYGROUP_BLUR, MANHACK_BODYGROUP_OFF );
  2047.         m_flBladeSpeed = 0.0;
  2048.         m_flPlaybackRate = 1.0;
  2049.         return;
  2050.     }
  2051.  
  2052.     if ( IsFlyingActivity( GetActivity() ) )
  2053.     {
  2054.         // Blades may only ramp up while the engine is running
  2055.         if ( m_flEngineStallTime < gpGlobals->curtime )
  2056.         {
  2057.             if (m_flBladeSpeed < 10)
  2058.             {
  2059.                 m_flBladeSpeed = m_flBladeSpeed * 2 + 1;
  2060.             }
  2061.             else
  2062.             {
  2063.                 // accelerate engine
  2064.                 m_flBladeSpeed = m_flBladeSpeed + 80 * flInterval;
  2065.             }
  2066.         }
  2067.  
  2068.         if (m_flBladeSpeed > 100)
  2069.         {
  2070.             m_flBladeSpeed = 100;
  2071.         }
  2072.  
  2073.         // blend through blades, blades+blur, blur
  2074.         if (m_flBladeSpeed < 20)
  2075.         {
  2076.             SetBodygroup( MANHACK_BODYGROUP_BLADE, MANHACK_BODYGROUP_ON );
  2077.             SetBodygroup( MANHACK_BODYGROUP_BLUR, MANHACK_BODYGROUP_OFF );
  2078.         }
  2079.         else if (m_flBladeSpeed < 40)
  2080.         {
  2081.             SetBodygroup( MANHACK_BODYGROUP_BLADE, MANHACK_BODYGROUP_ON );
  2082.             SetBodygroup( MANHACK_BODYGROUP_BLUR, MANHACK_BODYGROUP_ON );
  2083.         }
  2084.         else
  2085.         {
  2086.             SetBodygroup( MANHACK_BODYGROUP_BLADE, MANHACK_BODYGROUP_OFF );
  2087.             SetBodygroup( MANHACK_BODYGROUP_BLUR, MANHACK_BODYGROUP_ON );
  2088.         }
  2089.  
  2090.         m_flPlaybackRate = m_flBladeSpeed / 100.0;
  2091.     }
  2092.     else
  2093.     {
  2094.         m_flBladeSpeed = 0.0;
  2095.     }
  2096. }
  2097.  
  2098.  
  2099. //-----------------------------------------------------------------------------
  2100. // Purpose: Smokes and sparks, exploding periodically. Eventually it goes away.
  2101. //-----------------------------------------------------------------------------
  2102. void CNPC_Manhack::MoveExecute_Dead(float flInterval)
  2103. {
  2104.     if( GetWaterLevel() > 0 )
  2105.     {
  2106.         // No movement if sinking in water.
  2107.         return;
  2108.     }
  2109.  
  2110.     // Periodically emit smoke.
  2111.     if (gpGlobals->curtime > m_fSmokeTime && GetWaterLevel() == 0)
  2112.     {
  2113. //      UTIL_Smoke(GetAbsOrigin(), random->RandomInt(10, 15), 10);
  2114.         m_fSmokeTime = gpGlobals->curtime + random->RandomFloat( 0.1, 0.3);
  2115.     }
  2116.  
  2117.     // Periodically emit sparks.
  2118.     if (gpGlobals->curtime > m_fSparkTime)
  2119.     {
  2120.         g_pEffects->Sparks( GetAbsOrigin() );
  2121.         m_fSparkTime = gpGlobals->curtime + random->RandomFloat(0.1, 0.3);
  2122.     }
  2123.  
  2124.     Vector newVelocity = GetCurrentVelocity();
  2125.  
  2126.     // accelerate faster and faster when dying
  2127.     newVelocity = newVelocity + (newVelocity * 1.5 * flInterval );
  2128.  
  2129.     // Lose lift
  2130.     newVelocity.z -= 0.02*flInterval*(sv_gravity.GetFloat());
  2131.  
  2132.     // ----------------------------------------------------------------------------------------
  2133.     // Add in any forced velocity
  2134.     // ----------------------------------------------------------------------------------------
  2135.     newVelocity += m_vForceVelocity;
  2136.     SetCurrentVelocity( newVelocity );
  2137.     m_vForceVelocity = vec3_origin;
  2138.  
  2139.  
  2140.     // Lots of noise!! Out of control!
  2141.     AddNoiseToVelocity( 5.0 );
  2142.  
  2143.  
  2144.     // ----------------------
  2145.     // Limit overall speed
  2146.     // ----------------------
  2147.     LimitSpeed( -1, MANHACK_MAX_SPEED * 2.0 );
  2148.  
  2149.     QAngle angles = GetLocalAngles();
  2150.  
  2151.     // ------------------------------------------
  2152.     // If I'm dying, add random banking noise
  2153.     // ------------------------------------------
  2154.     angles.x += -20+(random->RandomInt(0,40));
  2155.     angles.z += -20+(random->RandomInt(0,40));
  2156.  
  2157.     CheckCollisions(flInterval);
  2158.     PlayFlySound();
  2159.  
  2160.     // SetLocalAngles( angles );
  2161.  
  2162.     WalkMove( GetCurrentVelocity() * flInterval,MASK_NPCSOLID );
  2163. }
  2164.  
  2165.  
  2166. //-----------------------------------------------------------------------------
  2167. // Purpose:
  2168. //-----------------------------------------------------------------------------
  2169. void CNPC_Manhack::Precache(void)
  2170. {
  2171.     //
  2172.     // Model.
  2173.     //
  2174.     PrecacheModel ("modelnames[ random->RandomInt( 0, ARRAYSIZE(modelnames) - 1 ) ]");
  2175.  
  2176.     static const char* modelnames[] = {
  2177.     "models/hacks/watermelon.mdl", //0
  2178.     "models/manhack.mdl", //1
  2179.     };
  2180.  
  2181.     PrecacheModel( MANHACK_GLOW_SPRITE );
  2182.     PropBreakablePrecacheAll( MAKE_STRING("models/manhack.mdl") );
  2183.    
  2184.     PrecacheScriptSound( "NPC_Manhack.Die" );
  2185.     PrecacheScriptSound( "NPC_Manhack.Bat" );
  2186.     PrecacheScriptSound( "NPC_Manhack.Grind" );
  2187.     PrecacheScriptSound( "NPC_Manhack.Slice" );
  2188.     PrecacheScriptSound( "NPC_Manhack.EngineNoise" );
  2189.     PrecacheScriptSound( "NPC_Manhack.Unpack" );
  2190.     PrecacheScriptSound( "NPC_Manhack.ChargeAnnounce" );
  2191.     PrecacheScriptSound( "NPC_Manhack.ChargeEnd" );
  2192.     PrecacheScriptSound( "NPC_Manhack.Stunned" );
  2193.  
  2194.     // Sounds used on Client:
  2195.     PrecacheScriptSound( "NPC_Manhack.EngineSound1" );
  2196.     PrecacheScriptSound( "NPC_Manhack.EngineSound2"  );
  2197.     PrecacheScriptSound( "NPC_Manhack.BladeSound" );
  2198.  
  2199.     BaseClass::Precache();
  2200. }
  2201.  
  2202. //-----------------------------------------------------------------------------
  2203. // Purpose:
  2204. // Input  :
  2205. // Output :
  2206. //-----------------------------------------------------------------------------
  2207. void CNPC_Manhack::GatherEnemyConditions( CBaseEntity *pEnemy )
  2208. {
  2209.     // The Manhack "regroups" when its in attack range but to
  2210.     // far above or below its enemy.  Set the start attack
  2211.     // condition if we are far enough away from the enemy
  2212.     // or at the correct height
  2213.  
  2214.     // Don't bother with Z if the enemy is in a vehicle
  2215.     float fl2DDist = 60.0f;
  2216.     float flZDist = 12.0f;
  2217.  
  2218.     if ( GetEnemy()->IsPlayer() && assert_cast< CBasePlayer * >(GetEnemy())->IsInAVehicle() )
  2219.     {
  2220.         flZDist = 24.0f;
  2221.     }
  2222.  
  2223.     if ((GetAbsOrigin() - pEnemy->GetAbsOrigin()).Length2D() > fl2DDist)
  2224.     {
  2225.         SetCondition(COND_MANHACK_START_ATTACK);
  2226.     }
  2227.     else
  2228.     {
  2229.         float targetZ   = pEnemy->EyePosition().z;
  2230.         if (fabs(GetAbsOrigin().z - targetZ) < flZDist)
  2231.         {
  2232.             SetCondition(COND_MANHACK_START_ATTACK);
  2233.         }
  2234.     }
  2235.     BaseClass::GatherEnemyConditions(pEnemy);
  2236. }
  2237.  
  2238.  
  2239. //-----------------------------------------------------------------------------
  2240. // Purpose: For innate melee attack
  2241. // Input  :
  2242. // Output :
  2243. //-----------------------------------------------------------------------------
  2244. int CNPC_Manhack::MeleeAttack1Conditions( float flDot, float flDist )
  2245. {
  2246.     if ( GetEnemy() == NULL )
  2247.         return COND_NONE;
  2248.  
  2249.     //TODO: We could also decide if we want to back up here
  2250.     if ( m_flNextBurstTime > gpGlobals->curtime )
  2251.         return COND_NONE;
  2252.  
  2253.     float flMaxDist = 45;
  2254.     float flMinDist = 24;
  2255.     bool bEnemyInVehicle = GetEnemy()->IsPlayer() && assert_cast< CBasePlayer * >(GetEnemy())->IsInAVehicle();
  2256.     if ( GetEnemy()->IsPlayer() && assert_cast< CBasePlayer * >(GetEnemy())->IsInAVehicle() )
  2257.     {
  2258.         flMinDist = 0;
  2259.         flMaxDist = 200.0f;
  2260.     }
  2261.  
  2262.     if (flDist > flMaxDist)
  2263.     {
  2264.         return COND_TOO_FAR_TO_ATTACK;
  2265.     }
  2266.  
  2267.     if (flDist < flMinDist)
  2268.     {
  2269.         return COND_TOO_CLOSE_TO_ATTACK;
  2270.     }
  2271.  
  2272.     // Check our current velocity and speed, if it's too far off, we need to settle
  2273.  
  2274.     // Don't bother with Z if the enemy is in a vehicle
  2275.     if ( bEnemyInVehicle )
  2276.     {
  2277.         return COND_CAN_MELEE_ATTACK1;
  2278.     }
  2279.  
  2280.     // Assume the this check is in regards to my current enemy
  2281.     // for the Manhacks spetial condition
  2282.     float deltaZ = GetAbsOrigin().z - GetEnemy()->EyePosition().z;
  2283.     if ( (deltaZ > 12.0f) || (deltaZ < -24.0f) )
  2284.     {
  2285.         return COND_TOO_CLOSE_TO_ATTACK;
  2286.     }
  2287.  
  2288.     return COND_CAN_MELEE_ATTACK1;
  2289. }
  2290.  
  2291.  
  2292. //-----------------------------------------------------------------------------
  2293. // Purpose:
  2294. // Input  : *pTask -
  2295. //-----------------------------------------------------------------------------
  2296. void CNPC_Manhack::RunTask( const Task_t *pTask )
  2297. {
  2298.     switch ( pTask->iTask )
  2299.     {
  2300.         // Override this task so we go for the enemy at eye level
  2301.     case TASK_MANHACK_HOVER:
  2302.         {
  2303.             break;
  2304.         }
  2305.  
  2306.     // If my enemy has moved significantly, update my path
  2307.     case TASK_WAIT_FOR_MOVEMENT:
  2308.         {
  2309.             CBaseEntity *pEnemy = GetEnemy();
  2310.             if (pEnemy &&
  2311.                 (GetCurSchedule()->GetId() == SCHED_CHASE_ENEMY) &&
  2312.                 GetNavigator()->IsGoalActive() )
  2313.             {
  2314.                 Vector vecEnemyPosition;
  2315.                 vecEnemyPosition = pEnemy->EyePosition();
  2316.                 if ( GetNavigator()->GetGoalPos().DistToSqr(vecEnemyPosition) > 40 * 40 )
  2317.                 {
  2318.                     GetNavigator()->UpdateGoalPos( vecEnemyPosition );
  2319.                 }
  2320.             }              
  2321.             BaseClass::RunTask(pTask);
  2322.             break;
  2323.         }
  2324.  
  2325.     case TASK_MANHACK_MOVEAT_SAVEPOSITION:
  2326.         {
  2327.             // do the movement thingy
  2328.  
  2329. //          NDebugOverlay::Line( GetAbsOrigin(), m_vSavePosition, 0, 255, 0, true, 0.1);
  2330.  
  2331.             Vector dir = (m_vSavePosition - GetAbsOrigin());
  2332.             float dist = VectorNormalize( dir );
  2333.             float t = m_fSwarmMoveTime - gpGlobals->curtime;
  2334.  
  2335.             if (t < 0.1)
  2336.             {
  2337.                 if (dist > 256)
  2338.                 {
  2339.                     TaskFail( FAIL_NO_ROUTE );
  2340.                 }
  2341.                 else
  2342.                 {
  2343.                     TaskComplete();
  2344.                 }
  2345.             }
  2346.             else if (dist < 64)
  2347.             {
  2348.                 m_vSwarmMoveTarget = GetAbsOrigin() - Vector( -dir.y, dir.x, 0 ) * 4;
  2349.             }
  2350.             else
  2351.             {
  2352.                 m_vSwarmMoveTarget = GetAbsOrigin() + dir * 10;
  2353.             }
  2354.             break;
  2355.         }
  2356.  
  2357.     default:
  2358.         {
  2359.             BaseClass::RunTask(pTask);
  2360.         }
  2361.     }
  2362. }
  2363.  
  2364. //-----------------------------------------------------------------------------
  2365. // Purpose:
  2366. //-----------------------------------------------------------------------------
  2367. void CNPC_Manhack::Spawn(void)
  2368. {
  2369.     Precache();
  2370.  
  2371. #ifdef _XBOX
  2372.     // Always fade the corpse
  2373.     AddSpawnFlags( SF_NPC_FADE_CORPSE );
  2374. #endif // _XBOX
  2375.  
  2376.     SetModel ("modelnames[ random->RandomInt( 0, ARRAYSIZE(modelnames) - 1 ) ]");
  2377.  
  2378.     SetHullType(HULL_TINY_CENTERED);
  2379.     SetHullSizeNormal();
  2380.  
  2381.     SetSolid( SOLID_BBOX );
  2382.     AddSolidFlags( FSOLID_NOT_STANDABLE );
  2383.  
  2384.     if ( HasSpawnFlags( SF_MANHACK_CARRIED ) )
  2385.     {
  2386.         AddSolidFlags( FSOLID_NOT_SOLID );
  2387.         SetMoveType( MOVETYPE_NONE );
  2388.     }
  2389.     else
  2390.     {
  2391.         SetMoveType( MOVETYPE_VPHYSICS );
  2392.     }
  2393.  
  2394.     m_iHealth           = sk_manhack_health.GetFloat();
  2395.     SetViewOffset( Vector(0, 0, 10) );      // Position of the eyes relative to NPC's origin.
  2396.     m_flFieldOfView     = VIEW_FIELD_FULL;
  2397.     m_NPCState          = NPC_STATE_NONE;
  2398.  
  2399.     if ( m_spawnflags & SF_MANHACK_USE_AIR_NODES)
  2400.     {
  2401.         SetNavType(NAV_FLY);
  2402.     }
  2403.     else
  2404.     {
  2405.         SetNavType(NAV_GROUND);
  2406.     }
  2407.          
  2408.     AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL );
  2409.     AddEffects( EF_NOSHADOW );
  2410.  
  2411.     SetBloodColor( DONT_BLEED );
  2412.     SetCurrentVelocity( vec3_origin );
  2413.     m_vForceVelocity.Init();
  2414.     m_vCurrentBanking.Init();
  2415.     m_vTargetBanking.Init();
  2416.  
  2417.     m_flNextBurstTime   = gpGlobals->curtime;
  2418.  
  2419.     CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_MOVE_FLY | bits_CAP_SQUAD );
  2420.  
  2421.     m_flNextEngineSoundTime     = gpGlobals->curtime;
  2422.     m_flWaterSuspendTime        = gpGlobals->curtime;
  2423.     m_flEngineStallTime         = gpGlobals->curtime;
  2424.     m_fForceMoveTime            = gpGlobals->curtime;
  2425.     m_vForceMoveTarget          = vec3_origin;
  2426.     m_fSwarmMoveTime            = gpGlobals->curtime;
  2427.     m_vSwarmMoveTarget          = vec3_origin;
  2428.     m_nLastSpinSound            = -1;
  2429.  
  2430.     m_fSmokeTime        = 0;
  2431.     m_fSparkTime        = 0;
  2432.  
  2433.     // Set the noise mod to huge numbers right now, in case this manhack starts out waiting for a script
  2434.     // for instance, we don't want him to bob whilst he's waiting for a script. This allows designers
  2435.     // to 'hide' manhacks in small places. (sjb)
  2436.     SetNoiseMod( MANHACK_NOISEMOD_HIDE, MANHACK_NOISEMOD_HIDE, MANHACK_NOISEMOD_HIDE );
  2437.  
  2438.     // Start out with full power!
  2439.     m_fEnginePowerScale = GetMaxEnginePower();
  2440.    
  2441.     // find panels
  2442.     m_iPanel1 = LookupPoseParameter( "Panel1" );
  2443.     m_iPanel2 = LookupPoseParameter( "Panel2" );
  2444.     m_iPanel3 = LookupPoseParameter( "Panel3" );
  2445.     m_iPanel4 = LookupPoseParameter( "Panel4" );
  2446.  
  2447.     m_fHeadYaw          = 0;
  2448.  
  2449.     NPCInit();
  2450.  
  2451.     // Manhacks are designed to slam into things, so don't take much damage from it!
  2452.     SetImpactEnergyScale( 0.001 );
  2453.  
  2454.     // Manhacks get 30 seconds worth of free knowledge.
  2455.     GetEnemies()->SetFreeKnowledgeDuration( 30.0 );
  2456.    
  2457.     // don't be an NPC, we want to collide with debris stuff
  2458.     SetCollisionGroup( COLLISION_GROUP_NONE );
  2459.  
  2460.     m_bHeld = false;
  2461.     m_bHackedByAlyx = false;
  2462.     StopLoitering();
  2463. }
  2464.  
  2465. //-----------------------------------------------------------------------------
  2466. // Purpose:
  2467. //-----------------------------------------------------------------------------
  2468. void CNPC_Manhack::StartEye( void )
  2469. {
  2470.     //Create our Eye sprite
  2471.     if ( m_pEyeGlow == NULL )
  2472.     {
  2473.         m_pEyeGlow = CSprite::SpriteCreate( MANHACK_GLOW_SPRITE, GetLocalOrigin(), false );
  2474.         m_pEyeGlow->SetAttachment( this, LookupAttachment( "Eye" ) );
  2475.        
  2476.         if( m_bHackedByAlyx )
  2477.         {
  2478.             m_pEyeGlow->SetTransparency( kRenderTransAdd, 0, 255, 0, 128, kRenderFxNoDissipation );
  2479.             m_pEyeGlow->SetColor( 0, 255, 0 );
  2480.         }
  2481.         else
  2482.         {
  2483.             m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation );
  2484.             m_pEyeGlow->SetColor( 255, 0, 0 );
  2485.         }
  2486.  
  2487.         m_pEyeGlow->SetBrightness( 164, 0.1f );
  2488.         m_pEyeGlow->SetScale( 0.25f, 0.1f );
  2489.         m_pEyeGlow->SetAsTemporary();
  2490.     }
  2491.  
  2492.     //Create our light sprite
  2493.     if ( m_pLightGlow == NULL )
  2494.     {
  2495.         m_pLightGlow = CSprite::SpriteCreate( MANHACK_GLOW_SPRITE, GetLocalOrigin(), false );
  2496.         m_pLightGlow->SetAttachment( this, LookupAttachment( "Light" ) );
  2497.  
  2498.         if( m_bHackedByAlyx )
  2499.         {
  2500.             m_pLightGlow->SetTransparency( kRenderTransAdd, 0, 255, 0, 128, kRenderFxNoDissipation );
  2501.             m_pLightGlow->SetColor( 0, 255, 0 );
  2502.         }
  2503.         else
  2504.         {
  2505.             m_pLightGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation );
  2506.             m_pLightGlow->SetColor( 255, 0, 0 );
  2507.         }
  2508.  
  2509.         m_pLightGlow->SetBrightness( 164, 0.1f );
  2510.         m_pLightGlow->SetScale( 0.25f, 0.1f );
  2511.         m_pLightGlow->SetAsTemporary();
  2512.     }
  2513. }
  2514.  
  2515. //-----------------------------------------------------------------------------
  2516.  
  2517. void CNPC_Manhack::Activate()
  2518. {
  2519.     BaseClass::Activate();
  2520.  
  2521.     if ( IsAlive() )
  2522.     {
  2523.         StartEye();
  2524.     }
  2525. }
  2526.  
  2527. //-----------------------------------------------------------------------------
  2528. // Purpose: Get the engine sound started. Unless we're not supposed to have it on yet!
  2529. //-----------------------------------------------------------------------------
  2530. void CNPC_Manhack::PostNPCInit( void )
  2531. {
  2532.     // SetAbsVelocity( vec3_origin );
  2533.     m_bBladesActive = (m_spawnflags & (SF_MANHACK_PACKED_UP|SF_MANHACK_CARRIED)) ? false : true;
  2534.     BladesInit();
  2535. }
  2536.  
  2537. void CNPC_Manhack::BladesInit()
  2538. {
  2539.     if( !m_bBladesActive )
  2540.     {
  2541.         // manhack is packed up, so has no power of its own.
  2542.         // don't start the engine sounds.
  2543.         // make us fall a little slower than we should, for visual's sake
  2544.         SetGravity( UTIL_ScaleForGravity( 400 ) );
  2545.  
  2546.         SetActivity( ACT_IDLE );
  2547.     }
  2548.     else
  2549.     {
  2550.         bool engineSound = (m_spawnflags & SF_NPC_GAG) ? false : true;
  2551.         StartEngine( engineSound );
  2552.         SetActivity( ACT_FLY );
  2553.     }
  2554. }
  2555.  
  2556.  
  2557. //-----------------------------------------------------------------------------
  2558. // Crank up the engine!
  2559. //-----------------------------------------------------------------------------
  2560. void CNPC_Manhack::StartEngine( bool fStartSound )
  2561. {
  2562.     if( fStartSound )
  2563.     {
  2564.         SoundInit();
  2565.     }
  2566.  
  2567.     // Make the blade appear.
  2568.     SetBodygroup( 1, 1 );
  2569.  
  2570.     // Pop up a little if falling fast!
  2571.     Vector vecVelocity;
  2572.     GetVelocity( &vecVelocity, NULL );
  2573.     if( ( m_spawnflags & SF_MANHACK_PACKED_UP ) && vecVelocity.z < 0 )
  2574.     {
  2575.         // DevMsg(" POP UP \n" );
  2576.         // ApplyAbsVelocityImpulse( Vector(0,0,-vecVelocity.z*0.75) );
  2577.     }
  2578.  
  2579.     // Under powered flight now.
  2580.     // SetMoveType( MOVETYPE_STEP );
  2581.     // SetGravity( MANHACK_GRAVITY );
  2582.     AddFlag( FL_FLY );
  2583. }
  2584.  
  2585.  
  2586. //-----------------------------------------------------------------------------
  2587. // Purpose: Start the manhack's engine sound.
  2588. //-----------------------------------------------------------------------------
  2589. void CNPC_Manhack::SoundInit( void )
  2590. {
  2591.     m_nEnginePitch1 = MANHACK_MIN_PITCH1;
  2592.     m_flEnginePitch1Time = gpGlobals->curtime;
  2593.     m_nEnginePitch2 = MANHACK_MIN_PITCH2;
  2594.     m_flEnginePitch2Time = gpGlobals->curtime;
  2595. }
  2596.  
  2597.  
  2598. //-----------------------------------------------------------------------------
  2599. // Purpose:
  2600. //-----------------------------------------------------------------------------
  2601. void CNPC_Manhack::StopLoopingSounds(void)
  2602. {
  2603.     BaseClass::StopLoopingSounds();
  2604.     m_nEnginePitch1 = -1;
  2605.     m_flEnginePitch1Time = gpGlobals->curtime;
  2606.     m_nEnginePitch2 = -1;
  2607.     m_flEnginePitch2Time = gpGlobals->curtime;
  2608. }
  2609.  
  2610.  
  2611. //-----------------------------------------------------------------------------
  2612. // Purpose:
  2613. // Input  : pTask -
  2614. //-----------------------------------------------------------------------------
  2615. void CNPC_Manhack::StartTask( const Task_t *pTask )
  2616. {
  2617.     switch (pTask->iTask)
  2618.     {  
  2619.     case TASK_MANHACK_UNPACK:
  2620.         {
  2621.             // Just play a sound for now.
  2622.             EmitSound( "NPC_Manhack.Unpack" );
  2623.  
  2624.             TaskComplete();
  2625.         }
  2626.         break;
  2627.  
  2628.     case TASK_MANHACK_HOVER:
  2629.         break;
  2630.  
  2631.     case TASK_MOVE_TO_TARGET_RANGE:
  2632.     case TASK_GET_PATH_TO_GOAL:
  2633.     case TASK_GET_PATH_TO_ENEMY_LKP:
  2634.     case TASK_GET_PATH_TO_PLAYER:
  2635.         {
  2636.             BaseClass::StartTask( pTask );
  2637.             /*
  2638.             // FIXME: why were these tasks considered bad?
  2639.             _asm
  2640.             {
  2641.                 int 3;
  2642.                 int 5;
  2643.             }
  2644.             */
  2645.         }
  2646.         break;
  2647.  
  2648.     case TASK_FACE_IDEAL:
  2649.         {
  2650.             // this shouldn't ever happen, but if it does, don't choke
  2651.             TaskComplete();
  2652.         }
  2653.         break;
  2654.  
  2655.     case TASK_GET_PATH_TO_ENEMY:
  2656.         {
  2657.             if (IsUnreachable(GetEnemy()))
  2658.             {
  2659.                 TaskFail(FAIL_NO_ROUTE);
  2660.                 return;
  2661.             }
  2662.  
  2663.             CBaseEntity *pEnemy = GetEnemy();
  2664.  
  2665.             if ( pEnemy == NULL )
  2666.             {
  2667.                 TaskFail(FAIL_NO_ENEMY);
  2668.                 return;
  2669.             }
  2670.                        
  2671.             if ( GetNavigator()->SetGoal( GOALTYPE_ENEMY ) )
  2672.             {
  2673.                 TaskComplete();
  2674.             }
  2675.             else
  2676.             {
  2677.                 // no way to get there =(
  2678.                 DevWarning( 2, "GetPathToEnemy failed!!\n" );
  2679.                 RememberUnreachable(GetEnemy());
  2680.                 TaskFail(FAIL_NO_ROUTE);
  2681.             }
  2682.             break;
  2683.         }
  2684.         break;
  2685.  
  2686.     case TASK_GET_PATH_TO_TARGET:
  2687.         // DevMsg("TARGET\n");
  2688.         BaseClass::StartTask( pTask );
  2689.         break;
  2690.  
  2691.     case TASK_MANHACK_FIND_SQUAD_CENTER:
  2692.         {
  2693.             if (!m_pSquad)
  2694.             {
  2695.                 m_vSavePosition = GetAbsOrigin();
  2696.                 TaskComplete();
  2697.                 break;
  2698.             }
  2699.  
  2700.             // calc center of squad
  2701.             int count = 0;
  2702.             m_vSavePosition = Vector( 0, 0, 0 );
  2703.  
  2704.             // give attacking members more influence
  2705.             AISquadIter_t iter;
  2706.             for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  2707.             {
  2708.                 if (pSquadMember->HasStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ))
  2709.                 {
  2710.                     m_vSavePosition += pSquadMember->GetAbsOrigin() * 10;
  2711.                     count += 10;
  2712.                 }
  2713.                 else
  2714.                 {
  2715.                     m_vSavePosition += pSquadMember->GetAbsOrigin();
  2716.                     count++;
  2717.                 }
  2718.             }
  2719.  
  2720.             // pull towards enemy
  2721.             if (GetEnemy() != NULL)
  2722.             {
  2723.                 m_vSavePosition += GetEnemyLKP() * 4;
  2724.                 count += 4;
  2725.             }
  2726.  
  2727.             Assert( count != 0 );
  2728.             m_vSavePosition = m_vSavePosition * (1.0 / count);
  2729.  
  2730.             TaskComplete();
  2731.         }
  2732.         break;
  2733.  
  2734.     case TASK_MANHACK_FIND_SQUAD_MEMBER:
  2735.         {
  2736.             if (m_pSquad)
  2737.             {
  2738.                 CAI_BaseNPC *pSquadMember = m_pSquad->GetAnyMember();
  2739.                 m_vSavePosition = pSquadMember->GetAbsOrigin();
  2740.  
  2741.                 // find attacking members
  2742.                 AISquadIter_t iter;
  2743.                 for (pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  2744.                 {
  2745.                     // are they attacking?
  2746.                     if (pSquadMember->HasStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ))
  2747.                     {
  2748.                         m_vSavePosition = pSquadMember->GetAbsOrigin();
  2749.                         break;
  2750.                     }
  2751.                     // do they have a goal?
  2752.                     if (pSquadMember->GetNavigator()->IsGoalActive())
  2753.                     {
  2754.                         m_vSavePosition = pSquadMember->GetAbsOrigin();
  2755.                         break;
  2756.                     }
  2757.                 }
  2758.             }
  2759.             else
  2760.             {
  2761.                 m_vSavePosition = GetAbsOrigin();
  2762.             }
  2763.  
  2764.             TaskComplete();
  2765.         }
  2766.         break;
  2767.  
  2768.     case TASK_MANHACK_MOVEAT_SAVEPOSITION:
  2769.         {
  2770.             trace_t tr;
  2771.             AI_TraceLine( GetAbsOrigin(), m_vSavePosition, MASK_NPCWORLDSTATIC, this, COLLISION_GROUP_NONE, &tr );
  2772.             if (tr.DidHitWorld())
  2773.             {
  2774.                 TaskFail( FAIL_NO_ROUTE );
  2775.             }
  2776.             else
  2777.             {
  2778.                 m_fSwarmMoveTime = gpGlobals->curtime + RandomFloat( pTask->flTaskData * 0.8, pTask->flTaskData * 1.2 );
  2779.             }
  2780.         }
  2781.         break;
  2782.  
  2783.     default:
  2784.         BaseClass::StartTask(pTask);
  2785.         break;
  2786.     }
  2787. }
  2788.  
  2789.  
  2790. //-----------------------------------------------------------------------------
  2791. // Purpose:
  2792. //-----------------------------------------------------------------------------
  2793. void CNPC_Manhack::UpdateOnRemove( void )
  2794. {
  2795.     DestroySmokeTrail();
  2796.     KillSprites( 0.0 );
  2797.     BaseClass::UpdateOnRemove();
  2798. }
  2799.  
  2800.  
  2801. //-----------------------------------------------------------------------------
  2802. // Purpose:  This is a generic function (to be implemented by sub-classes) to
  2803. //           handle specific interactions between different types of characters
  2804. //           (For example the barnacle grabbing an NPC)
  2805. // Input  :  Constant for the type of interaction
  2806. // Output :  true  - if sub-class has a response for the interaction
  2807. //           false - if sub-class has no response
  2808. //-----------------------------------------------------------------------------
  2809. bool CNPC_Manhack::HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt)
  2810. {
  2811.     if (interactionType == g_interactionVortigauntClaw)
  2812.     {
  2813.         // Freeze so vortigaunt and hit me easier
  2814.  
  2815.         m_vForceMoveTarget.x = ((Vector *)data)->x;
  2816.         m_vForceMoveTarget.y = ((Vector *)data)->y;
  2817.         m_vForceMoveTarget.z = ((Vector *)data)->z;
  2818.         m_fForceMoveTime   = gpGlobals->curtime + 2.0;
  2819.         return false;
  2820.     }
  2821.  
  2822.     return false;
  2823. }
  2824.  
  2825. //-----------------------------------------------------------------------------
  2826. // Purpose:
  2827. // Output : float
  2828. //-----------------------------------------------------------------------------
  2829. float CNPC_Manhack::ManhackMaxSpeed( void )
  2830. {
  2831.     if( m_flWaterSuspendTime > gpGlobals->curtime )
  2832.     {
  2833.         // Slower in water!
  2834.         return MANHACK_MAX_SPEED * 0.1;
  2835.     }
  2836.  
  2837.     if ( HasPhysicsAttacker( MANHACK_SMASH_TIME ) )
  2838.     {
  2839.         return MANHACK_NPC_BURST_SPEED;
  2840.     }
  2841.  
  2842.     return MANHACK_MAX_SPEED;
  2843. }
  2844.  
  2845.  
  2846.  
  2847. //-----------------------------------------------------------------------------
  2848. // Purpose:
  2849. // Output :
  2850. //-----------------------------------------------------------------------------
  2851. void CNPC_Manhack::ClampMotorForces( Vector &linear, AngularImpulse &angular )
  2852. {
  2853.     float scale = m_flBladeSpeed / 100.0;
  2854.  
  2855.     // Msg("%.0f %.0f %.0f\n", linear.x, linear.y, linear.z );
  2856.  
  2857.     float fscale = 3000 * scale;
  2858.  
  2859.     if ( m_flEngineStallTime > gpGlobals->curtime )
  2860.     {
  2861.         linear.x = 0.0f;
  2862.         linear.y = 0.0f;
  2863.         linear.z = clamp( linear.z, -fscale, fscale < 1200 ? 1200 : fscale );
  2864.     }
  2865.     else
  2866.     {
  2867.         // limit reaction forces
  2868.         linear.x = clamp( linear.x, -fscale, fscale );
  2869.         linear.y = clamp( linear.y, -fscale, fscale );
  2870.         linear.z = clamp( linear.z, -fscale, fscale < 1200 ? 1200 : fscale );
  2871.     }
  2872.  
  2873.     angular.x *= scale;
  2874.     angular.y *= scale;
  2875.     angular.z *= scale;
  2876. }
  2877.  
  2878. //-----------------------------------------------------------------------------
  2879. // Purpose:
  2880. //-----------------------------------------------------------------------------
  2881. void CNPC_Manhack::KillSprites( float flDelay )
  2882. {
  2883.     if( m_pEyeGlow )
  2884.     {
  2885.         m_pEyeGlow->FadeAndDie( flDelay );
  2886.         m_pEyeGlow = NULL;
  2887.     }
  2888.  
  2889.     if( m_pLightGlow )
  2890.     {
  2891.         m_pLightGlow->FadeAndDie( flDelay );
  2892.         m_pLightGlow = NULL;
  2893.     }
  2894.  
  2895.     // Re-enable for light trails
  2896.     /*
  2897.     if ( m_hLightTrail )
  2898.     {
  2899.         m_hLightTrail->FadeAndDie( flDelay );
  2900.         m_hLightTrail = NULL;
  2901.     }
  2902.     */
  2903. }
  2904.  
  2905. //-----------------------------------------------------------------------------
  2906. // Purpose: Tests whether we're above the target's feet but also below their top
  2907. // Input  : *pTarget - who we're testing against
  2908. //-----------------------------------------------------------------------------
  2909. bool CNPC_Manhack::IsInEffectiveTargetZone( CBaseEntity *pTarget )
  2910. {
  2911.     Vector  vecMaxPos, vecMinPos;
  2912.     float   ourHeight = WorldSpaceCenter().z;
  2913.  
  2914.     // If the enemy is in a vehicle, we need to get those bounds
  2915.     if ( pTarget && pTarget->IsPlayer() && assert_cast< CBasePlayer * >(pTarget)->IsInAVehicle() )
  2916.     {
  2917.         CBaseEntity *pVehicle = assert_cast< CBasePlayer * >(pTarget)->GetVehicleEntity();
  2918.         pVehicle->CollisionProp()->NormalizedToWorldSpace( Vector(0.0f,0.0f,1.0f), &vecMaxPos );
  2919.         pVehicle->CollisionProp()->NormalizedToWorldSpace( Vector(0.0f,0.0f,0.0f), &vecMinPos );
  2920.    
  2921.         if ( ourHeight > vecMinPos.z && ourHeight < vecMaxPos.z )
  2922.             return true;
  2923.  
  2924.         return false;
  2925.     }
  2926.    
  2927.     // Get the enemies top and bottom point
  2928.     pTarget->CollisionProp()->NormalizedToWorldSpace( Vector(0.0f,0.0f,1.0f), &vecMaxPos );
  2929. #ifdef _XBOX
  2930.     pTarget->CollisionProp()->NormalizedToWorldSpace( Vector(0.0f,0.0f,0.5f), &vecMinPos ); // Only half the body is valid
  2931. #else
  2932.     pTarget->CollisionProp()->NormalizedToWorldSpace( Vector(0.0f,0.0f,0.0f), &vecMinPos );
  2933. #endif // _XBOX
  2934.     // See if we're within that range
  2935.     if ( ourHeight > vecMinPos.z && ourHeight < vecMaxPos.z )
  2936.         return true;
  2937.  
  2938.     return false;
  2939. }
  2940.  
  2941. //-----------------------------------------------------------------------------
  2942. // Purpose:
  2943. // Input  : *pEnemy -
  2944. //          &chasePosition -
  2945. //          &tolerance -
  2946. //-----------------------------------------------------------------------------
  2947. void CNPC_Manhack::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition )
  2948. {
  2949.     if ( pEnemy && pEnemy->IsPlayer() && assert_cast< CBasePlayer * >(pEnemy)->IsInAVehicle() )
  2950.     {
  2951.         Vector vecNewPos;
  2952.         CBaseEntity *pVehicle = assert_cast< CBasePlayer * >(pEnemy)->GetVehicleEntity();
  2953.         pVehicle->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,0.5f), &vecNewPos );
  2954.         chasePosition.z = vecNewPos.z;
  2955.     }
  2956.     else
  2957.     {
  2958.         Vector vecTarget;
  2959.         pEnemy->CollisionProp()->NormalizedToCollisionSpace( Vector(0,0,0.75f), &vecTarget );
  2960.         chasePosition.z += vecTarget.z;
  2961.     }
  2962. }
  2963.  
  2964. float CNPC_Manhack::GetDefaultNavGoalTolerance()
  2965. {
  2966.     return GetHullWidth();
  2967. }
  2968.  
  2969. //-----------------------------------------------------------------------------
  2970. // Purpose: Input that disables the manhack's swarm behavior
  2971. //-----------------------------------------------------------------------------
  2972. void CNPC_Manhack::InputDisableSwarm( inputdata_t &inputdata )
  2973. {
  2974.     m_bDoSwarmBehavior = false;
  2975. }
  2976.  
  2977. //-----------------------------------------------------------------------------
  2978. // Purpose:
  2979. // Input  : &inputdata -
  2980. //-----------------------------------------------------------------------------
  2981. void CNPC_Manhack::InputUnpack( inputdata_t &inputdata )
  2982. {
  2983.     if ( HasSpawnFlags( SF_MANHACK_PACKED_UP ) == false )
  2984.         return;
  2985.  
  2986.     SetCondition( COND_LIGHT_DAMAGE );
  2987. }
  2988.  
  2989. //-----------------------------------------------------------------------------
  2990. // Purpose:
  2991. // Input  : *pPhysGunUser -
  2992. //          reason -
  2993. //-----------------------------------------------------------------------------
  2994. void CNPC_Manhack::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  2995. {
  2996.     m_hPhysicsAttacker = pPhysGunUser;
  2997.     m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  2998.  
  2999.     if ( reason == PUNTED_BY_CANNON )
  3000.     {
  3001.         StopLoitering();
  3002.  
  3003.         m_bHeld = false;
  3004.  
  3005.         // There's about to be a massive change in velocity.
  3006.         // Think immediately so we can do our slice traces, etc.
  3007.         SetNextThink( gpGlobals->curtime + 0.01f );
  3008.  
  3009.         // Stall our engine for awhile
  3010.         m_flEngineStallTime = gpGlobals->curtime + 2.0f;
  3011.         SetEyeState( MANHACK_EYE_STATE_STUNNED );
  3012.     }
  3013.     else
  3014.     {
  3015.         // Suppress collisions between the manhack and the player; we're currently bumping
  3016.         // almost certainly because it's not purely a physics object.
  3017.         SetOwnerEntity( pPhysGunUser );
  3018.         m_bHeld = true;
  3019.     }
  3020. }
  3021.  
  3022.  
  3023. //-----------------------------------------------------------------------------
  3024. // Purpose:
  3025. // Input  : *pPhysGunUser -
  3026. //          Reason -
  3027. //-----------------------------------------------------------------------------
  3028. void CNPC_Manhack::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  3029. {
  3030.     // Stop suppressing collisions between the manhack and the player
  3031.     SetOwnerEntity( NULL );
  3032.  
  3033.     m_bHeld = false;
  3034.  
  3035.     if ( Reason == LAUNCHED_BY_CANNON )
  3036.     {
  3037.         m_hPhysicsAttacker = pPhysGunUser;
  3038.         m_flLastPhysicsInfluenceTime = gpGlobals->curtime;
  3039.  
  3040.         // There's about to be a massive change in velocity.
  3041.         // Think immediately so we can do our slice traces, etc.
  3042.         SetNextThink( gpGlobals->curtime + 0.01f );
  3043.  
  3044.         // Stall our engine for awhile
  3045.         m_flEngineStallTime = gpGlobals->curtime + 2.0f;
  3046.         SetEyeState( MANHACK_EYE_STATE_STUNNED );
  3047.     }
  3048.     else
  3049.     {
  3050.         if( m_bHackedByAlyx && !GetEnemy() )
  3051.         {
  3052.             // If a hacked manhack is released in peaceable conditions,
  3053.             // just loiter, don't zip off.
  3054.             StartLoitering( GetAbsOrigin() );
  3055.         }
  3056.  
  3057.         m_hPhysicsAttacker = NULL;
  3058.         m_flLastPhysicsInfluenceTime = 0;
  3059.     }
  3060. }
  3061.  
  3062. void CNPC_Manhack::StartLoitering( const Vector &vecLoiterPosition )
  3063. {
  3064.     //Msg("Start Loitering\n");
  3065.  
  3066.     m_vTargetBanking = vec3_origin;
  3067.     m_vecLoiterPosition = GetAbsOrigin();
  3068.     m_vForceVelocity = vec3_origin;
  3069.     SetCurrentVelocity( vec3_origin );
  3070. }
  3071.  
  3072. CBasePlayer *CNPC_Manhack::HasPhysicsAttacker( float dt )
  3073. {
  3074.     // If the player is holding me now, or I've been recently thrown
  3075.     // then return a pointer to that player
  3076.     if ( IsHeldByPhyscannon() || (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime) )
  3077.     {
  3078.         return m_hPhysicsAttacker;
  3079.     }
  3080.     return NULL;
  3081. }
  3082.  
  3083. //-----------------------------------------------------------------------------
  3084. // Manhacks that have been hacked by Alyx get more engine power (fly faster)
  3085. //-----------------------------------------------------------------------------
  3086. float CNPC_Manhack::GetMaxEnginePower()
  3087. {
  3088.     if( m_bHackedByAlyx )
  3089.     {
  3090.         return 2.0f;
  3091.     }
  3092.  
  3093.     return 1.0f;
  3094. }
  3095.  
  3096.  
  3097. //-----------------------------------------------------------------------------
  3098. // Purpose:
  3099. //-----------------------------------------------------------------------------
  3100. void CNPC_Manhack::UpdatePanels( void )
  3101. {
  3102.     if ( m_flEngineStallTime > gpGlobals->curtime )
  3103.     {
  3104.         SetPoseParameter( m_iPanel1, random->RandomFloat( 0.0f, 90.0f ) );
  3105.         SetPoseParameter( m_iPanel2, random->RandomFloat( 0.0f, 90.0f ) );
  3106.         SetPoseParameter( m_iPanel3, random->RandomFloat( 0.0f, 90.0f ) );
  3107.         SetPoseParameter( m_iPanel4, random->RandomFloat( 0.0f, 90.0f ) );
  3108.         return;
  3109.     }
  3110.  
  3111.     float panelPosition = GetPoseParameter( m_iPanel1 );
  3112.  
  3113.     if ( m_bShowingHostile )
  3114.     {
  3115.         panelPosition = 90.0f;//UTIL_Approach( 90.0f, panelPosition, 90.0f );
  3116.     }
  3117.     else
  3118.     {
  3119.         panelPosition = UTIL_Approach( 0.0f, panelPosition, 25.0f );
  3120.     }
  3121.  
  3122.     //FIXME: If we're going to have all these be equal, there's no need for 4 poses..
  3123.     SetPoseParameter( m_iPanel1, panelPosition );
  3124.     SetPoseParameter( m_iPanel2, panelPosition );
  3125.     SetPoseParameter( m_iPanel3, panelPosition );
  3126.     SetPoseParameter( m_iPanel4, panelPosition );
  3127.  
  3128.     //TODO: Make these waver randomly?
  3129. }
  3130.  
  3131. //-----------------------------------------------------------------------------
  3132. // Purpose:
  3133. // Input  : hostile -
  3134. //-----------------------------------------------------------------------------
  3135. void CNPC_Manhack::ShowHostile( bool hostile /*= true*/)
  3136. {
  3137.     if ( m_bShowingHostile == hostile )
  3138.         return;
  3139.  
  3140.     //TODO: Open the manhack panels or close them, depending on the state
  3141.     m_bShowingHostile = hostile;
  3142.  
  3143.     if ( hostile )
  3144.     {
  3145.         EmitSound( "NPC_Manhack.ChargeAnnounce" );
  3146.     }
  3147.     else
  3148.     {
  3149.         EmitSound( "NPC_Manhack.ChargeEnd" );
  3150.     }
  3151. }
  3152.  
  3153. //-----------------------------------------------------------------------------
  3154. // Purpose:
  3155. //-----------------------------------------------------------------------------
  3156. void CNPC_Manhack::StartBurst( const Vector &vecDirection )
  3157. {
  3158.     if ( m_flBurstDuration > gpGlobals->curtime )
  3159.         return;
  3160.  
  3161.     ShowHostile();
  3162.  
  3163.     // Don't burst attack again for a couple seconds
  3164.     m_flNextBurstTime = gpGlobals->curtime + 2.0;
  3165.     m_flBurstDuration = gpGlobals->curtime + 1.0;
  3166.    
  3167.     // Save off where we were going towards and for how long
  3168.     m_vecBurstDirection = vecDirection;
  3169. }
  3170.  
  3171. //-----------------------------------------------------------------------------
  3172. // Purpose:
  3173. //-----------------------------------------------------------------------------
  3174. void CNPC_Manhack::StopBurst( bool bInterruptSchedule /*= false*/ )
  3175. {
  3176.     if ( m_flBurstDuration < gpGlobals->curtime )
  3177.         return;
  3178.  
  3179.     ShowHostile( false );
  3180.  
  3181.     // Stop our burst timers
  3182.     m_flNextBurstTime = gpGlobals->curtime + 2.0f; //FIXME: Skill level based
  3183.     m_flBurstDuration = gpGlobals->curtime - 0.1f;
  3184.  
  3185.     if ( bInterruptSchedule )
  3186.     {
  3187.         // We need to rethink our current schedule
  3188.         ClearSchedule( "Stopping burst" );
  3189.     }
  3190. }
  3191.  
  3192. //-----------------------------------------------------------------------------
  3193. // Purpose:
  3194. //-----------------------------------------------------------------------------
  3195. void CNPC_Manhack::SetEyeState( int state )
  3196. {
  3197.     // Make sure we're active
  3198.     StartEye();
  3199.  
  3200.     switch( state )
  3201.     {
  3202.     case MANHACK_EYE_STATE_STUNNED:
  3203.         {
  3204.             if ( m_pEyeGlow )
  3205.             {
  3206.                 //Toggle our state
  3207.                 m_pEyeGlow->SetColor( 255, 128, 0 );
  3208.                 m_pEyeGlow->SetScale( 0.15f, 0.1f );
  3209.                 m_pEyeGlow->SetBrightness( 164, 0.1f );
  3210.                 m_pEyeGlow->m_nRenderFX = kRenderFxStrobeFast;
  3211.             }
  3212.            
  3213.             if ( m_pLightGlow )
  3214.             {
  3215.                 m_pLightGlow->SetColor( 255, 128, 0 );
  3216.                 m_pLightGlow->SetScale( 0.15f, 0.1f );
  3217.                 m_pLightGlow->SetBrightness( 164, 0.1f );
  3218.                 m_pLightGlow->m_nRenderFX = kRenderFxStrobeFast;
  3219.             }
  3220.  
  3221.             EmitSound("NPC_Manhack.Stunned");
  3222.  
  3223.             break;
  3224.         }
  3225.  
  3226.     case MANHACK_EYE_STATE_CHARGE:
  3227.         {
  3228.             if ( m_pEyeGlow )
  3229.             {
  3230.                 //Toggle our state
  3231.                 if( m_bHackedByAlyx )
  3232.                 {
  3233.                     m_pEyeGlow->SetColor( 0, 255, 0 );
  3234.                 }
  3235.                 else
  3236.                 {
  3237.                     m_pEyeGlow->SetColor( 255, 0, 0 );
  3238.                 }
  3239.  
  3240.                 m_pEyeGlow->SetScale( 0.25f, 0.5f );
  3241.                 m_pEyeGlow->SetBrightness( 164, 0.1f );
  3242.                 m_pEyeGlow->m_nRenderFX = kRenderFxNone;
  3243.             }
  3244.            
  3245.             if ( m_pLightGlow )
  3246.             {
  3247.                 if( m_bHackedByAlyx )
  3248.                 {
  3249.                     m_pLightGlow->SetColor( 0, 255, 0 );
  3250.                 }
  3251.                 else
  3252.                 {
  3253.                     m_pLightGlow->SetColor( 255, 0, 0 );
  3254.                 }
  3255.  
  3256.                 m_pLightGlow->SetScale( 0.25f, 0.5f );
  3257.                 m_pLightGlow->SetBrightness( 164, 0.1f );
  3258.                 m_pLightGlow->m_nRenderFX = kRenderFxNone;
  3259.             }
  3260.  
  3261.             break;
  3262.         }
  3263.    
  3264.     default:
  3265.         if ( m_pEyeGlow )
  3266.             m_pEyeGlow->m_nRenderFX = kRenderFxNone;
  3267.         break;
  3268.     }
  3269. }
  3270.  
  3271.  
  3272. unsigned int CNPC_Manhack::PhysicsSolidMaskForEntity( void ) const
  3273. {
  3274.     unsigned int mask = BaseClass::PhysicsSolidMaskForEntity();
  3275.     if ( m_bIgnoreClipbrushes )
  3276.     {
  3277.         mask &= ~CONTENTS_MONSTERCLIP;
  3278.     }
  3279.     return mask;
  3280. }
  3281.  
  3282. //-----------------------------------------------------------------------------
  3283. // Purpose:
  3284. // Output : Returns true on success, false on failure.
  3285. //-----------------------------------------------------------------------------
  3286. bool CNPC_Manhack::CreateVPhysics( void )
  3287. {
  3288.     if ( HasSpawnFlags( SF_MANHACK_CARRIED ) )
  3289.         return false;
  3290.  
  3291.     return BaseClass::CreateVPhysics();
  3292. }
  3293.  
  3294. //-----------------------------------------------------------------------------
  3295. //
  3296. // Schedules
  3297. //
  3298. //-----------------------------------------------------------------------------
  3299. AI_BEGIN_CUSTOM_NPC( npc_manhack, CNPC_Manhack )
  3300.  
  3301.     DECLARE_TASK( TASK_MANHACK_HOVER );
  3302.     DECLARE_TASK( TASK_MANHACK_UNPACK );
  3303.     DECLARE_TASK( TASK_MANHACK_FIND_SQUAD_CENTER );
  3304.     DECLARE_TASK( TASK_MANHACK_FIND_SQUAD_MEMBER );
  3305.     DECLARE_TASK( TASK_MANHACK_MOVEAT_SAVEPOSITION );
  3306.  
  3307.     DECLARE_CONDITION( COND_MANHACK_START_ATTACK );
  3308.  
  3309.     DECLARE_ACTIVITY( ACT_MANHACK_UNPACK );
  3310.  
  3311. //=========================================================
  3312. // > SCHED_MANHACK_ATTACK_HOVER
  3313. //=========================================================
  3314. DEFINE_SCHEDULE
  3315. (
  3316.     SCHED_MANHACK_ATTACK_HOVER,
  3317.  
  3318.     "   Tasks"
  3319.     "       TASK_SET_ACTIVITY       ACTIVITY:ACT_FLY"
  3320.     "       TASK_MANHACK_HOVER      0"
  3321.     "   "
  3322.     "   Interrupts"
  3323.     "       COND_TOO_FAR_TO_ATTACK"
  3324.     "       COND_TOO_CLOSE_TO_ATTACK"
  3325.     "       COND_NEW_ENEMY"
  3326.     "       COND_ENEMY_DEAD"
  3327.     "       COND_LIGHT_DAMAGE"
  3328.     "       COND_HEAVY_DAMAGE"
  3329.     "       COND_ENEMY_OCCLUDED"
  3330. );
  3331.  
  3332.  
  3333. //=========================================================
  3334. // > SCHED_MANHACK_ATTACK_HOVER
  3335. //=========================================================
  3336. DEFINE_SCHEDULE
  3337. (
  3338.     SCHED_MANHACK_DEPLOY,
  3339.  
  3340.     "   Tasks"
  3341.     "       TASK_PLAY_SEQUENCE          ACTIVITY:ACT_MANHACK_UNPACK"
  3342.     "       TASK_SET_ACTIVITY           ACTIVITY:ACT_FLY"
  3343.     "   "
  3344. //  "   Interrupts"
  3345. );
  3346.  
  3347. //=========================================================
  3348. // > SCHED_MANHACK_REGROUP
  3349. //=========================================================
  3350. DEFINE_SCHEDULE
  3351. (
  3352.     SCHED_MANHACK_REGROUP,
  3353.  
  3354.     "   Tasks"
  3355.     "       TASK_STOP_MOVING                            0"
  3356.     "       TASK_SET_TOLERANCE_DISTANCE                 24"
  3357.     "       TASK_STORE_ENEMY_POSITION_IN_SAVEPOSITION   0"
  3358.     "       TASK_FIND_BACKAWAY_FROM_SAVEPOSITION        0"
  3359.     "       TASK_RUN_PATH                               0"
  3360.     "       TASK_WAIT_FOR_MOVEMENT                      0"
  3361.     "   "
  3362.     "   Interrupts"
  3363.     "       COND_MANHACK_START_ATTACK"
  3364.     "       COND_NEW_ENEMY"
  3365.     "       COND_CAN_MELEE_ATTACK1"
  3366. );
  3367.  
  3368.  
  3369.  
  3370. //=========================================================
  3371. // > SCHED_MANHACK_SWARN
  3372. //=========================================================
  3373. DEFINE_SCHEDULE
  3374. (
  3375.     SCHED_MANHACK_SWARM_IDLE,
  3376.  
  3377.     "   Tasks"
  3378.     "       TASK_STOP_MOVING                            0"
  3379.     "       TASK_SET_FAIL_SCHEDULE                      SCHEDULE:SCHED_MANHACK_SWARM_FAILURE"
  3380.     "       TASK_MANHACK_FIND_SQUAD_CENTER              0"
  3381.     "       TASK_MANHACK_MOVEAT_SAVEPOSITION            5"
  3382.     "   "
  3383.     "   Interrupts"
  3384.     "       COND_NEW_ENEMY"
  3385.     "       COND_SEE_ENEMY"
  3386.     "       COND_SEE_FEAR"
  3387.     "       COND_LIGHT_DAMAGE"
  3388.     "       COND_HEAVY_DAMAGE"
  3389.     "       COND_SMELL"
  3390.     "       COND_PROVOKED"
  3391.     "       COND_GIVE_WAY"
  3392.     "       COND_HEAR_PLAYER"
  3393.     "       COND_HEAR_DANGER"
  3394.     "       COND_HEAR_COMBAT"
  3395.     "       COND_HEAR_BULLET_IMPACT"
  3396. );
  3397.  
  3398.  
  3399. DEFINE_SCHEDULE
  3400. (
  3401.     SCHED_MANHACK_SWARM,
  3402.  
  3403.     "   Tasks"
  3404.     "       TASK_STOP_MOVING                            0"
  3405.     "       TASK_SET_FAIL_SCHEDULE                      SCHEDULE:SCHED_MANHACK_SWARM_FAILURE"
  3406.     "       TASK_MANHACK_FIND_SQUAD_CENTER              0"
  3407.     "       TASK_MANHACK_MOVEAT_SAVEPOSITION            1"
  3408.     "   "
  3409.     "   Interrupts"
  3410.     "       COND_NEW_ENEMY"
  3411.     "       COND_CAN_MELEE_ATTACK1"
  3412.     "       COND_LIGHT_DAMAGE"
  3413.     "       COND_HEAVY_DAMAGE"
  3414. );
  3415.  
  3416. DEFINE_SCHEDULE
  3417. (
  3418.     SCHED_MANHACK_SWARM_FAILURE,
  3419.  
  3420.     "   Tasks"
  3421.     "       TASK_STOP_MOVING                            0"
  3422.     "       TASK_WAIT                                   2"
  3423.     "       TASK_WAIT_RANDOM                            2"
  3424.     "       TASK_MANHACK_FIND_SQUAD_MEMBER              0"
  3425.     "       TASK_GET_PATH_TO_SAVEPOSITION               0"
  3426.     "       TASK_WAIT_FOR_MOVEMENT                      0"
  3427.     "   "
  3428.     "   Interrupts"
  3429.     "       COND_SEE_ENEMY"
  3430.     "       COND_NEW_ENEMY"
  3431. );
  3432.  
  3433. AI_END_CUSTOM_NPC()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement