From 2a635526ef01458760817dff8910f48852ba1c8d Mon Sep 17 00:00:00 2001 From: jhh8 Date: Sun, 26 Apr 2026 13:20:54 +0300 Subject: [PATCH 1/2] allow weapons to define which marine class they can be picked up by "ClassRequirementOverride" keyvalue and SetMarineClassRequirement vscript function -1 = default behaviour 0 = officer 1 = SW 2 = medic 3 = tech --- reactivedrop/fgd/swarm.fgd | 8 ++++++++ src/game/client/swarm/c_asw_pickup_weapon.cpp | 3 ++- src/game/client/swarm/c_asw_pickup_weapon.h | 1 + src/game/client/swarm/c_asw_weapon.cpp | 2 ++ src/game/client/swarm/c_asw_weapon.h | 1 + src/game/server/swarm/asw_marine.cpp | 8 ++++---- src/game/server/swarm/asw_pickup_equipment.cpp | 4 ++++ src/game/server/swarm/asw_pickup_weapon.cpp | 7 ++++++- src/game/server/swarm/asw_pickup_weapon.h | 1 + src/game/server/swarm/asw_weapon.cpp | 9 +++++++++ src/game/server/swarm/asw_weapon.h | 2 ++ src/game/shared/swarm/asw_gamerules.cpp | 12 +++++++----- src/game/shared/swarm/asw_gamerules.h | 2 +- src/game/shared/swarm/asw_pickup_shared.cpp | 2 +- src/game/shared/swarm/asw_weapon_shared.cpp | 2 +- 15 files changed, 50 insertions(+), 14 deletions(-) diff --git a/reactivedrop/fgd/swarm.fgd b/reactivedrop/fgd/swarm.fgd index 33294f215..d5cd3bdab 100644 --- a/reactivedrop/fgd/swarm.fgd +++ b/reactivedrop/fgd/swarm.fgd @@ -2995,6 +2995,14 @@ 0 : "No" 1 : "Yes" ] + ClassRequirementOverride(choices) : "Class Requirement Override" : -1 : "Change which marine class can pickup this item" = + [ + -1 : "No Change" + 0 : "Officer" + 1 : "Special Weapons" + 2 : "Medic" + 3 : "Tech" + ] ] @PointClass base(Targetname, Parentname, Angles) studio("models/items/Ammobag/AmmoBag.mdl") = asw_ammo_drop : "Generic ammo stash" diff --git a/src/game/client/swarm/c_asw_pickup_weapon.cpp b/src/game/client/swarm/c_asw_pickup_weapon.cpp index 58db0a02c..83029ab79 100644 --- a/src/game/client/swarm/c_asw_pickup_weapon.cpp +++ b/src/game/client/swarm/c_asw_pickup_weapon.cpp @@ -22,7 +22,8 @@ IMPLEMENT_CLIENTCLASS_DT( C_ASW_Pickup_Weapon, DT_ASW_Pickup_Weapon, CASW_Pickup RecvPropInt (RECVINFO(m_iBulletsInGun)), RecvPropInt (RECVINFO(m_iClips)), RecvPropInt (RECVINFO(m_iSecondary)), - RecvPropBool (RECVINFO(m_bIsTemporaryPickup)), + RecvPropBool (RECVINFO(m_bIsTemporaryPickup)), + RecvPropInt (RECVINFO(m_iClassRequirementOverride)), END_RECV_TABLE() C_ASW_Pickup_Weapon::C_ASW_Pickup_Weapon() diff --git a/src/game/client/swarm/c_asw_pickup_weapon.h b/src/game/client/swarm/c_asw_pickup_weapon.h index 522273092..a135a559b 100644 --- a/src/game/client/swarm/c_asw_pickup_weapon.h +++ b/src/game/client/swarm/c_asw_pickup_weapon.h @@ -23,6 +23,7 @@ class C_ASW_Pickup_Weapon : public C_ASW_Pickup CNetworkVar(int, m_iClips); CNetworkVar(int, m_iSecondary); CNetworkVar(bool, m_bIsTemporaryPickup); + CNetworkVar(int, m_iClassRequirementOverride); int m_nUseIconTextureID; bool m_bWideIcon; diff --git a/src/game/client/swarm/c_asw_weapon.cpp b/src/game/client/swarm/c_asw_weapon.cpp index a9fb3d842..9abf49302 100644 --- a/src/game/client/swarm/c_asw_weapon.cpp +++ b/src/game/client/swarm/c_asw_weapon.cpp @@ -64,6 +64,7 @@ BEGIN_NETWORK_TABLE( CASW_Weapon, DT_ASW_Weapon ) RecvPropBool ( RECVINFO( m_bIsTemporaryPickup ) ), RecvPropEHandle( RECVINFO( m_hOriginalOwnerMR ) ), RecvPropInt( RECVINFO( m_iInventoryEquipSlot ) ), + RecvPropInt( RECVINFO( m_iClassRequirementOverride ) ), END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( C_ASW_Weapon ) @@ -169,6 +170,7 @@ m_GlowObject( this, glow_outline_color_weapon.GetColorAsVector(), 1.0f, false, t m_bPoweredUp = false; m_bIsTemporaryPickup = false; + m_iClassRequirementOverride = -1; } diff --git a/src/game/client/swarm/c_asw_weapon.h b/src/game/client/swarm/c_asw_weapon.h index 534d53196..0c8f5befa 100644 --- a/src/game/client/swarm/c_asw_weapon.h +++ b/src/game/client/swarm/c_asw_weapon.h @@ -202,6 +202,7 @@ class C_ASW_Weapon : public C_BaseCombatWeapon, public IASW_Client_Usable_Entity virtual bool AllowedToPickup( C_ASW_Inhabitable_NPC *pNPC ); bool m_bSwappingWeapon; bool m_bIsTemporaryPickup; + int m_iClassRequirementOverride; // check if this weapon wants to perform a sync kill virtual bool CheckSyncKill( byte &forced_action, short &sync_kill_ent ) { return false; } diff --git a/src/game/server/swarm/asw_marine.cpp b/src/game/server/swarm/asw_marine.cpp index f2e08f443..a60898d0e 100644 --- a/src/game/server/swarm/asw_marine.cpp +++ b/src/game/server/swarm/asw_marine.cpp @@ -3000,9 +3000,9 @@ bool CASW_Marine::TakeWeaponPickup( CASW_Weapon *pWeapon ) // check we're allowed to take this item bool bAllowed = true; if (pOldWeapon) // we're swapping with an existing weapon - bAllowed = ASWGameRules()->MarineCanPickup(GetMarineResource(), pWeapon->GetClassname(), pOldWeapon->GetClassname()); + bAllowed = ASWGameRules()->MarineCanPickup(GetMarineResource(), pWeapon->GetClassname(), pOldWeapon->GetClassname(), pWeapon->m_iClassRequirementOverride); else // we're putting it into an empty slot - bAllowed = ASWGameRules()->MarineCanPickup(GetMarineResource(), pWeapon->GetClassname()); + bAllowed = ASWGameRules()->MarineCanPickup(GetMarineResource(), pWeapon->GetClassname(), NULL, pWeapon->m_iClassRequirementOverride); if (!bAllowed) return false; @@ -3096,11 +3096,11 @@ bool CASW_Marine::TakeWeaponPickup(CASW_Pickup_Weapon* pPickup) bool bAllowed = true; if ( pWeapon ) // we're swapping with an existing weapon { - bAllowed = ASWGameRules()->MarineCanPickup( GetMarineResource(), pPickup->GetWeaponClass(), pWeapon->GetClassname() ); + bAllowed = ASWGameRules()->MarineCanPickup( GetMarineResource(), pPickup->GetWeaponClass(), pWeapon->GetClassname(), pPickup->m_iClassRequirementOverride ); } else // we're putting it into an empty slot { - bAllowed = ASWGameRules()->MarineCanPickup( GetMarineResource(), pPickup->GetWeaponClass() ); + bAllowed = ASWGameRules()->MarineCanPickup( GetMarineResource(), pPickup->GetWeaponClass(), NULL, pPickup->m_iClassRequirementOverride ); } if ( !bAllowed ) diff --git a/src/game/server/swarm/asw_pickup_equipment.cpp b/src/game/server/swarm/asw_pickup_equipment.cpp index 30508f2b0..ac68bf12c 100644 --- a/src/game/server/swarm/asw_pickup_equipment.cpp +++ b/src/game/server/swarm/asw_pickup_equipment.cpp @@ -212,6 +212,7 @@ void CASW_Pickup_Weapon_Ammo_Bag::InitFrom(CASW_Marine* pMarine, CASW_Weapon* pW m_iClips = 0; m_iSecondary = 0; m_bIsTemporaryPickup = pWeapon->m_bIsTemporaryPickup; + m_iClassRequirementOverride = pWeapon->m_iClassRequirementOverride; for (int i=0;iSetClip1( 0 ); pWeapon->SetClip2( 0 ); pWeapon->m_bIsTemporaryPickup = m_bIsTemporaryPickup; + pWeapon->m_iClassRequirementOverride = m_iClassRequirementOverride; // equip the weapon pMarine->Weapon_Equip_In_Index( pWeapon, pMarine->GetWeaponPositionForPickup( GetWeaponClass(), m_bIsTemporaryPickup ) ); @@ -287,6 +289,7 @@ void CASW_Pickup_Weapon_Ammo_Satchel::InitFrom(CASW_Marine* pMarine, CASW_Weapon //m_nAmmoDrops = pSatchel->m_nAmmoDrops; m_bIsTemporaryPickup = pWeapon->m_bIsTemporaryPickup; + m_iClassRequirementOverride = pWeapon->m_iClassRequirementOverride; } void CASW_Pickup_Weapon_Ammo_Satchel::InitWeapon(CASW_Marine* pMarine, CASW_Weapon* pWeapon) @@ -301,6 +304,7 @@ void CASW_Pickup_Weapon_Ammo_Satchel::InitWeapon(CASW_Marine* pMarine, CASW_Weap //pWeapon->SetClip1( 0 ); //pWeapon->SetClip2( 0 ); pWeapon->m_bIsTemporaryPickup = m_bIsTemporaryPickup; + m_iClassRequirementOverride = pWeapon->m_iClassRequirementOverride; // equip the weapon pMarine->Weapon_Equip_In_Index( pWeapon, pMarine->GetWeaponPositionForPickup( GetWeaponClass(), m_bIsTemporaryPickup ) ); diff --git a/src/game/server/swarm/asw_pickup_weapon.cpp b/src/game/server/swarm/asw_pickup_weapon.cpp index 787def2b7..306e3d8b9 100644 --- a/src/game/server/swarm/asw_pickup_weapon.cpp +++ b/src/game/server/swarm/asw_pickup_weapon.cpp @@ -16,7 +16,8 @@ IMPLEMENT_SERVERCLASS_ST(CASW_Pickup_Weapon, DT_ASW_Pickup_Weapon) SendPropInt (SENDINFO(m_iBulletsInGun), 16 ), SendPropInt (SENDINFO(m_iClips), 10 ), SendPropInt (SENDINFO(m_iSecondary), 10 ), - SendPropBool (SENDINFO(m_bIsTemporaryPickup) ), + SendPropBool (SENDINFO(m_bIsTemporaryPickup) ), + SendPropInt (SENDINFO(m_iClassRequirementOverride) ), END_SEND_TABLE() BEGIN_DATADESC( CASW_Pickup_Weapon ) @@ -24,11 +25,13 @@ BEGIN_DATADESC( CASW_Pickup_Weapon ) DEFINE_KEYFIELD( m_iClips, FIELD_INTEGER, "Clips"), DEFINE_KEYFIELD( m_iSecondary, FIELD_INTEGER, "SecondaryBullets"), DEFINE_KEYFIELD( m_bIsTemporaryPickup, FIELD_BOOLEAN, "IsTemporaryPickup"), + DEFINE_KEYFIELD( m_iClassRequirementOverride, FIELD_INTEGER, "ClassRequirementOverride" ), END_DATADESC() CASW_Pickup_Weapon::CASW_Pickup_Weapon() { m_bIsTemporaryPickup = false; + m_iClassRequirementOverride = -1; } //--------------------------------------------------------- @@ -79,6 +82,7 @@ void CASW_Pickup_Weapon::InitFrom(CASW_Marine* pMarine, CASW_Weapon* pWeapon) m_iClips = bullets_on_player / pWeapon->GetMaxClip1(); m_iSecondary = pWeapon->Clip2(); m_bIsTemporaryPickup = pWeapon->m_bIsTemporaryPickup; + m_iClassRequirementOverride = pWeapon->m_iClassRequirementOverride; if (pMarine->GetNumberOfWeaponsUsingAmmo(iAmmoIndex) > 1) { @@ -107,6 +111,7 @@ void CASW_Pickup_Weapon::InitWeapon(CASW_Marine* pMarine, CASW_Weapon* pWeapon) pMarine->GiveAmmo(GetNumClips() * pWeapon->GetMaxClip1(), pWeapon->GetPrimaryAmmoType()); pWeapon->m_bIsTemporaryPickup = m_bIsTemporaryPickup; + pWeapon->m_iClassRequirementOverride = m_iClassRequirementOverride; } // player has used this item diff --git a/src/game/server/swarm/asw_pickup_weapon.h b/src/game/server/swarm/asw_pickup_weapon.h index 538b80542..dc638bb47 100644 --- a/src/game/server/swarm/asw_pickup_weapon.h +++ b/src/game/server/swarm/asw_pickup_weapon.h @@ -33,6 +33,7 @@ class CASW_Pickup_Weapon : public CASW_Pickup CNetworkVar(int, m_iClips); CNetworkVar(int, m_iSecondary); CNetworkVar(bool, m_bIsTemporaryPickup); + CNetworkVar(int, m_iClassRequirementOverride); }; class CASW_Pickup_Weapon_Rifle : public CASW_Pickup_Weapon diff --git a/src/game/server/swarm/asw_weapon.cpp b/src/game/server/swarm/asw_weapon.cpp index d493bb005..8632b109c 100644 --- a/src/game/server/swarm/asw_weapon.cpp +++ b/src/game/server/swarm/asw_weapon.cpp @@ -59,6 +59,7 @@ IMPLEMENT_SERVERCLASS_ST( CASW_Weapon, DT_ASW_Weapon ) SendPropBool( SENDINFO( m_bIsTemporaryPickup ) ), SendPropEHandle( SENDINFO( m_hOriginalOwnerMR ) ), SendPropInt( SENDINFO( m_iInventoryEquipSlot ), 2, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iClassRequirementOverride ) ), END_SEND_TABLE() //--------------------------------------------------------- @@ -72,6 +73,7 @@ BEGIN_DATADESC( CASW_Weapon ) DEFINE_FIELD( m_fFastReloadStart, FIELD_TIME ), DEFINE_FIELD( m_fFastReloadEnd, FIELD_TIME ), DEFINE_KEYFIELD( m_bIsTemporaryPickup, FIELD_BOOLEAN, "IsTemporaryPickup" ), + DEFINE_KEYFIELD( m_iClassRequirementOverride, FIELD_INTEGER, "ClassRequirementOverride" ), DEFINE_INPUTFUNC( FIELD_EHANDLE, "ForcePickUp", InputForcePickUp ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceDrop", InputForceDrop ), DEFINE_OUTPUT( m_OnPickedUp, "OnPickedUp" ), @@ -79,6 +81,7 @@ BEGIN_DATADESC( CASW_Weapon ) END_DATADESC() BEGIN_ENT_SCRIPTDESC( CASW_Weapon, CBaseCombatWeapon, "Alien Swarm weapon" ) + DEFINE_SCRIPTFUNC( SetMarineClassRequirement, "-1 = default behaviour, 0 = officer, 1 = SW, 2 = medic, 3 = tech" ) END_SCRIPTDESC() ConVar asw_weapon_safety_hull("asw_weapon_safety_hull", "0", FCVAR_CHEAT, "Size of hull used to check for AI shots going too near a friendly"); @@ -109,6 +112,7 @@ CASW_Weapon::CASW_Weapon() m_bIsTemporaryPickup = false; m_hOriginalOwnerMR = NULL; m_iInventoryEquipSlot = 0; + m_iClassRequirementOverride = -1; } @@ -452,3 +456,8 @@ void CASW_Weapon::InputForceDrop( inputdata_t &data ) pMarine->DropWeapon( this, false ); } + +void CASW_Weapon::SetMarineClassRequirement( int nClass ) +{ + m_iClassRequirementOverride = nClass; +} \ No newline at end of file diff --git a/src/game/server/swarm/asw_weapon.h b/src/game/server/swarm/asw_weapon.h index 7b7e63bd2..71355c08b 100644 --- a/src/game/server/swarm/asw_weapon.h +++ b/src/game/server/swarm/asw_weapon.h @@ -157,6 +157,7 @@ class CASW_Weapon : public CBaseCombatWeapon, public IASW_Server_Usable_Entity IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_flTimeWeaponIdle ); CNetworkVar(bool, m_bIsTemporaryPickup); + CNetworkVar(int, m_iClassRequirementOverride); // effects virtual const char* GetUTracerType(); @@ -170,6 +171,7 @@ class CASW_Weapon : public CBaseCombatWeapon, public IASW_Server_Usable_Entity bool IsBeingCarried() const; bool IsCarriedByLocalPlayer(); virtual void FallInit(); + void SetMarineClassRequirement( int nClass ); // check if this weapon wants to perform a sync kill virtual bool CheckSyncKill( byte &forced_action, short &sync_kill_ent ) { return false; } diff --git a/src/game/shared/swarm/asw_gamerules.cpp b/src/game/shared/swarm/asw_gamerules.cpp index 4b2be42fc..52e254585 100644 --- a/src/game/shared/swarm/asw_gamerules.cpp +++ b/src/game/shared/swarm/asw_gamerules.cpp @@ -6284,7 +6284,7 @@ int CAlienSwarm::GetRandomValidWeaponSelectionExtra( CASW_Marine_Resource *pMari #endif // CLIENT_DLL -bool CAlienSwarm::MarineCanPickup( CASW_Marine_Resource *pMarineResource, const char *szWeaponClass, const char *szSwappingClass ) +bool CAlienSwarm::MarineCanPickup( CASW_Marine_Resource *pMarineResource, const char *szWeaponClass, const char *szSwappingClass, const int iRequiredClassOverride ) { if ( !pMarineResource ) return false; @@ -6312,26 +6312,28 @@ bool CAlienSwarm::MarineCanPickup( CASW_Marine_Resource *pMarineResource, const if ( bCheckRestriction ) { + const int iRequiredClass = iRequiredClassOverride != -1 ? iRequiredClassOverride : pItem->m_iRequiredClass; + // check various class skills - if ( pItem->m_iRequiredClass == MARINE_CLASS_TECH && !pProfile->CanUseTechWeapons() ) + if ( iRequiredClass == MARINE_CLASS_TECH && !pProfile->CanUseTechWeapons() ) { V_strncpy( m_szPickupDenial, "#asw_requires_tech", sizeof( m_szPickupDenial ) ); return false; } - if ( pItem->m_iRequiredClass == MARINE_CLASS_MEDIC && !pProfile->CanUseFirstAid() ) + if ( iRequiredClass == MARINE_CLASS_MEDIC && !pProfile->CanUseFirstAid() ) { V_strncpy( m_szPickupDenial, "#asw_requires_medic", sizeof( m_szPickupDenial ) ); return false; } - if ( pItem->m_iRequiredClass == MARINE_CLASS_SPECIAL_WEAPONS && pProfile->GetMarineClass() != MARINE_CLASS_SPECIAL_WEAPONS ) + if ( iRequiredClass == MARINE_CLASS_SPECIAL_WEAPONS && pProfile->GetMarineClass() != MARINE_CLASS_SPECIAL_WEAPONS ) { V_strncpy( m_szPickupDenial, "#asw_requires_sw", sizeof( m_szPickupDenial ) ); return false; } - if ( pItem->m_iRequiredClass == MARINE_CLASS_NCO && pProfile->GetMarineClass() != MARINE_CLASS_NCO ) + if ( iRequiredClass == MARINE_CLASS_NCO && pProfile->GetMarineClass() != MARINE_CLASS_NCO ) { V_strncpy( m_szPickupDenial, "#asw_requires_nco", sizeof( m_szPickupDenial ) ); return false; diff --git a/src/game/shared/swarm/asw_gamerules.h b/src/game/shared/swarm/asw_gamerules.h index d0401f6ac..165896e63 100644 --- a/src/game/shared/swarm/asw_gamerules.h +++ b/src/game/shared/swarm/asw_gamerules.h @@ -471,7 +471,7 @@ class CAlienSwarm : public CSingleplayRules // pickups int GetRandomValidWeaponSelectionRegular(CASW_Marine_Resource* pMarineResource, int nNotAllowed = -1 ); int GetRandomValidWeaponSelectionExtra(CASW_Marine_Resource* pMarineResource); - virtual bool MarineCanPickup(CASW_Marine_Resource* pMarineResource, const char* szWeaponClass, const char* szSwappingClass=NULL); + virtual bool MarineCanPickup(CASW_Marine_Resource* pMarineResource, const char* szWeaponClass, const char* szSwappingClass=NULL, const int iRequiredClassOverride = -1); virtual bool MarineCanSelectInLobby(CASW_Marine_Resource* pMarineResource, const char* szWeaponClass, const char* szSwappingClass = NULL); bool MarineCanPickupAmmo(CASW_Marine *pMarine, CASW_Ammo *pAmmo); bool MarineCanPickupPowerup(CASW_Marine *pMarine, CASW_Powerup *pPowerup); diff --git a/src/game/shared/swarm/asw_pickup_shared.cpp b/src/game/shared/swarm/asw_pickup_shared.cpp index c63fab357..a381cbe9a 100644 --- a/src/game/shared/swarm/asw_pickup_shared.cpp +++ b/src/game/shared/swarm/asw_pickup_shared.cpp @@ -51,7 +51,7 @@ bool CASW_Pickup_Weapon::AllowedToPickup( CASW_Inhabitable_NPC *pNPC ) const char *szSwappingClass = pWeapon ? pWeapon->GetClassname() : ""; // first check if the gamerules will allow it - bool bAllowed = ASWGameRules()->MarineCanPickup( pMarine->GetMarineResource(), GetWeaponClass(), szSwappingClass ); + bool bAllowed = ASWGameRules()->MarineCanPickup( pMarine->GetMarineResource(), GetWeaponClass(), szSwappingClass, m_iClassRequirementOverride ); #ifdef CLIENT_DLL m_bSwappingWeapon = ( pWeapon != NULL ); diff --git a/src/game/shared/swarm/asw_weapon_shared.cpp b/src/game/shared/swarm/asw_weapon_shared.cpp index 230915aa0..7d3dac506 100644 --- a/src/game/shared/swarm/asw_weapon_shared.cpp +++ b/src/game/shared/swarm/asw_weapon_shared.cpp @@ -1703,7 +1703,7 @@ bool CASW_Weapon::AllowedToPickup( CASW_Inhabitable_NPC *pNPC ) const char *szSwappingClass = pWeapon ? pWeapon->GetClassname() : ""; // first check if the gamerules will allow it - bool bAllowed = ASWGameRules()->MarineCanPickup( pMarine->GetMarineResource(), GetClassname(), szSwappingClass ); + bool bAllowed = ASWGameRules()->MarineCanPickup( pMarine->GetMarineResource(), GetClassname(), szSwappingClass, m_iClassRequirementOverride ); #ifdef CLIENT_DLL m_bSwappingWeapon = ( pWeapon != NULL ); #endif From d6e6b38dd8886de5bbfadd104d3124f12a494a86 Mon Sep 17 00:00:00 2001 From: jhh8 <95616101+jhh8@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:01:25 +0300 Subject: [PATCH 2/2] Update swarm.fgd --- reactivedrop/fgd/swarm.fgd | 1 + 1 file changed, 1 insertion(+) diff --git a/reactivedrop/fgd/swarm.fgd b/reactivedrop/fgd/swarm.fgd index d5cd3bdab..71648b460 100644 --- a/reactivedrop/fgd/swarm.fgd +++ b/reactivedrop/fgd/swarm.fgd @@ -3002,6 +3002,7 @@ 1 : "Special Weapons" 2 : "Medic" 3 : "Tech" + 999 : "Any" ] ]