diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/npc_combines.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/hl2/npc_combines.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_combines.cpp | 860 |
1 files changed, 430 insertions, 430 deletions
diff --git a/mp/src/game/server/hl2/npc_combines.cpp b/mp/src/game/server/hl2/npc_combines.cpp index aa7431b0..9addfbeb 100644 --- a/mp/src/game/server/hl2/npc_combines.cpp +++ b/mp/src/game/server/hl2/npc_combines.cpp @@ -1,431 +1,431 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: This is the soldier version of the combine, analogous to the HL1 grunt.
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "ai_hull.h"
-#include "ai_motor.h"
-#include "npc_combines.h"
-#include "bitstring.h"
-#include "engine/IEngineSound.h"
-#include "soundent.h"
-#include "ndebugoverlay.h"
-#include "npcevent.h"
-#include "hl2/hl2_player.h"
-#include "game.h"
-#include "ammodef.h"
-#include "explode.h"
-#include "ai_memory.h"
-#include "Sprite.h"
-#include "soundenvelope.h"
-#include "weapon_physcannon.h"
-#include "hl2_gamerules.h"
-#include "gameweaponmanager.h"
-#include "vehicle_base.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-ConVar sk_combine_s_health( "sk_combine_s_health","0");
-ConVar sk_combine_s_kick( "sk_combine_s_kick","0");
-
-ConVar sk_combine_guard_health( "sk_combine_guard_health", "0");
-ConVar sk_combine_guard_kick( "sk_combine_guard_kick", "0");
-
-// Whether or not the combine guard should spawn health on death
-ConVar combine_guard_spawn_health( "combine_guard_spawn_health", "1" );
-
-extern ConVar sk_plr_dmg_buckshot;
-extern ConVar sk_plr_num_shotgun_pellets;
-
-//Whether or not the combine should spawn health on death
-ConVar combine_spawn_health( "combine_spawn_health", "1" );
-
-LINK_ENTITY_TO_CLASS( npc_combine_s, CNPC_CombineS );
-
-
-#define AE_SOLDIER_BLOCK_PHYSICS 20 // trying to block an incoming physics object
-
-extern Activity ACT_WALK_EASY;
-extern Activity ACT_WALK_MARCH;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::Spawn( void )
-{
- Precache();
- SetModel( STRING( GetModelName() ) );
-
- if( IsElite() )
- {
- // Stronger, tougher.
- SetHealth( sk_combine_guard_health.GetFloat() );
- SetMaxHealth( sk_combine_guard_health.GetFloat() );
- SetKickDamage( sk_combine_guard_kick.GetFloat() );
- }
- else
- {
- SetHealth( sk_combine_s_health.GetFloat() );
- SetMaxHealth( sk_combine_s_health.GetFloat() );
- SetKickDamage( sk_combine_s_kick.GetFloat() );
- }
-
- CapabilitiesAdd( bits_CAP_ANIMATEDFACE );
- CapabilitiesAdd( bits_CAP_MOVE_SHOOT );
- CapabilitiesAdd( bits_CAP_DOORS_GROUP );
-
- BaseClass::Spawn();
-
-#if HL2_EPISODIC
- if (m_iUseMarch && !HasSpawnFlags(SF_NPC_START_EFFICIENT))
- {
- Msg( "Soldier %s is set to use march anim, but is not an efficient AI. The blended march anim can only be used for dead-ahead walks!\n", GetDebugName() );
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::Precache()
-{
- const char *pModelName = STRING( GetModelName() );
-
- if( !Q_stricmp( pModelName, "models/combine_super_soldier.mdl" ) )
- {
- m_fIsElite = true;
- }
- else
- {
- m_fIsElite = false;
- }
-
- if( !GetModelName() )
- {
- SetModelName( MAKE_STRING( "models/combine_soldier.mdl" ) );
- }
-
- PrecacheModel( STRING( GetModelName() ) );
-
- UTIL_PrecacheOther( "item_healthvial" );
- UTIL_PrecacheOther( "weapon_frag" );
- UTIL_PrecacheOther( "item_ammo_ar2_altfire" );
-
- BaseClass::Precache();
-}
-
-
-void CNPC_CombineS::DeathSound( const CTakeDamageInfo &info )
-{
- // NOTE: The response system deals with this at the moment
- if ( GetFlags() & FL_DISSOLVING )
- return;
-
- GetSentences()->Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Soldiers use CAN_RANGE_ATTACK2 to indicate whether they can throw
-// a grenade. Because they check only every half-second or so, this
-// condition must persist until it is updated again by the code
-// that determines whether a grenade can be thrown, so prevent the
-// base class from clearing it out. (sjb)
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::ClearAttackConditions( )
-{
- bool fCanRangeAttack2 = HasCondition( COND_CAN_RANGE_ATTACK2 );
-
- // Call the base class.
- BaseClass::ClearAttackConditions();
-
- if( fCanRangeAttack2 )
- {
- // We don't allow the base class to clear this condition because we
- // don't sense for it every frame.
- SetCondition( COND_CAN_RANGE_ATTACK2 );
- }
-}
-
-void CNPC_CombineS::PrescheduleThink( void )
-{
- /*//FIXME: This doesn't need to be in here, it's all debug info
- if( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
- {
- // Don't react unless we see the item!!
- CSound *pSound = NULL;
-
- pSound = GetLoudestSoundOfType( SOUND_PHYSICS_DANGER );
-
- if( pSound )
- {
- if( FInViewCone( pSound->GetSoundReactOrigin() ) )
- {
- DevMsg( "OH CRAP!\n" );
- NDebugOverlay::Line( EyePosition(), pSound->GetSoundReactOrigin(), 0, 0, 255, false, 2.0f );
- }
- }
- }
- */
-
- BaseClass::PrescheduleThink();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows for modification of the interrupt mask for the current schedule.
-// In the most cases the base implementation should be called first.
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::BuildScheduleTestBits( void )
-{
- //Interrupt any schedule with physics danger (as long as I'm not moving or already trying to block)
- if ( m_flGroundSpeed == 0.0 && !IsCurSchedule( SCHED_FLINCH_PHYSICS ) )
- {
- SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER );
- }
-
- BaseClass::BuildScheduleTestBits();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-int CNPC_CombineS::SelectSchedule ( void )
-{
- return BaseClass::SelectSchedule();
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-float CNPC_CombineS::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info )
-{
- switch( iHitGroup )
- {
- case HITGROUP_HEAD:
- {
- // Soldiers take double headshot damage
- return 2.0f;
- }
- }
-
- return BaseClass::GetHitgroupDamageMultiplier( iHitGroup, info );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::HandleAnimEvent( animevent_t *pEvent )
-{
- switch( pEvent->event )
- {
- case AE_SOLDIER_BLOCK_PHYSICS:
- DevMsg( "BLOCKING!\n" );
- m_fIsBlocking = true;
- break;
-
- default:
- BaseClass::HandleAnimEvent( pEvent );
- break;
- }
-}
-
-void CNPC_CombineS::OnChangeActivity( Activity eNewActivity )
-{
- // Any new sequence stops us blocking.
- m_fIsBlocking = false;
-
- BaseClass::OnChangeActivity( eNewActivity );
-
-#if HL2_EPISODIC
- // Give each trooper a varied look for his march. Done here because if you do it earlier (eg Spawn, StartTask), the
- // pose param gets overwritten.
- if (m_iUseMarch)
- {
- SetPoseParameter("casual", RandomFloat());
- }
-#endif
-}
-
-void CNPC_CombineS::OnListened()
-{
- BaseClass::OnListened();
-
- if ( HasCondition( COND_HEAR_DANGER ) && HasCondition( COND_HEAR_PHYSICS_DANGER ) )
- {
- if ( HasInterruptCondition( COND_HEAR_DANGER ) )
- {
- ClearCondition( COND_HEAR_PHYSICS_DANGER );
- }
- }
-
- // debugging to find missed schedules
-#if 0
- if ( HasCondition( COND_HEAR_DANGER ) && !HasInterruptCondition( COND_HEAR_DANGER ) )
- {
- DevMsg("Ignore danger in %s\n", GetCurSchedule()->GetName() );
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &info -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-void CNPC_CombineS::Event_Killed( const CTakeDamageInfo &info )
-{
- // Don't bother if we've been told not to, or the player has a megaphyscannon
- if ( combine_spawn_health.GetBool() == false || PlayerHasMegaPhysCannon() )
- {
- BaseClass::Event_Killed( info );
- return;
- }
-
- CBasePlayer *pPlayer = ToBasePlayer( info.GetAttacker() );
-
- if ( !pPlayer )
- {
- CPropVehicleDriveable *pVehicle = dynamic_cast<CPropVehicleDriveable *>( info.GetAttacker() ) ;
- if ( pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer() )
- {
- pPlayer = assert_cast<CBasePlayer *>( pVehicle->GetDriver() );
- }
- }
-
- if ( pPlayer != NULL )
- {
- // Elites drop alt-fire ammo, so long as they weren't killed by dissolving.
- if( IsElite() )
- {
-#ifdef HL2_EPISODIC
- if ( HasSpawnFlags( SF_COMBINE_NO_AR2DROP ) == false )
-#endif
- {
- CBaseEntity *pItem = DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
-
- if ( pItem )
- {
- IPhysicsObject *pObj = pItem->VPhysicsGetObject();
-
- if ( pObj )
- {
- Vector vel = RandomVector( -64.0f, 64.0f );
- AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f );
-
- vel[2] = 0.0f;
- pObj->AddVelocity( &vel, &angImp );
- }
-
- if( info.GetDamageType() & DMG_DISSOLVE )
- {
- CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pItem);
-
- if( pAnimating )
- {
- pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
- }
- }
- else
- {
- WeaponManager_AddManaged( pItem );
- }
- }
- }
- }
-
- CHalfLife2 *pHL2GameRules = static_cast<CHalfLife2 *>(g_pGameRules);
-
- // Attempt to drop health
- if ( pHL2GameRules->NPC_ShouldDropHealth( pPlayer ) )
- {
- DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
- pHL2GameRules->NPC_DroppedHealth();
- }
-
- if ( HasSpawnFlags( SF_COMBINE_NO_GRENADEDROP ) == false )
- {
- // Attempt to drop a grenade
- if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) )
- {
- DropItem( "weapon_frag", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
- pHL2GameRules->NPC_DroppedGrenade();
- }
- }
- }
-
- BaseClass::Event_Killed( info );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &info -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CombineS::IsLightDamage( const CTakeDamageInfo &info )
-{
- return BaseClass::IsLightDamage( info );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &info -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CombineS::IsHeavyDamage( const CTakeDamageInfo &info )
-{
- // Combine considers AR2 fire to be heavy damage
- if ( info.GetAmmoType() == GetAmmoDef()->Index("AR2") )
- return true;
-
- // 357 rounds are heavy damage
- if ( info.GetAmmoType() == GetAmmoDef()->Index("357") )
- return true;
-
- // Shotgun blasts where at least half the pellets hit me are heavy damage
- if ( info.GetDamageType() & DMG_BUCKSHOT )
- {
- int iHalfMax = sk_plr_dmg_buckshot.GetFloat() * sk_plr_num_shotgun_pellets.GetInt() * 0.5;
- if ( info.GetDamage() >= iHalfMax )
- return true;
- }
-
- // Rollermine shocks
- if( (info.GetDamageType() & DMG_SHOCK) && hl2_episodic.GetBool() )
- {
- return true;
- }
-
- return BaseClass::IsHeavyDamage( info );
-}
-
-#if HL2_EPISODIC
-//-----------------------------------------------------------------------------
-// Purpose: Translate base class activities into combot activites
-//-----------------------------------------------------------------------------
-Activity CNPC_CombineS::NPC_TranslateActivity( Activity eNewActivity )
-{
- // If the special ep2_outland_05 "use march" flag is set, use the more casual marching anim.
- if ( m_iUseMarch && eNewActivity == ACT_WALK )
- {
- eNewActivity = ACT_WALK_MARCH;
- }
-
- return BaseClass::NPC_TranslateActivity( eNewActivity );
-}
-
-
-//---------------------------------------------------------
-// Save/Restore
-//---------------------------------------------------------
-BEGIN_DATADESC( CNPC_CombineS )
-
- DEFINE_KEYFIELD( m_iUseMarch, FIELD_INTEGER, "usemarch" ),
-
-END_DATADESC()
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This is the soldier version of the combine, analogous to the HL1 grunt. +// +//=============================================================================// + +#include "cbase.h" +#include "ai_hull.h" +#include "ai_motor.h" +#include "npc_combines.h" +#include "bitstring.h" +#include "engine/IEngineSound.h" +#include "soundent.h" +#include "ndebugoverlay.h" +#include "npcevent.h" +#include "hl2/hl2_player.h" +#include "game.h" +#include "ammodef.h" +#include "explode.h" +#include "ai_memory.h" +#include "Sprite.h" +#include "soundenvelope.h" +#include "weapon_physcannon.h" +#include "hl2_gamerules.h" +#include "gameweaponmanager.h" +#include "vehicle_base.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar sk_combine_s_health( "sk_combine_s_health","0"); +ConVar sk_combine_s_kick( "sk_combine_s_kick","0"); + +ConVar sk_combine_guard_health( "sk_combine_guard_health", "0"); +ConVar sk_combine_guard_kick( "sk_combine_guard_kick", "0"); + +// Whether or not the combine guard should spawn health on death +ConVar combine_guard_spawn_health( "combine_guard_spawn_health", "1" ); + +extern ConVar sk_plr_dmg_buckshot; +extern ConVar sk_plr_num_shotgun_pellets; + +//Whether or not the combine should spawn health on death +ConVar combine_spawn_health( "combine_spawn_health", "1" ); + +LINK_ENTITY_TO_CLASS( npc_combine_s, CNPC_CombineS ); + + +#define AE_SOLDIER_BLOCK_PHYSICS 20 // trying to block an incoming physics object + +extern Activity ACT_WALK_EASY; +extern Activity ACT_WALK_MARCH; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CombineS::Spawn( void ) +{ + Precache(); + SetModel( STRING( GetModelName() ) ); + + if( IsElite() ) + { + // Stronger, tougher. + SetHealth( sk_combine_guard_health.GetFloat() ); + SetMaxHealth( sk_combine_guard_health.GetFloat() ); + SetKickDamage( sk_combine_guard_kick.GetFloat() ); + } + else + { + SetHealth( sk_combine_s_health.GetFloat() ); + SetMaxHealth( sk_combine_s_health.GetFloat() ); + SetKickDamage( sk_combine_s_kick.GetFloat() ); + } + + CapabilitiesAdd( bits_CAP_ANIMATEDFACE ); + CapabilitiesAdd( bits_CAP_MOVE_SHOOT ); + CapabilitiesAdd( bits_CAP_DOORS_GROUP ); + + BaseClass::Spawn(); + +#if HL2_EPISODIC + if (m_iUseMarch && !HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + Msg( "Soldier %s is set to use march anim, but is not an efficient AI. The blended march anim can only be used for dead-ahead walks!\n", GetDebugName() ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CNPC_CombineS::Precache() +{ + const char *pModelName = STRING( GetModelName() ); + + if( !Q_stricmp( pModelName, "models/combine_super_soldier.mdl" ) ) + { + m_fIsElite = true; + } + else + { + m_fIsElite = false; + } + + if( !GetModelName() ) + { + SetModelName( MAKE_STRING( "models/combine_soldier.mdl" ) ); + } + + PrecacheModel( STRING( GetModelName() ) ); + + UTIL_PrecacheOther( "item_healthvial" ); + UTIL_PrecacheOther( "weapon_frag" ); + UTIL_PrecacheOther( "item_ammo_ar2_altfire" ); + + BaseClass::Precache(); +} + + +void CNPC_CombineS::DeathSound( const CTakeDamageInfo &info ) +{ + // NOTE: The response system deals with this at the moment + if ( GetFlags() & FL_DISSOLVING ) + return; + + GetSentences()->Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Soldiers use CAN_RANGE_ATTACK2 to indicate whether they can throw +// a grenade. Because they check only every half-second or so, this +// condition must persist until it is updated again by the code +// that determines whether a grenade can be thrown, so prevent the +// base class from clearing it out. (sjb) +//----------------------------------------------------------------------------- +void CNPC_CombineS::ClearAttackConditions( ) +{ + bool fCanRangeAttack2 = HasCondition( COND_CAN_RANGE_ATTACK2 ); + + // Call the base class. + BaseClass::ClearAttackConditions(); + + if( fCanRangeAttack2 ) + { + // We don't allow the base class to clear this condition because we + // don't sense for it every frame. + SetCondition( COND_CAN_RANGE_ATTACK2 ); + } +} + +void CNPC_CombineS::PrescheduleThink( void ) +{ + /*//FIXME: This doesn't need to be in here, it's all debug info + if( HasCondition( COND_HEAR_PHYSICS_DANGER ) ) + { + // Don't react unless we see the item!! + CSound *pSound = NULL; + + pSound = GetLoudestSoundOfType( SOUND_PHYSICS_DANGER ); + + if( pSound ) + { + if( FInViewCone( pSound->GetSoundReactOrigin() ) ) + { + DevMsg( "OH CRAP!\n" ); + NDebugOverlay::Line( EyePosition(), pSound->GetSoundReactOrigin(), 0, 0, 255, false, 2.0f ); + } + } + } + */ + + BaseClass::PrescheduleThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: Allows for modification of the interrupt mask for the current schedule. +// In the most cases the base implementation should be called first. +//----------------------------------------------------------------------------- +void CNPC_CombineS::BuildScheduleTestBits( void ) +{ + //Interrupt any schedule with physics danger (as long as I'm not moving or already trying to block) + if ( m_flGroundSpeed == 0.0 && !IsCurSchedule( SCHED_FLINCH_PHYSICS ) ) + { + SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER ); + } + + BaseClass::BuildScheduleTestBits(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +int CNPC_CombineS::SelectSchedule ( void ) +{ + return BaseClass::SelectSchedule(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CNPC_CombineS::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info ) +{ + switch( iHitGroup ) + { + case HITGROUP_HEAD: + { + // Soldiers take double headshot damage + return 2.0f; + } + } + + return BaseClass::GetHitgroupDamageMultiplier( iHitGroup, info ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_CombineS::HandleAnimEvent( animevent_t *pEvent ) +{ + switch( pEvent->event ) + { + case AE_SOLDIER_BLOCK_PHYSICS: + DevMsg( "BLOCKING!\n" ); + m_fIsBlocking = true; + break; + + default: + BaseClass::HandleAnimEvent( pEvent ); + break; + } +} + +void CNPC_CombineS::OnChangeActivity( Activity eNewActivity ) +{ + // Any new sequence stops us blocking. + m_fIsBlocking = false; + + BaseClass::OnChangeActivity( eNewActivity ); + +#if HL2_EPISODIC + // Give each trooper a varied look for his march. Done here because if you do it earlier (eg Spawn, StartTask), the + // pose param gets overwritten. + if (m_iUseMarch) + { + SetPoseParameter("casual", RandomFloat()); + } +#endif +} + +void CNPC_CombineS::OnListened() +{ + BaseClass::OnListened(); + + if ( HasCondition( COND_HEAR_DANGER ) && HasCondition( COND_HEAR_PHYSICS_DANGER ) ) + { + if ( HasInterruptCondition( COND_HEAR_DANGER ) ) + { + ClearCondition( COND_HEAR_PHYSICS_DANGER ); + } + } + + // debugging to find missed schedules +#if 0 + if ( HasCondition( COND_HEAR_DANGER ) && !HasInterruptCondition( COND_HEAR_DANGER ) ) + { + DevMsg("Ignore danger in %s\n", GetCurSchedule()->GetName() ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +void CNPC_CombineS::Event_Killed( const CTakeDamageInfo &info ) +{ + // Don't bother if we've been told not to, or the player has a megaphyscannon + if ( combine_spawn_health.GetBool() == false || PlayerHasMegaPhysCannon() ) + { + BaseClass::Event_Killed( info ); + return; + } + + CBasePlayer *pPlayer = ToBasePlayer( info.GetAttacker() ); + + if ( !pPlayer ) + { + CPropVehicleDriveable *pVehicle = dynamic_cast<CPropVehicleDriveable *>( info.GetAttacker() ) ; + if ( pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer() ) + { + pPlayer = assert_cast<CBasePlayer *>( pVehicle->GetDriver() ); + } + } + + if ( pPlayer != NULL ) + { + // Elites drop alt-fire ammo, so long as they weren't killed by dissolving. + if( IsElite() ) + { +#ifdef HL2_EPISODIC + if ( HasSpawnFlags( SF_COMBINE_NO_AR2DROP ) == false ) +#endif + { + CBaseEntity *pItem = DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + + if ( pItem ) + { + IPhysicsObject *pObj = pItem->VPhysicsGetObject(); + + if ( pObj ) + { + Vector vel = RandomVector( -64.0f, 64.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pObj->AddVelocity( &vel, &angImp ); + } + + if( info.GetDamageType() & DMG_DISSOLVE ) + { + CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pItem); + + if( pAnimating ) + { + pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + else + { + WeaponManager_AddManaged( pItem ); + } + } + } + } + + CHalfLife2 *pHL2GameRules = static_cast<CHalfLife2 *>(g_pGameRules); + + // Attempt to drop health + if ( pHL2GameRules->NPC_ShouldDropHealth( pPlayer ) ) + { + DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + pHL2GameRules->NPC_DroppedHealth(); + } + + if ( HasSpawnFlags( SF_COMBINE_NO_GRENADEDROP ) == false ) + { + // Attempt to drop a grenade + if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) ) + { + DropItem( "weapon_frag", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); + pHL2GameRules->NPC_DroppedGrenade(); + } + } + } + + BaseClass::Event_Killed( info ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CombineS::IsLightDamage( const CTakeDamageInfo &info ) +{ + return BaseClass::IsLightDamage( info ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CombineS::IsHeavyDamage( const CTakeDamageInfo &info ) +{ + // Combine considers AR2 fire to be heavy damage + if ( info.GetAmmoType() == GetAmmoDef()->Index("AR2") ) + return true; + + // 357 rounds are heavy damage + if ( info.GetAmmoType() == GetAmmoDef()->Index("357") ) + return true; + + // Shotgun blasts where at least half the pellets hit me are heavy damage + if ( info.GetDamageType() & DMG_BUCKSHOT ) + { + int iHalfMax = sk_plr_dmg_buckshot.GetFloat() * sk_plr_num_shotgun_pellets.GetInt() * 0.5; + if ( info.GetDamage() >= iHalfMax ) + return true; + } + + // Rollermine shocks + if( (info.GetDamageType() & DMG_SHOCK) && hl2_episodic.GetBool() ) + { + return true; + } + + return BaseClass::IsHeavyDamage( info ); +} + +#if HL2_EPISODIC +//----------------------------------------------------------------------------- +// Purpose: Translate base class activities into combot activites +//----------------------------------------------------------------------------- +Activity CNPC_CombineS::NPC_TranslateActivity( Activity eNewActivity ) +{ + // If the special ep2_outland_05 "use march" flag is set, use the more casual marching anim. + if ( m_iUseMarch && eNewActivity == ACT_WALK ) + { + eNewActivity = ACT_WALK_MARCH; + } + + return BaseClass::NPC_TranslateActivity( eNewActivity ); +} + + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CNPC_CombineS ) + + DEFINE_KEYFIELD( m_iUseMarch, FIELD_INTEGER, "usemarch" ), + +END_DATADESC() #endif
\ No newline at end of file |