You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
7.8 KiB
290 lines
7.8 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
|
|
#include "gameweaponmanager.h" |
|
#include "saverestore_utlvector.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//========================================================= |
|
//========================================================= |
|
class CGameWeaponManager; |
|
static CUtlVector<CGameWeaponManager *> g_Managers; |
|
|
|
|
|
//========================================================= |
|
//========================================================= |
|
class CGameWeaponManager : public CBaseEntity |
|
{ |
|
DECLARE_CLASS( CGameWeaponManager, CBaseEntity ); |
|
DECLARE_DATADESC(); |
|
|
|
public: |
|
void Spawn(); |
|
CGameWeaponManager() |
|
{ |
|
m_flAmmoMod = 1.0f; |
|
m_bExpectingWeapon = false; |
|
g_Managers.AddToTail( this ); |
|
} |
|
|
|
~CGameWeaponManager() |
|
{ |
|
g_Managers.FindAndRemove( this ); |
|
} |
|
|
|
void Think(); |
|
void InputSetMaxPieces( inputdata_t &inputdata ); |
|
void InputSetAmmoModifier( inputdata_t &inputdata ); |
|
|
|
string_t m_iszWeaponName; |
|
int m_iMaxPieces; |
|
float m_flAmmoMod; |
|
bool m_bExpectingWeapon; |
|
|
|
CUtlVector<EHANDLE> m_ManagedNonWeapons; |
|
|
|
}; |
|
|
|
BEGIN_DATADESC( CGameWeaponManager ) |
|
|
|
//fields |
|
DEFINE_KEYFIELD( m_iszWeaponName, FIELD_STRING, "weaponname" ), |
|
DEFINE_KEYFIELD( m_iMaxPieces, FIELD_INTEGER, "maxpieces" ), |
|
DEFINE_KEYFIELD( m_flAmmoMod, FIELD_FLOAT, "ammomod" ), |
|
DEFINE_FIELD( m_bExpectingWeapon, FIELD_BOOLEAN ), |
|
// funcs |
|
DEFINE_FUNCTION( Think ), |
|
// inputs |
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxPieces", InputSetMaxPieces ), |
|
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAmmoModifier", InputSetAmmoModifier ), |
|
|
|
DEFINE_UTLVECTOR( m_ManagedNonWeapons, FIELD_EHANDLE ), |
|
|
|
END_DATADESC() |
|
|
|
LINK_ENTITY_TO_CLASS( game_weapon_manager, CGameWeaponManager ); |
|
|
|
void CreateWeaponManager( const char *pWeaponName, int iMaxPieces ) |
|
{ |
|
CGameWeaponManager *pManager = (CGameWeaponManager *)CreateEntityByName( "game_weapon_manager"); |
|
|
|
if( pManager ) |
|
{ |
|
pManager->m_iszWeaponName = MAKE_STRING( pWeaponName ); |
|
pManager->m_iMaxPieces = iMaxPieces; |
|
DispatchSpawn( pManager ); |
|
} |
|
} |
|
|
|
void WeaponManager_AmmoMod( CBaseCombatWeapon *pWeapon ) |
|
{ |
|
for ( int i = 0; i < g_Managers.Count(); i++ ) |
|
{ |
|
if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) |
|
{ |
|
int iNewClip = (int)(pWeapon->m_iClip1 * g_Managers[i]->m_flAmmoMod); |
|
int iNewRandomClip = iNewClip + RandomInt( -2, 2 ); |
|
|
|
if ( iNewRandomClip > pWeapon->GetMaxClip1() ) |
|
{ |
|
iNewRandomClip = pWeapon->GetMaxClip1(); |
|
} |
|
else if ( iNewRandomClip <= 0 ) |
|
{ |
|
//Drop at least one bullet. |
|
iNewRandomClip = 1; |
|
} |
|
|
|
pWeapon->m_iClip1 = iNewRandomClip; |
|
} |
|
} |
|
} |
|
|
|
void WeaponManager_AddManaged( CBaseEntity *pWeapon ) |
|
{ |
|
for ( int i = 0; i < g_Managers.Count(); i++ ) |
|
{ |
|
if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) |
|
{ |
|
Assert( g_Managers[i]->m_ManagedNonWeapons.Find( pWeapon ) == g_Managers[i]->m_ManagedNonWeapons.InvalidIndex() ); |
|
g_Managers[i]->m_ManagedNonWeapons.AddToTail( pWeapon ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void WeaponManager_RemoveManaged( CBaseEntity *pWeapon ) |
|
{ |
|
for ( int i = 0; i < g_Managers.Count(); i++ ) |
|
{ |
|
if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) |
|
{ |
|
int j = g_Managers[i]->m_ManagedNonWeapons.Find( pWeapon ); |
|
if ( j != g_Managers[i]->m_ManagedNonWeapons.InvalidIndex() ) |
|
{ |
|
g_Managers[i]->m_ManagedNonWeapons.FastRemove( j ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CGameWeaponManager::Spawn() |
|
{ |
|
SetThink( &CGameWeaponManager::Think ); |
|
SetNextThink( gpGlobals->curtime ); |
|
CBaseEntity *pEntity = CreateEntityByName( STRING(m_iszWeaponName) ); |
|
if ( !pEntity ) |
|
{ |
|
DevMsg("%s removed itself!\n", GetDebugName() ); |
|
UTIL_Remove(this); |
|
} |
|
else |
|
{ |
|
m_bExpectingWeapon = ( dynamic_cast<CBaseCombatWeapon *>(pEntity) != NULL ); |
|
UTIL_Remove(pEntity); |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
// Count of all the weapons in the world of my type and |
|
// see if we have a surplus. If there is a surplus, try |
|
// to find suitable candidates for removal. |
|
// |
|
// Right now we just remove the first weapons we find that |
|
// are behind the player, or are out of the player's PVS. |
|
// Later, we may want to score the results so that we |
|
// removed the farthest gun that's not in the player's |
|
// viewcone, etc. |
|
// |
|
// Some notes and thoughts: |
|
// |
|
// This code is designed NOT to remove weapons that are |
|
// hand-placed by level designers. It should only clean |
|
// up weapons dropped by dead NPCs, which is useful in |
|
// situations where enemies are spawned in for a sustained |
|
// period of time. |
|
// |
|
// Right now we PREFER to remove weapons that are not in the |
|
// player's PVS, but this could be opposite of what we |
|
// really want. We may only want to conduct the cleanup on |
|
// weapons that are IN the player's PVS. |
|
//--------------------------------------------------------- |
|
void CGameWeaponManager::Think() |
|
{ |
|
int i; |
|
|
|
// Don't have to think all that often. |
|
SetNextThink( gpGlobals->curtime + 2.0 ); |
|
|
|
const char *pszWeaponName = STRING( m_iszWeaponName ); |
|
|
|
CUtlVector<CBaseEntity *> candidates( 0, 64 ); |
|
|
|
if ( m_bExpectingWeapon ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = NULL; |
|
// Firstly, count the total number of weapons of this type in the world. |
|
// Also count how many of those can potentially be removed. |
|
pWeapon = assert_cast<CBaseCombatWeapon *>(gEntList.FindEntityByClassname( pWeapon, pszWeaponName )); |
|
|
|
while( pWeapon ) |
|
{ |
|
if( !pWeapon->IsEffectActive( EF_NODRAW ) && pWeapon->IsRemoveable() ) |
|
{ |
|
candidates.AddToTail( pWeapon ); |
|
} |
|
|
|
pWeapon = assert_cast<CBaseCombatWeapon *>(gEntList.FindEntityByClassname( pWeapon, pszWeaponName )); |
|
} |
|
} |
|
else |
|
{ |
|
for ( i = 0; i < m_ManagedNonWeapons.Count(); i++) |
|
{ |
|
CBaseEntity *pEntity = m_ManagedNonWeapons[i]; |
|
if ( pEntity ) |
|
{ |
|
Assert( pEntity->m_iClassname == m_iszWeaponName ); |
|
if ( !pEntity->IsEffectActive( EF_NODRAW ) ) |
|
{ |
|
candidates.AddToTail( pEntity ); |
|
} |
|
} |
|
else |
|
{ |
|
m_ManagedNonWeapons.FastRemove( i-- ); |
|
} |
|
} |
|
} |
|
|
|
// Calculate the surplus. |
|
int surplus = candidates.Count() - m_iMaxPieces; |
|
|
|
// Based on what the player can see, try to clean up the world by removing weapons that |
|
// the player cannot see right at the moment. |
|
CBaseEntity *pCandidate; |
|
for ( i = 0; i < candidates.Count() && surplus > 0; i++ ) |
|
{ |
|
bool fRemovedOne = false; |
|
|
|
pCandidate = candidates[i]; |
|
Assert( !pCandidate->IsEffectActive( EF_NODRAW ) ); |
|
|
|
if ( gpGlobals->maxClients == 1 ) |
|
{ |
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); |
|
// Nodraw serves as a flag that this weapon is already being removed since |
|
// all we're really doing inside this loop is marking them for removal by |
|
// the entity system. We don't want to count the same weapon as removed |
|
// more than once. |
|
if( !UTIL_FindClientInPVS( pCandidate->edict() ) ) |
|
{ |
|
fRemovedOne = true; |
|
} |
|
else if( !pPlayer->FInViewCone( pCandidate ) ) |
|
{ |
|
fRemovedOne = true; |
|
} |
|
else if ( UTIL_DistApprox( pPlayer->GetAbsOrigin(), pCandidate->GetAbsOrigin() ) > (30*12) ) |
|
{ |
|
fRemovedOne = true; |
|
} |
|
} |
|
else |
|
{ |
|
fRemovedOne = true; |
|
} |
|
|
|
if( fRemovedOne ) |
|
{ |
|
pCandidate->AddEffects( EF_NODRAW ); |
|
UTIL_Remove( pCandidate ); |
|
|
|
DevMsg( 2, "Surplus %s removed\n", pszWeaponName); |
|
surplus--; |
|
} |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CGameWeaponManager::InputSetMaxPieces( inputdata_t &inputdata ) |
|
{ |
|
m_iMaxPieces = inputdata.value.Int(); |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CGameWeaponManager::InputSetAmmoModifier( inputdata_t &inputdata ) |
|
{ |
|
m_flAmmoMod = inputdata.value.Float(); |
|
}
|
|
|