Grenade/alt-fire item dropping for metrocops and player companions

This commit is contained in:
Blixibon 2021-09-25 14:26:31 -05:00
parent 24e6ab3767
commit 85097e119e
8 changed files with 232 additions and 12 deletions

View File

@ -230,7 +230,9 @@ DEFINE_INPUTFUNC( FIELD_STRING, "SetPoliceGoal", InputSetPoliceGoal ),
DEFINE_AIGRENADE_DATADESC()
#endif
#ifndef MAPBASE
DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ),
#endif
DEFINE_FIELD( m_fIsElite, FIELD_BOOLEAN ),
#ifndef MAPBASE
DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ),
@ -3687,7 +3689,9 @@ void CNPC_Combine::SetActivity( Activity NewActivity )
{
BaseClass::SetActivity( NewActivity );
#ifndef MAPBASE // CAI_GrenadeUser
m_iLastAnimEventHandled = -1;
#endif
}
//-----------------------------------------------------------------------------

View File

@ -59,7 +59,7 @@ public:
// Create components
virtual bool CreateComponents();
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
bool CanThrowGrenade( const Vector &vecTarget );
bool CheckCanThrowGrenade( const Vector &vecTarget );
#endif
@ -118,7 +118,7 @@ public:
const char* GetGrenadeAttachment() { return "lefthand"; }
#else
#endif
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
void DelayAltFireAttack( float flDelay );
void DelaySquadAltFireAttack( float flDelay );
#endif
@ -131,7 +131,7 @@ public:
Vector EyeOffset( Activity nActivity );
Vector EyePosition( void );
Vector BodyTarget( const Vector &posSrc, bool bNoisy = true );
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector GetAltFireTarget();
#endif
@ -320,7 +320,7 @@ private:
private:
int m_nKickDamage;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector m_vecTossVelocity;
EHANDLE m_hForcedGrenadeTarget;
#else
@ -334,12 +334,12 @@ private:
// Time Variables
float m_flNextPainSoundTime;
float m_flNextAlertSoundTime;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
float m_flNextGrenadeCheck;
#endif
float m_flNextLostSoundTime;
float m_flAlertPatrolTime; // When to stop doing alert patrol
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
float m_flNextAltFireTime; // Elites only. Next time to begin considering alt-fire attack.
#endif
@ -351,7 +351,7 @@ private:
CAI_Sentence< CNPC_Combine > m_Sentences;
#endif
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
int m_iNumGrenades;
#endif
CAI_AssaultBehavior m_AssaultBehavior;
@ -365,9 +365,11 @@ private:
#endif
public:
#ifndef MAPBASE // CAI_GrenadeUser
int m_iLastAnimEventHandled;
#endif
bool m_fIsElite;
#ifndef MAPBASE
#ifndef MAPBASE // CAI_GrenadeUser
Vector m_vecAltFireTarget;
#endif

View File

@ -261,6 +261,7 @@ BEGIN_DATADESC( CNPC_MetroPolice )
#ifdef MAPBASE
DEFINE_AIGRENADE_DATADESC()
DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ),
DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ),
#endif
END_DATADESC()
@ -517,6 +518,11 @@ CNPC_MetroPolice::CNPC_MetroPolice()
{
#ifdef MAPBASE
m_iGrenadeCapabilities = GRENCAP_GRENADE;
if (ai_grenade_always_drop.GetBool())
{
m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED);
}
#endif
}
@ -3681,6 +3687,11 @@ void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info )
DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
pHL2GameRules->NPC_DroppedHealth();
}
#ifdef MAPBASE
// Drop grenades if we should
DropGrenadeItemsOnDeath( info, pPlayer );
#endif
}
BaseClass::Event_Killed( info );

View File

@ -67,8 +67,12 @@ public:
const char* GetGrenadeAttachment() { return "LHand"; }
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; }
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0 && BaseClass::IsAltFireCapable(); }
virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; }
virtual bool ShouldDropGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_GRENADE) != 0 && BaseClass::ShouldDropGrenades(); }
virtual bool ShouldDropInterruptedGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_INTERRUPTED) != 0 && BaseClass::ShouldDropInterruptedGrenades(); }
virtual bool ShouldDropAltFire() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_ALTFIRE) != 0 && BaseClass::ShouldDropAltFire(); }
#endif
Vector EyeDirection3D( void ) { return CAI_BaseHumanoid::EyeDirection3D(); } // cops don't have eyes
@ -520,6 +524,7 @@ private:
// Determines whether this NPC is allowed to use grenades or alt-fire stuff.
eGrenadeCapabilities m_iGrenadeCapabilities;
eGrenadeDropCapabilities m_iGrenadeDropCapabilities;
#endif
AIHANDLE m_hManhack;

View File

@ -34,6 +34,7 @@
#ifdef MAPBASE
#include "mapbase/GlobalStrings.h"
#include "world.h"
#include "vehicle_base.h"
#endif
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
@ -148,6 +149,7 @@ BEGIN_DATADESC( CNPC_PlayerCompanion )
#ifdef MAPBASE
DEFINE_AIGRENADE_DATADESC()
DEFINE_INPUT( m_iGrenadeCapabilities, FIELD_INTEGER, "SetGrenadeCapabilities" ),
DEFINE_INPUT( m_iGrenadeDropCapabilities, FIELD_INTEGER, "SetGrenadeDropCapabilities" ),
#endif
END_DATADESC()
@ -175,6 +177,19 @@ string_t CNPC_PlayerCompanion::gm_iszAR2Classname;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CNPC_PlayerCompanion::CNPC_PlayerCompanion()
{
#ifdef MAPBASE
if (ai_grenade_always_drop.GetBool())
{
m_iGrenadeDropCapabilities = (eGrenadeDropCapabilities)(GRENDROPCAP_GRENADE | GRENDROPCAP_ALTFIRE | GRENDROPCAP_INTERRUPTED);
}
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_PlayerCompanion::CreateBehaviors()
{
#ifdef HL2_EPISODIC
@ -4179,6 +4194,38 @@ void CNPC_PlayerCompanion::OnPlayerKilledOther( CBaseEntity *pVictim, const CTak
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CNPC_PlayerCompanion::Event_Killed( const CTakeDamageInfo &info )
{
// For now, allied player companions are set to always drop grenades and other items
// even if the player did not kill them
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (!IsPlayerAlly( pPlayer ))
{
pPlayer = ToBasePlayer( info.GetAttacker() );
// See if there's a player in a vehicle instead (from CNPC_CombineS)
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 )
{
// Drop grenades if we should
DropGrenadeItemsOnDeath( info, pPlayer );
}
BaseClass::Event_Killed( info );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )

View File

@ -108,8 +108,10 @@ class CNPC_PlayerCompanion : public CAI_PlayerAlly
{
DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly );
#endif
public:
CNPC_PlayerCompanion();
//---------------------------------
bool CreateBehaviors();
void Precache();
@ -237,6 +239,7 @@ public:
// This is just here to overwrite ai_playerally's TLK_ENEMY_DEAD
virtual void OnKilledNPC(CBaseCombatCharacter *pKilled) {}
virtual void Event_Killed( const CTakeDamageInfo &info );
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
virtual void DoCustomCombatAI( void );
#endif
@ -337,13 +340,18 @@ public:
bool AllowReadinessValueChange( void );
#ifdef MAPBASE
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0; }
virtual bool IsAltFireCapable() { return (m_iGrenadeCapabilities & GRENCAP_ALTFIRE) != 0 && BaseClass::IsAltFireCapable(); }
virtual bool IsGrenadeCapable() { return (m_iGrenadeCapabilities & GRENCAP_GRENADE) != 0; }
virtual bool ShouldDropGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_GRENADE) != 0 && BaseClass::ShouldDropGrenades(); }
virtual bool ShouldDropInterruptedGrenades() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_INTERRUPTED) != 0 && BaseClass::ShouldDropInterruptedGrenades(); }
virtual bool ShouldDropAltFire() { return (m_iGrenadeDropCapabilities & GRENDROPCAP_ALTFIRE) != 0 && BaseClass::ShouldDropAltFire(); }
private:
// Determines whether this NPC is allowed to use grenades or alt-fire stuff.
eGrenadeCapabilities m_iGrenadeCapabilities;
eGrenadeDropCapabilities m_iGrenadeDropCapabilities;
#endif
protected:

View File

@ -11,3 +11,5 @@
int COMBINE_AE_BEGIN_ALTFIRE;
int COMBINE_AE_ALTFIRE;
ConVar ai_grenade_always_drop( "ai_grenade_always_drop", "0", FCVAR_NONE, "Causes non-Combine grenade user NPCs to be allowed to drop grenades, alt-fire items, etc. *IF* the keyvalue has not already been set in Hammer. This is useful for debugging purposes or if a mod which was developed before this feature was introduced wants its NPCs to drop grenades and beyond without recompiling all of the maps." );

View File

@ -19,6 +19,9 @@
#include "basegrenade_shared.h"
#include "ai_squad.h"
#include "GlobalStrings.h"
#include "gameweaponmanager.h"
#include "hl2_gamerules.h"
#include "weapon_physcannon.h"
#define COMBINE_AE_GREN_TOSS ( 7 )
@ -36,6 +39,7 @@
DEFINE_FIELD( m_flNextAltFireTime, FIELD_TIME ), \
DEFINE_FIELD( m_vecAltFireTarget, FIELD_VECTOR ), \
DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \
DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), \
DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \
DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \
@ -61,12 +65,22 @@
extern int COMBINE_AE_BEGIN_ALTFIRE;
extern int COMBINE_AE_ALTFIRE;
extern ConVar ai_grenade_always_drop;
enum eGrenadeCapabilities
{
GRENCAP_GRENADE = (1 << 0),
GRENCAP_ALTFIRE = (1 << 1),
};
// What grenade/item types NPCs are capable of dropping
enum eGrenadeDropCapabilities
{
GRENDROPCAP_GRENADE = (1 << 0),
GRENDROPCAP_ALTFIRE = (1 << 1),
GRENDROPCAP_INTERRUPTED = (1 << 2), // Drops grenades when interrupted mid-animation
};
//-----------------------------------------------------------------------------
// Other classes can use this and access some CAI_GrenadeUser functions.
//-----------------------------------------------------------------------------
@ -102,7 +116,8 @@ public:
m_OnOutOfGrenades.Set( pLastGrenade, pLastGrenade, this );
}
virtual bool IsAltFireCapable() { return false; }
// Use secondary ammo as a way of checking if this is a weapon which can be alt-fired (e.g. AR2 or SMG)
virtual bool IsAltFireCapable() { return (GetActiveWeapon() && GetActiveWeapon()->UsesSecondaryAmmo()); }
virtual bool IsGrenadeCapable() { return true; }
inline bool HasGrenades() { return m_iNumGrenades > 0; }
@ -113,6 +128,7 @@ public:
virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; }
void HandleAnimEvent( animevent_t *pEvent );
void SetActivity( Activity NewActivity );
// Soldiers use "lefthand", cops use "LHand", and citizens use "anim_attachment_LH"
virtual const char* GetGrenadeAttachment() { return "anim_attachment_LH"; }
@ -131,6 +147,12 @@ public:
// For OnThrowGrenade + point_entity_replace, see grenade_frag.cpp
bool UsingOnThrowGrenade() { return m_OnThrowGrenade.NumberOfElements() > 0; }
// For dropping grenades and beyond
void DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer );
virtual bool ShouldDropGrenades() { return HasGrenades(); }
virtual bool ShouldDropInterruptedGrenades() { return true; }
virtual bool ShouldDropAltFire() { return HasGrenades(); }
protected:
void StartTask_FaceAltFireTarget( const Task_t *pTask );
@ -151,6 +173,9 @@ protected: // We can't have any private saved variables because only derived cla
Vector m_vecAltFireTarget;
Vector m_vecTossVelocity;
// CNPC_Combine port for determining if we tossed a grenade
int m_iLastAnimEventHandled;
COutputEHANDLE m_OnThrowGrenade;
COutputEHANDLE m_OnOutOfGrenades;
};
@ -166,6 +191,8 @@ void CAI_GrenadeUser<BASE_NPC>::HandleAnimEvent( animevent_t *pEvent )
if (this->GetActiveWeapon())
this->GetActiveWeapon()->WeaponSound( SPECIAL1 );
m_iLastAnimEventHandled = pEvent->event;
//SpeakIfAllowed( TLK_CMB_THROWGRENADE, "altfire:1" );
return;
}
@ -185,6 +212,8 @@ void CAI_GrenadeUser<BASE_NPC>::HandleAnimEvent( animevent_t *pEvent )
AddGrenades(-1);
m_iLastAnimEventHandled = pEvent->event;
return;
}
@ -221,12 +250,25 @@ void CAI_GrenadeUser<BASE_NPC>::HandleAnimEvent( animevent_t *pEvent )
// wait six seconds before even looking again to see if a grenade can be thrown.
m_flNextGrenadeCheck = gpGlobals->curtime + 6;
m_iLastAnimEventHandled = pEvent->event;
return;
}
BaseClass::HandleAnimEvent( pEvent );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class BASE_NPC>
void CAI_GrenadeUser<BASE_NPC>::SetActivity( Activity NewActivity )
{
BaseClass::SetActivity( NewActivity );
m_iLastAnimEventHandled = -1;
}
//-----------------------------------------------------------------------------
// Purpose: Force the combine soldier to throw a grenade at the target
// If I'm a combine elite, fire my combine ball at the target instead.
@ -534,6 +576,105 @@ void CAI_GrenadeUser<BASE_NPC>::ClearAttackConditions()
}
}
//-----------------------------------------------------------------------------
// Purpose: Drops grenades and alt-fire items on death. Based on code from npc_combines.cpp and npc_combine.cpp
//-----------------------------------------------------------------------------
template <class BASE_NPC>
void CAI_GrenadeUser<BASE_NPC>::DropGrenadeItemsOnDeath( const CTakeDamageInfo &info, CBasePlayer *pPlayer )
{
// Elites drop alt-fire ammo, so long as they weren't killed by dissolving.
if( IsAltFireCapable() && ShouldDropAltFire() )
{
CBaseEntity *pItem;
if (this->GetActiveWeapon() && FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" ))
pItem = this->DropItem( "item_ammo_smg1_grenade", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
else
pItem = this->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 );
}
}
}
if ( IsGrenadeCapable() )
{
if ( ShouldDropGrenades() )
{
CHalfLife2 *pHL2GameRules = static_cast<CHalfLife2 *>(g_pGameRules);
// Attempt to drop a grenade
if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) )
{
this->DropItem( "weapon_frag", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
pHL2GameRules->NPC_DroppedGrenade();
}
}
// if I was killed before I could finish throwing my grenade, drop
// a grenade item that the player can retrieve.
if (GetActivity() == ACT_RANGE_ATTACK2 && ShouldDropInterruptedGrenades())
{
if( m_iLastAnimEventHandled != COMBINE_AE_GREN_TOSS )
{
// Drop the grenade as an item.
Vector vecStart;
this->GetAttachment( GetGrenadeAttachment(), vecStart );
CBaseEntity *pItem = DropItem( "weapon_frag", vecStart, RandomAngle(0,360) );
if ( pItem )
{
IPhysicsObject *pObj = pItem->VPhysicsGetObject();
if ( pObj )
{
Vector vel;
vel.x = random->RandomFloat( -100.0f, 100.0f );
vel.y = random->RandomFloat( -100.0f, 100.0f );
vel.z = random->RandomFloat( 800.0f, 1200.0f );
AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f );
vel[2] = 0.0f;
pObj->AddVelocity( &vel, &angImp );
}
// In the Citadel we need to dissolve this
if ( PlayerHasMegaPhysCannon() && GlobalEntity_GetCounter("super_phys_gun") != 1 )
{
CBaseCombatWeapon *pWeapon = static_cast<CBaseCombatWeapon *>(pItem);
pWeapon->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Task helpers
//-----------------------------------------------------------------------------