From 49943cb4c9895696e09dc44e17b2bc419b0c8935 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Mon, 23 Feb 2026 01:34:40 +0200 Subject: [PATCH 01/29] custom weapon script loading --- sp/src/game/client/c_basecombatweapon.cpp | 14 ++ sp/src/game/client/hl2/hud_ammo.cpp | 58 +++++- sp/src/game/client/weapon_selection.cpp | 6 +- sp/src/game/server/ai_basenpc_squad.cpp | 4 +- sp/src/game/server/basecombatweapon.cpp | 9 +- sp/src/game/server/baseentity.h | 5 +- sp/src/game/server/gameweaponmanager.cpp | 8 +- sp/src/game/server/hl2/npc_alyx_episodic.cpp | 6 +- sp/src/game/server/hl2/npc_citizen17.cpp | 15 +- sp/src/game/server/hl2/npc_combine.cpp | 2 +- .../game/server/hl2/npc_playercompanion.cpp | 10 +- sp/src/game/server/mapbase/GlobalStrings.h | 2 +- sp/src/game/server/player_command.cpp | 2 +- .../game/shared/basecombatweapon_shared.cpp | 181 +++++++++++++++++- sp/src/game/shared/basecombatweapon_shared.h | 11 ++ sp/src/game/shared/weapon_parse.h | 3 + 16 files changed, 296 insertions(+), 40 deletions(-) diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index ff8e6d64f98..0f884356131 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -191,6 +191,20 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) m_iOldState = m_iState; m_bJustRestored = false; + +#ifdef MAPBASE + if (updateType == DATA_UPDATE_CREATED) + { + Precache(); //cache weapon on client again, otherwise the client will always use default script name while server not + } + + //update script in case if wanted + if (m_iOldNeedsUpdate != m_iNeedsUpdate) + { + Precache(); + m_iOldNeedsUpdate = m_iNeedsUpdate; //assign new value to prevent updates on client when we don't want + } +#endif } //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/hl2/hud_ammo.cpp b/sp/src/game/client/hl2/hud_ammo.cpp index 9e9e913991c..e3e3aa4ebfd 100644 --- a/sp/src/game/client/hl2/hud_ammo.cpp +++ b/sp/src/game/client/hl2/hud_ammo.cpp @@ -51,6 +51,9 @@ class CHudAmmo : public CHudNumericDisplay, public CHudElement int m_iAmmo; int m_iAmmo2; CHudTexture *m_iconPrimaryAmmo; +#ifdef MAPBASE + int m_iOldNeedsUpdate; //needs update tracking +#endif }; DECLARE_HUDELEMENT( CHudAmmo ); @@ -66,6 +69,9 @@ CHudAmmo::CHudAmmo( const char *pElementName ) : BaseClass(NULL, "HudAmmo"), CHu hudlcd->SetGlobalStat( "(ammo_secondary)", "0" ); hudlcd->SetGlobalStat( "(weapon_print_name)", "" ); hudlcd->SetGlobalStat( "(weapon_name)", "" ); +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; // Initialize +#endif } //----------------------------------------------------------------------------- @@ -75,6 +81,9 @@ void CHudAmmo::Init( void ) { m_iAmmo = -1; m_iAmmo2 = -1; +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; // Initialize +#endif m_iconPrimaryAmmo = NULL; @@ -107,6 +116,9 @@ void CHudAmmo::Reset() m_hCurrentVehicle = NULL; m_iAmmo = 0; m_iAmmo2 = 0; +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; // Initialize +#endif UpdateAmmoDisplays(); } @@ -158,7 +170,12 @@ void CHudAmmo::UpdatePlayerAmmo( C_BasePlayer *player ) hudlcd->SetGlobalStat( "(ammo_primary)", VarArgs( "%d", ammo1 ) ); hudlcd->SetGlobalStat( "(ammo_secondary)", VarArgs( "%d", ammo2 ) ); +#ifdef MAPBASE + int iNeedsUpdate = wpn ? wpn->m_iOldNeedsUpdate : -1; + if (wpn == m_hCurrentActiveWeapon && m_iOldNeedsUpdate == iNeedsUpdate) +#else if (wpn == m_hCurrentActiveWeapon) +#endif { // same weapon, just update counts SetAmmo(ammo1, true); @@ -185,6 +202,10 @@ void CHudAmmo::UpdatePlayerAmmo( C_BasePlayer *player ) g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("WeaponChanged"); m_hCurrentActiveWeapon = wpn; } + +#ifdef MAPBASE + m_iOldNeedsUpdate = iNeedsUpdate; +#endif } void CHudAmmo::UpdateVehicleAmmo( C_BasePlayer *player, IClientVehicle *pVehicle ) @@ -362,6 +383,9 @@ class CHudSecondaryAmmo : public CHudNumericDisplay, public CHudElement CHudSecondaryAmmo( const char *pElementName ) : BaseClass( NULL, "HudAmmoSecondary" ), CHudElement( pElementName ) { m_iAmmo = -1; +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; +#endif SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_WEAPONSELECTION | HIDEHUD_PLAYERDEAD | HIDEHUD_NEEDSUIT ); } @@ -369,6 +393,10 @@ class CHudSecondaryAmmo : public CHudNumericDisplay, public CHudElement void Init( void ) { #ifndef HL2MP +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; +#endif + wchar_t *tempString = g_pVGuiLocalize->Find("#Valve_Hud_AMMO_ALT"); if (tempString) { @@ -414,6 +442,9 @@ class CHudSecondaryAmmo : public CHudNumericDisplay, public CHudElement // hud reset, update ammo state BaseClass::Reset(); m_iAmmo = 0; +#ifdef MAPBASE + m_iOldNeedsUpdate = -1; +#endif m_hCurrentActiveWeapon = NULL; SetAlpha( 0 ); UpdateAmmoState(); @@ -473,9 +504,14 @@ class CHudSecondaryAmmo : public CHudNumericDisplay, public CHudElement SetAmmo(player->GetAmmoCount(wpn->GetSecondaryAmmoType())); } +#ifdef MAPBASE + int iNeedsUpdate = wpn ? wpn->m_iOldNeedsUpdate : -1; + if (m_hCurrentActiveWeapon != wpn || m_iOldNeedsUpdate != iNeedsUpdate) +#else if ( m_hCurrentActiveWeapon != wpn ) +#endif { - if ( wpn->UsesSecondaryAmmo() ) + if (wpn && wpn->UsesSecondaryAmmo()) { // we've changed to a weapon that uses secondary ammo g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("WeaponUsesSecondaryAmmo"); @@ -487,15 +523,35 @@ class CHudSecondaryAmmo : public CHudNumericDisplay, public CHudElement } m_hCurrentActiveWeapon = wpn; +#ifndef MAPBASE // Get the icon we should be displaying m_iconSecondaryAmmo = gWR.GetAmmoIconFromWeapon( m_hCurrentActiveWeapon->GetSecondaryAmmoType() ); +#endif + } + +#ifdef MAPBASE + // Always update the icon if weapon is valid + if (wpn && wpn->UsesSecondaryAmmo()) + { + m_iconSecondaryAmmo = gWR.GetAmmoIconFromWeapon(wpn->GetSecondaryAmmoType()); } + else + { + m_iconSecondaryAmmo = nullptr; + } + + // Update m_iOldNeedsUpdate + m_iOldNeedsUpdate = iNeedsUpdate; +#endif } private: CHandle< C_BaseCombatWeapon > m_hCurrentActiveWeapon; CHudTexture *m_iconSecondaryAmmo; int m_iAmmo; +#ifdef MAPBASE + int m_iOldNeedsUpdate; //needs update tracking +#endif }; DECLARE_HUDELEMENT( CHudSecondaryAmmo ); diff --git a/sp/src/game/client/weapon_selection.cpp b/sp/src/game/client/weapon_selection.cpp index 5568c532ec3..ad0d0c37afd 100644 --- a/sp/src/game/client/weapon_selection.cpp +++ b/sp/src/game/client/weapon_selection.cpp @@ -505,8 +505,10 @@ void CBaseHudWeaponSelection::SwitchToLastWeapon( void ) void CBaseHudWeaponSelection::SetWeaponSelected( void ) { Assert( GetSelectedWeapon() ); - // Mark selection so that it's placed into next CUserCmd created - input->MakeWeaponSelection( GetSelectedWeapon() ); + + //Mark selection so that it's placed into next CUserCmd created if isn't active (or we'll get prediction glitch with custom scripts) + if(GetSelectedWeapon() != GetActiveWeapon()) + input->MakeWeaponSelection( GetSelectedWeapon() ); } diff --git a/sp/src/game/server/ai_basenpc_squad.cpp b/sp/src/game/server/ai_basenpc_squad.cpp index 1e895536f0c..91c276ac662 100644 --- a/sp/src/game/server/ai_basenpc_squad.cpp +++ b/sp/src/game/server/ai_basenpc_squad.cpp @@ -267,7 +267,7 @@ int CAI_BaseNPC::NumWeaponsInSquad( const char *pszWeaponClassname ) if( !GetSquad() ) { - if( GetActiveWeapon() && GetActiveWeapon()->m_iClassname == iszWeaponClassname ) + if(GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), iszWeaponClassname.ToCStr())) { // I'm alone in my squad, but I do have this weapon. return 1; @@ -281,7 +281,7 @@ int CAI_BaseNPC::NumWeaponsInSquad( const char *pszWeaponClassname ) CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter ); while ( pSquadmate ) { - if( pSquadmate->GetActiveWeapon() && pSquadmate->GetActiveWeapon()->m_iClassname == iszWeaponClassname ) + if(pSquadmate->GetActiveWeapon() && FClassnameIs(pSquadmate->GetActiveWeapon(), iszWeaponClassname.ToCStr())) { count++; } diff --git a/sp/src/game/server/basecombatweapon.cpp b/sp/src/game/server/basecombatweapon.cpp index 2863b747fdc..db6666018af 100644 --- a/sp/src/game/server/basecombatweapon.cpp +++ b/sp/src/game/server/basecombatweapon.cpp @@ -201,8 +201,13 @@ CBaseEntity* CBaseCombatWeapon::Respawn( void ) // will decide when to make the weapon visible and touchable. CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() ); - if ( pNewWeapon ) + if (pNewWeapon) { +#ifdef MAPBASE + pNewWeapon->KeyValue("weaponscriptname", GetClassname()); // pass along the script name + pNewWeapon->Precache(); // precache it here to avoid prediction errors + pNewWeapon->SetModel(GetWorldModel()); // fix bbox for world model +#endif // MAPBASE pNewWeapon->AddEffects( EF_NODRAW );// invisible for now pNewWeapon->SetTouch( NULL );// no touch pNewWeapon->SetThink( &CBaseCombatWeapon::AttemptToMaterialize ); @@ -215,7 +220,7 @@ CBaseEntity* CBaseCombatWeapon::Respawn( void ) } else { - Warning("Respawn failed to create %s!\n", GetClassname() ); + Warning("Respawn failed to create %s!\n", GetClassname()); } return pNewWeapon; diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 77b7b46d898..ddeaf8fdf4c 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -645,9 +645,10 @@ class CBaseEntity : public IServerEntity const char* GetPreTemplateName(); // Not threadsafe. Get the name stripped of template unique decoration bool NameMatches( const char *pszNameOrWildcard ); - bool ClassMatches( const char *pszClassOrWildcard ); bool NameMatches( string_t nameStr ); - bool ClassMatches( string_t nameStr ); + + virtual bool ClassMatches( string_t nameStr ); + virtual bool ClassMatches( const char *pszClassOrWildcard ); private: bool NameMatchesComplex( const char *pszNameOrWildcard ); diff --git a/sp/src/game/server/gameweaponmanager.cpp b/sp/src/game/server/gameweaponmanager.cpp index 68bced8a0de..44a62266192 100644 --- a/sp/src/game/server/gameweaponmanager.cpp +++ b/sp/src/game/server/gameweaponmanager.cpp @@ -87,7 +87,7 @@ void WeaponManager_AmmoMod( CBaseCombatWeapon *pWeapon ) { for ( int i = 0; i < g_Managers.Count(); i++ ) { - if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) + if (FClassnameIs(pWeapon, g_Managers[i]->m_iszWeaponName.ToCStr())) { int iNewClip = (int)(pWeapon->m_iClip1 * g_Managers[i]->m_flAmmoMod); int iNewRandomClip = iNewClip + RandomInt( -2, 2 ); @@ -111,7 +111,7 @@ void WeaponManager_AddManaged( CBaseEntity *pWeapon ) { for ( int i = 0; i < g_Managers.Count(); i++ ) { - if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) + if (FClassnameIs(pWeapon, g_Managers[i]->m_iszWeaponName.ToCStr())) { Assert( g_Managers[i]->m_ManagedNonWeapons.Find( pWeapon ) == g_Managers[i]->m_ManagedNonWeapons.InvalidIndex() ); g_Managers[i]->m_ManagedNonWeapons.AddToTail( pWeapon ); @@ -124,7 +124,7 @@ void WeaponManager_RemoveManaged( CBaseEntity *pWeapon ) { for ( int i = 0; i < g_Managers.Count(); i++ ) { - if ( g_Managers[i]->m_iszWeaponName == pWeapon->m_iClassname ) + if (FClassnameIs(pWeapon, g_Managers[i]->m_iszWeaponName.ToCStr())) { int j = g_Managers[i]->m_ManagedNonWeapons.Find( pWeapon ); if ( j != g_Managers[i]->m_ManagedNonWeapons.InvalidIndex() ) @@ -213,7 +213,7 @@ void CGameWeaponManager::Think() CBaseEntity *pEntity = m_ManagedNonWeapons[i]; if ( pEntity ) { - Assert( pEntity->m_iClassname == m_iszWeaponName ); + Assert(FClassnameIs(pEntity, m_iszWeaponName.ToCStr())); if ( !pEntity->IsEffectActive( EF_NODRAW ) ) { candidates.AddToTail( pEntity ); diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.cpp b/sp/src/game/server/hl2/npc_alyx_episodic.cpp index dd5a35b0dda..609bb67ed4b 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/sp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -2585,7 +2585,7 @@ void CNPC_Alyx::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarge { BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); - if( pWeapon && pWeapon->ClassMatches( CLASSNAME_ALYXGUN ) ) + if(pWeapon && pWeapon->ClassMatches(CLASSNAME_ALYXGUN.ToCStr())) { pWeapon->SUB_Remove(); } @@ -3256,7 +3256,7 @@ void CNPC_Alyx::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombat void CNPC_Alyx::OnGivenWeapon( CBaseCombatWeapon *pNewWeapon ) { // HACK: This causes Alyx to pull her gun from a holstered position - if ( pNewWeapon->ClassMatches( CLASSNAME_ALYXGUN ) ) + if (pNewWeapon->ClassMatches(CLASSNAME_ALYXGUN.ToCStr())) { // Put it away so we can pull it out properly GetActiveWeapon()->Holster(); @@ -3279,7 +3279,7 @@ void CNPC_Alyx::Weapon_Equip( CBaseCombatWeapon *pWeapon ) //----------------------------------------------------------------------------- bool CNPC_Alyx::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { - if( !pWeapon->ClassMatches( CLASSNAME_SHOTGUN ) ) + if(!pWeapon->ClassMatches(CLASSNAME_SHOTGUN.ToCStr())) return false; return BaseClass::Weapon_CanUse( pWeapon ); diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index c66ca4e5237..ad225385918 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -924,18 +924,19 @@ void CNPC_Citizen::FixupMattWeapon() if ( pWeapon && pWeapon->ClassMatches( "weapon_crowbar" ) && NameMatches( "matt" ) ) #endif { - Weapon_Drop( pWeapon ); - UTIL_Remove( pWeapon ); - pWeapon = (CBaseCombatWeapon *)CREATE_UNSAVED_ENTITY( CMattsPipe, "weapon_crowbar" ); - pWeapon->SetName( AllocPooledString( "matt_weapon" ) ); - DispatchSpawn( pWeapon ); + variant_t tmpVar; + + tmpVar.SetString(MAKE_STRING("weapon_mattpipe")); + pWeapon->AcceptInput("ChangeScript", this, this, tmpVar, 0); + + //weapon doesn't save the prevent pick up flag when dropped, set the flag when needed (death in this case) + tmpVar.SetString(MAKE_STRING("OnDeath matt_weapon:AddOutput:spawnflags 2:0.00:1")); + AcceptInput("AddOutput", this, this, tmpVar, 0); #ifdef DEBUG extern bool g_bReceivedChainedActivate; g_bReceivedChainedActivate = false; #endif - pWeapon->Activate(); - Weapon_Equip( pWeapon ); } } diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index c1375eb7348..833153ad59a 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -3876,7 +3876,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea //----------------------------------------------------------------------------- bool CNPC_Combine::HasShotgun() { - if( GetActiveWeapon() && GetActiveWeapon()->m_iClassname == s_iszShotgunClassname ) + if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), s_iszShotgunClassname.ToCStr())) { return true; } diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 660cb218aec..c0653d59d3b 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -398,11 +398,11 @@ Disposition_t CNPC_PlayerCompanion::IRelationType( CBaseEntity *pTarget ) pTarget->IsNPC() && ((CAI_BaseNPC *)pTarget)->GetActiveWeapon() && #ifdef MAPBASE - (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname ) && - ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname ) ) ) ) + (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname.ToCStr() ) && + ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname.ToCStr() ) ) ) ) #else - ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) && - ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) ) ) + ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) && + ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) ) ) #endif { if ( (pTarget->GetAbsOrigin() - GetAbsOrigin()).LengthSqr() < Square( 25 * 12 ) ) @@ -2816,7 +2816,7 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) #ifdef MAPBASE if (EntIsClass(pWeapon, gm_iszShotgunClassname)) #else - if( pWeapon->ClassMatches( gm_iszShotgunClassname ) ) + f(pWeapon->ClassMatches(gm_iszShotgunClassname.ToCStr())) #endif { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index 13c4775a6f8..c80d0415a1a 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -74,7 +74,7 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) // Since classnames are pooled, the global string and the entity's classname should point to the same string in memory. // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. - return ent->m_iClassname == str2; + return ent->GetClassname() == str2.ToCStr(); } // ------------------------------------------------------------- diff --git a/sp/src/game/server/player_command.cpp b/sp/src/game/server/player_command.cpp index d5a6a1f08d8..7614babee02 100644 --- a/sp/src/game/server/player_command.cpp +++ b/sp/src/game/server/player_command.cpp @@ -393,7 +393,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper if ( weapon ) { VPROF( "player->SelectItem()" ); - player->SelectItem( weapon->GetName(), ucmd->weaponsubtype ); + player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype ); } } diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index bfba16e3e04..c69ff190bcd 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -38,6 +38,10 @@ #endif +#ifdef MAPBASE + #include "gamestringpool.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -321,7 +325,11 @@ void CBaseCombatWeapon::Precache( void ) else { // Couldn't read data file, remove myself - Warning( "Error reading weapon data file for: %s\n", GetWeaponScriptName() ); +#ifdef MAPBASE + Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", GetClassname(false), GetClassname()) +#else + Warning( "Error reading weapon data file for: %s\n", GetClassname() ); +#endif // Remove( ); //don't remove, this gets released soon! } } @@ -361,6 +369,19 @@ bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) { SetAmmoFromMapper(atof(szValue), true); } + if (FStrEq(szKeyName, "weaponscriptname")) + { + if (szValue[0] != '\0') //if not empty - use val, else get real classname + { + Q_strncpy(m_iszWeaponScriptName.GetForModify(), szValue, MAX_WEAPON_STRING); + } + else + { + Q_strncpy(m_iszWeaponScriptName.GetForModify(), GetClassname(false), MAX_WEAPON_STRING); + } + + return true; + } else if ( FStrEq(szKeyName, "spawnflags") ) { m_spawnflags = atoi(szValue); @@ -382,6 +403,31 @@ bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int i { return BaseClass::GetKeyValue(szKeyName, szValue, iMaxLen); } + +//----------------------------------------------------------------------------- +// Purpose: Returns weaponscriptname to make weapons of the same classname working properly with the rest of the code. +// Putting false will return the real classname, instead of weaponscriptname. +//----------------------------------------------------------------------------- +inline const char* CBaseCombatWeapon::GetClassname(bool bUseWeaponScriptName) +{ + if (bUseWeaponScriptName && Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) + { + if (Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) + { + return m_iszWeaponScriptName.Get(); + } + } + + return BaseClass::GetClassname(); +} + +//----------------------------------------------------------------------------- +// Purpose: This is used by FClassnameIs to compare classname strings, we replace it with script name. +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::ClassMatches(const char* pszClassOrWildcard) +{ + return Matcher_NamesMatch(pszClassOrWildcard, GetClassname()); +} #endif //----------------------------------------------------------------------------- @@ -1966,6 +2012,109 @@ void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata ) SetWeaponVisible( false ); } } + +ConVar sv_weapon_clips_reset_mode("sv_weapon_clips_reset_mode", "1", + FCVAR_REPLICATED | FCVAR_CHEAT, + "Sets the way to reset clips:\n0 - No reset at all.\n1 - Set max clip value.\n2 - Set max clip value if more then max clip.", + true, 0.0f, true, 2.0f +); + +ConVar sv_weapon_vm_anim_reset_mode("sv_weapon_vm_anim_reset_mode", "0", + FCVAR_REPLICATED | FCVAR_CHEAT, + "Animation to set after weapon script change, 0 to use idle animation, 1 to use deploy animation.", + true, 0, true, 1 +); + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: Input to change the weapon script name and re-Precache +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) +{ + const char* pszNewScript = inputdata.value.String(); + + //don't update if empty or already used this script + if (!pszNewScript || !pszNewScript[0] || FClassnameIs(this, pszNewScript)) + return; + + //don't update if other weapon that owns my owner uses the same script + if (GetOwner() && GetOwner()->Weapon_OwnsThisType(pszNewScript)) + return; + + //copy new data for networked and stored + Q_strncpy(m_iszWeaponScriptName.GetForModify(), pszNewScript, MAX_WEAPON_STRING); + + //finish reload before update + if (m_bInReload) + { + FinishReload(); + } + + m_iNeedsUpdate++; //trigger client update + + Precache(); //update with new script + + if (GetOwner() && GetOwner()->IsPlayer()) + { + //this updates wpn's vm at the same time as wpn's script, instead of waiting 2-6 seconds + if (GetOwner()->GetActiveWeapon() == this) + SetViewModel(); + + SetModel(GetViewModel()); //this fixes wrong sequence nums (DOESN'T AFFECT WORLD MODEL) + + //use deploy anim if we want + if (GetOwner()->GetActiveWeapon() == this) + { + if (sv_weapon_vm_anim_reset_mode.GetBool()) + { + Deploy(); + } + else + { + SendWeaponAnim(ACT_VM_IDLE); + } + } + } + + //if i have no owner - reset collsion model with bbox + check if my new wm has collision + //NOTE: no need if owned by NPC or plr as they update collision when drop weapons + if (!GetOwner()) + { + SetModel(GetWorldModel()); + VPhysicsDestroyObject(); + + if (!VPhysicsInitNormal(SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false)) + { + SetMoveType(MOVETYPE_NONE); + SetSolid(SOLID_BBOX); + AddSolidFlags(FSOLID_TRIGGER); + } + } + + //we don't want to reset clips at all, return + if (sv_weapon_clips_reset_mode.GetInt() == 0) + return; + + //we want to set max clipw vals + if (sv_weapon_clips_reset_mode.GetInt() == 1) + { + if (UsesClipsForAmmo1()) + m_iClip1 = GetMaxClip1(); + + if (UsesClipsForAmmo2()) + m_iClip2 = GetMaxClip2(); + } + + //we want to set max clip only if this weapon has more ammo in clips than max + if (sv_weapon_clips_reset_mode.GetInt() == 2) + { + if (UsesClipsForAmmo1() && m_iClip1 > GetMaxClip1()) + m_iClip1 = GetMaxClip1(); + + if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) + m_iClip2 = GetMaxClip2(); + } +} #endif //----------------------------------------------------------------------------- @@ -3243,6 +3392,11 @@ BEGIN_DATADESC( CBaseCombatWeapon ) DEFINE_FIELD( m_bAltFireHudHintDisplayed, FIELD_BOOLEAN ), DEFINE_FIELD( m_flHudHintPollTime, FIELD_TIME ), DEFINE_FIELD( m_flHudHintMinDisplayTime, FIELD_TIME ), + +#ifdef MAPBASE + DEFINE_AUTO_ARRAY( m_iszWeaponScriptName, FIELD_CHARACTER ), + DEFINE_FIELD( m_iNeedsUpdate, FIELD_INTEGER ), +#endif // Just to quiet classcheck.. this field exists only on the client // DEFINE_FIELD( m_iOldState, FIELD_INTEGER ), @@ -3270,6 +3424,7 @@ BEGIN_DATADESC( CBaseCombatWeapon ) DEFINE_INPUTFUNC( FIELD_VOID, "BreakConstraint", InputBreakConstraint ), DEFINE_INPUTFUNC( FIELD_VOID, "ForcePrimaryFire", InputForcePrimaryFire ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceSecondaryFire", InputForceSecondaryFire ), + DEFINE_INPUTFUNC( FIELD_STRING, "ChangeScript", InputChangeScript ), #endif // Outputs @@ -3406,6 +3561,10 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) SendPropInt( SENDINFO( m_nViewModelIndex ), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bFlipViewModel ) ), + +#ifdef MAPBASE + SendPropInt( SENDINFO( m_iNeedsUpdate ) ), +#endif #if defined( TF_DLL ) SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), @@ -3420,6 +3579,10 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) RecvPropInt( RECVINFO( m_nViewModelIndex ) ), RecvPropBool( RECVINFO( m_bFlipViewModel ) ), + +#ifdef MAPBASE + RecvPropInt( RECVINFO(m_iNeedsUpdate) ), +#endif #endif END_NETWORK_TABLE() @@ -3430,14 +3593,14 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) SendPropDataTable("LocalActiveWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponData), SendProxy_SendActiveLocalWeaponDataTable ), SendPropModelIndex( SENDINFO(m_iViewModelIndex) ), SendPropModelIndex( SENDINFO(m_iWorldModelIndex) ), -#ifdef MAPBASE - SendPropModelIndex( SENDINFO(m_iDroppedModelIndex) ), -#endif SendPropInt( SENDINFO(m_iState ), 8, SPROP_UNSIGNED ), SendPropEHandle( SENDINFO(m_hOwner) ), - + #ifdef MAPBASE + SendPropModelIndex( SENDINFO(m_iDroppedModelIndex) ), + SendPropString( SENDINFO(m_iszWeaponScriptName) ), SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_iNeedsUpdate), 0, SPROP_UNSIGNED ), #endif #else @@ -3445,14 +3608,14 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) RecvPropDataTable("LocalActiveWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponData)), RecvPropInt( RECVINFO(m_iViewModelIndex)), RecvPropInt( RECVINFO(m_iWorldModelIndex)), -#ifdef MAPBASE - RecvPropInt( RECVINFO(m_iDroppedModelIndex) ), -#endif RecvPropInt( RECVINFO(m_iState )), RecvPropEHandle( RECVINFO(m_hOwner ) ), - + #ifdef MAPBASE + RecvPropInt( RECVINFO(m_iDroppedModelIndex) ), RecvPropInt( RECVINFO( m_spawnflags ) ), + RecvPropInt( RECVINFO(m_iNeedsUpdate) ), + RecvPropString( RECVINFO(m_iszWeaponScriptName) ), #endif #endif diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index d7fd5c47d4e..514fc2eeff7 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -217,6 +217,8 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM void SetAmmoFromMapper( float flAmmo, bool bSecondary = false ); virtual bool KeyValue( const char *szKeyName, const char *szValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + inline const char* GetClassname( bool bUseWeaponScriptName = true ); + virtual bool ClassMatches( const char* pszClassOrWildcard ); #endif void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ); @@ -569,6 +571,7 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM void InputForceFire( inputdata_t &inputdata, bool bSecondary = false ); void InputForcePrimaryFire( inputdata_t &inputdata ); void InputForceSecondaryFire( inputdata_t &inputdata ); + void InputChangeScript(inputdata_t& inputdata); // Input to change the weapon script name and re-Precache #endif void InputHideWeapon( inputdata_t &inputdata ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); @@ -734,6 +737,14 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM float m_fFireDuration; // The amount of time that the weapon has sustained firing int m_iSubType; +#ifdef MAPBASE +#ifdef CLIENT_DLL + int m_iOldNeedsUpdate = 0; //client's variable to compare with networked and decide if update is needed +#endif + CNetworkString( m_iszWeaponScriptName, MAX_WEAPON_STRING ); //networked weapon script name + CNetworkVar(int, m_iNeedsUpdate); //mark for client in case if weapon script update is wanted +#endif + float m_flUnlockTime; EHANDLE m_hLocker; // Who locked this weapon. diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 0d74d95b2c3..1fed922c72f 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -105,6 +105,9 @@ class FileWeaponInfo_t // SHARED char szClassName[MAX_WEAPON_STRING]; char szPrintName[MAX_WEAPON_STRING]; // Name for showing in HUD, etc. +#ifdef MAPBASE + char szWeaponScriptName[MAX_WEAPON_STRING]; +#endif char szViewModel[MAX_WEAPON_STRING]; // View model of this weapon char szWorldModel[MAX_WEAPON_STRING]; // Model of this weapon seen carried by the player From 5a43eefcba56cca91d25850352e0fc2ce9114b65 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:03:58 +0200 Subject: [PATCH 02/29] fixes for prev commit --- sp/src/game/server/hl2/npc_citizen17.cpp | 8 +++ .../game/shared/basecombatweapon_shared.cpp | 53 ++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index ad225385918..f86a28e8d92 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -924,6 +924,7 @@ void CNPC_Citizen::FixupMattWeapon() if ( pWeapon && pWeapon->ClassMatches( "weapon_crowbar" ) && NameMatches( "matt" ) ) #endif { +#ifdef MAPBASE variant_t tmpVar; tmpVar.SetString(MAKE_STRING("weapon_mattpipe")); @@ -932,6 +933,13 @@ void CNPC_Citizen::FixupMattWeapon() //weapon doesn't save the prevent pick up flag when dropped, set the flag when needed (death in this case) tmpVar.SetString(MAKE_STRING("OnDeath matt_weapon:AddOutput:spawnflags 2:0.00:1")); AcceptInput("AddOutput", this, this, tmpVar, 0); +#else + Weapon_Drop( pWeapon ); + UTIL_Remove( pWeapon ); + pWeapon = (CBaseCombatWeapon *)CREATE_UNSAVED_ENTITY( CMattsPipe, "weapon_crowbar" ); + pWeapon->SetName( AllocPooledString( "matt_weapon" ) ); + DispatchSpawn( pWeapon ); +#endif #ifdef DEBUG extern bool g_bReceivedChainedActivate; diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index c69ff190bcd..550ba12f28e 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -64,6 +64,20 @@ ConVar tf_weapon_criticals_bucket_bottom( "tf_weapon_criticals_bucket_bottom", " ConVar tf_weapon_criticals_bucket_default( "tf_weapon_criticals_bucket_default", "300.0", FCVAR_REPLICATED | FCVAR_CHEAT ); #endif // TF +#ifdef MAPBASE +ConVar sv_weapon_clips_reset_mode("sv_weapon_clips_reset_mode", "1", + FCVAR_REPLICATED | FCVAR_CHEAT, + "Sets the way to reset clips:\n0 - No reset at all.\n1 - Set max clip value.\n2 - Set max clip value if more then max clip.", + true, 0.0f, true, 2.0f +); + +ConVar sv_weapon_vm_anim_reset_mode("sv_weapon_vm_anim_reset_mode", "0", + FCVAR_REPLICATED | FCVAR_CHEAT, + "Animation to set after weapon script change, 0 to use idle animation, 1 to use deploy animation.", + true, 0, true, 1 +); +#endif + CBaseCombatWeapon::CBaseCombatWeapon() { // Constructor must call this @@ -1999,33 +2013,7 @@ void CBaseCombatWeapon::InputForceSecondaryFire( inputdata_t &inputdata ) { InputForceFire(inputdata, true); } -#endif -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata ) -{ - // Only hide if we're still the active weapon. If we're not the active weapon - if ( GetOwner() && GetOwner()->GetActiveWeapon() == this ) - { - SetWeaponVisible( false ); - } -} - -ConVar sv_weapon_clips_reset_mode("sv_weapon_clips_reset_mode", "1", - FCVAR_REPLICATED | FCVAR_CHEAT, - "Sets the way to reset clips:\n0 - No reset at all.\n1 - Set max clip value.\n2 - Set max clip value if more then max clip.", - true, 0.0f, true, 2.0f -); - -ConVar sv_weapon_vm_anim_reset_mode("sv_weapon_vm_anim_reset_mode", "0", - FCVAR_REPLICATED | FCVAR_CHEAT, - "Animation to set after weapon script change, 0 to use idle animation, 1 to use deploy animation.", - true, 0, true, 1 -); - -#ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: Input to change the weapon script name and re-Precache //----------------------------------------------------------------------------- @@ -2117,6 +2105,19 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) } #endif +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata ) +{ + // Only hide if we're still the active weapon. If we're not the active weapon + if ( GetOwner() && GetOwner()->GetActiveWeapon() == this ) + { + SetWeaponVisible( false ); + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- From fdc902fbee40ed0a93f3c2fddbcd55ae5fcec41a Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 03:18:57 +0200 Subject: [PATCH 03/29] added check if we have a valid script file for the input --- sp/src/game/shared/basecombatweapon_shared.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 550ba12f28e..81a22e2251b 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -2028,6 +2028,24 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) //don't update if other weapon that owns my owner uses the same script if (GetOwner() && GetOwner()->Weapon_OwnsThisType(pszNewScript)) return; + + char sz[128]; + Q_snprintf(sz, sizeof(sz), "scripts/%s", pszNewScript); + + KeyValues* pKV = ReadEncryptedKVFile(filesystem, sz, GetEncryptionKey(), +#if defined( DOD_DLL ) + true // Only read .ctx files! +#else + false +#endif + ); + + //don't if file doesn't exists + if (!pKV) + { + Warning("Error reading weapon data file \"%s\".\n", pszNewScript); + return; + } //copy new data for networked and stored Q_strncpy(m_iszWeaponScriptName.GetForModify(), pszNewScript, MAX_WEAPON_STRING); From 6da6d8a7efabb05806833bea54f66cf45c3d8ddd Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:54:31 +0200 Subject: [PATCH 04/29] Update basecombatweapon_shared.cpp --- sp/src/game/shared/basecombatweapon_shared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 81a22e2251b..3c041f95ba0 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -340,7 +340,7 @@ void CBaseCombatWeapon::Precache( void ) { // Couldn't read data file, remove myself #ifdef MAPBASE - Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", GetClassname(false), GetClassname()) + Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", GetClassname(false), GetClassname()); #else Warning( "Error reading weapon data file for: %s\n", GetClassname() ); #endif From 07f82386ebfa8268db9f21dc18ef35fc65a53aa0 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:05:28 +0200 Subject: [PATCH 05/29] Update npc_combine.cpp --- sp/src/game/server/hl2/npc_combine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index 833153ad59a..d5781cf4adb 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -3824,7 +3824,7 @@ void CNPC_Combine::OnEndMoveAndShoot() WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { #ifdef MAPBASE - if( pWeapon->ClassMatches( gm_isz_class_AR2 ) ) + if( pWeapon->ClassMatches( gm_isz_class_AR2.ToCStr() ) ) #else if( FClassnameIs( pWeapon, "weapon_ar2" ) ) #endif @@ -3839,7 +3839,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea } } #ifdef MAPBASE - else if( pWeapon->ClassMatches( gm_isz_class_Shotgun ) ) + else if( pWeapon->ClassMatches( gm_isz_class_Shotgun.ToCStr() ) ) #else else if( FClassnameIs( pWeapon, "weapon_shotgun" ) ) #endif @@ -3854,7 +3854,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea return WEAPON_PROFICIENCY_PERFECT; } #ifdef MAPBASE - else if( pWeapon->ClassMatches( gm_isz_class_SMG1 ) ) + else if( pWeapon->ClassMatches( gm_isz_class_SMG1.ToCStr() ) ) #else else if( FClassnameIs( pWeapon, "weapon_smg1" ) ) #endif @@ -3862,7 +3862,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea return WEAPON_PROFICIENCY_GOOD; } #ifdef MAPBASE - else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) ) + else if ( pWeapon->ClassMatches( gm_isz_class_Pistol.ToCStr() ) ) { // Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer. return WEAPON_PROFICIENCY_VERY_GOOD; From 4387dff61619e2eaa10604c840eb9e5aa6b66c1a Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:14:28 +0200 Subject: [PATCH 06/29] included mapbase_matchers_base.h + npc_playercompanion.cpp fix --- sp/src/game/server/hl2/npc_playercompanion.cpp | 4 ++-- sp/src/game/shared/basecombatweapon_shared.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index c0653d59d3b..53dc8126fd6 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -398,8 +398,8 @@ Disposition_t CNPC_PlayerCompanion::IRelationType( CBaseEntity *pTarget ) pTarget->IsNPC() && ((CAI_BaseNPC *)pTarget)->GetActiveWeapon() && #ifdef MAPBASE - (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname.ToCStr() ) && - ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname.ToCStr() ) ) ) ) + (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname ) && + ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname ) ) ) ) #else ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) && ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) ) ) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 3c041f95ba0..40bb48a8a0c 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -40,6 +40,7 @@ #ifdef MAPBASE #include "gamestringpool.h" + #include "mapbase_matchers_base.h" #endif // memdbgon must be the last include file in a .cpp file!!! From 2a1df665582ae76b2870f4212b4d87575ee071ca Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:36:37 +0200 Subject: [PATCH 07/29] now this should compile on both windows and linux --- sp/src/game/shared/basecombatweapon_shared.cpp | 7 ++----- sp/src/game/shared/basecombatweapon_shared.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 40bb48a8a0c..89366a02709 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -423,14 +423,11 @@ bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int i // Purpose: Returns weaponscriptname to make weapons of the same classname working properly with the rest of the code. // Putting false will return the real classname, instead of weaponscriptname. //----------------------------------------------------------------------------- -inline const char* CBaseCombatWeapon::GetClassname(bool bUseWeaponScriptName) +const char* CBaseCombatWeapon::GetClassname(bool bUseWeaponScriptName) { if (bUseWeaponScriptName && Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) { - if (Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) - { - return m_iszWeaponScriptName.Get(); - } + return m_iszWeaponScriptName.Get(); } return BaseClass::GetClassname(); diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index 514fc2eeff7..2caabde4746 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -217,7 +217,7 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM void SetAmmoFromMapper( float flAmmo, bool bSecondary = false ); virtual bool KeyValue( const char *szKeyName, const char *szValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); - inline const char* GetClassname( bool bUseWeaponScriptName = true ); + const char* GetClassname( bool bUseWeaponScriptName = true ); virtual bool ClassMatches( const char* pszClassOrWildcard ); #endif From ccada7b07768e7b69364e152cac4cf146fa94f2b Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:17:23 +0200 Subject: [PATCH 08/29] fixes after changes for linux compile The inline was important for client, so we now override CBaseEntity's GetClassname() method, to get real classname for a weapon - use m_iClassname directly. --- sp/src/game/server/baseentity.h | 4 ++-- sp/src/game/shared/basecombatweapon_shared.cpp | 10 +++------- sp/src/game/shared/basecombatweapon_shared.h | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index ddeaf8fdf4c..5e41f871857 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -821,8 +821,8 @@ class CBaseEntity : public IServerEntity bool ReadKeyField( const char *varName, variant_t *var ); // classname access - void SetClassname( const char *className ); - const char* GetClassname(); + void SetClassname( const char *className ); + virtual const char* GetClassname(); // Debug Overlays void EntityText( int text_offset, const char *text, float flDuration, int r = 255, int g = 255, int b = 255, int a = 255 ); diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 89366a02709..e2fec69190c 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -341,7 +341,7 @@ void CBaseCombatWeapon::Precache( void ) { // Couldn't read data file, remove myself #ifdef MAPBASE - Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", GetClassname(false), GetClassname()); + Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", m_iClassname, GetClassname()); #else Warning( "Error reading weapon data file for: %s\n", GetClassname() ); #endif @@ -390,10 +390,6 @@ bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) { Q_strncpy(m_iszWeaponScriptName.GetForModify(), szValue, MAX_WEAPON_STRING); } - else - { - Q_strncpy(m_iszWeaponScriptName.GetForModify(), GetClassname(false), MAX_WEAPON_STRING); - } return true; } @@ -423,9 +419,9 @@ bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int i // Purpose: Returns weaponscriptname to make weapons of the same classname working properly with the rest of the code. // Putting false will return the real classname, instead of weaponscriptname. //----------------------------------------------------------------------------- -const char* CBaseCombatWeapon::GetClassname(bool bUseWeaponScriptName) +const char* CBaseCombatWeapon::GetClassname() { - if (bUseWeaponScriptName && Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) + if (Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) { return m_iszWeaponScriptName.Get(); } diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index 2caabde4746..d3962d73783 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -217,7 +217,7 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM void SetAmmoFromMapper( float flAmmo, bool bSecondary = false ); virtual bool KeyValue( const char *szKeyName, const char *szValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); - const char* GetClassname( bool bUseWeaponScriptName = true ); + virtual const char* GetClassname(); virtual bool ClassMatches( const char* pszClassOrWildcard ); #endif From 14e9c1bdab3f726ed8b5da21289e18cfcb183f21 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:12:20 +0200 Subject: [PATCH 09/29] major fixes for custom weapon scripts Probably will add one more commit. Changes: - Added ClassMatches( string_t nameStr ) override. - Fixed EntIsClass, now it will compare strings, not pointers (so it can work for weapons). - Added missing Active() and Weapon_Equip() for not Mapbase Matt pipe. - Fixed memory leak in ChangeScript input caused by not removing the path pointer. - Removed duplicate of m_iNeedsUpdate for DT_LocalWeaponData (turns out it's not needed here). --- sp/src/game/server/hl2/npc_citizen17.cpp | 5 +++++ sp/src/game/server/mapbase/GlobalStrings.h | 2 +- sp/src/game/shared/basecombatweapon_shared.cpp | 18 ++++++++++-------- sp/src/game/shared/basecombatweapon_shared.h | 1 + 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index f86a28e8d92..869eb9884b1 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -945,6 +945,11 @@ void CNPC_Citizen::FixupMattWeapon() extern bool g_bReceivedChainedActivate; g_bReceivedChainedActivate = false; #endif + +#ifndef MAPBASE + pWeapon->Activate(); + Weapon_Equip( pWeapon ); +#endif } } diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index c80d0415a1a..13721e56866 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -74,7 +74,7 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) // Since classnames are pooled, the global string and the entity's classname should point to the same string in memory. // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. - return ent->GetClassname() == str2.ToCStr(); + return FStrEq(ent->GetClassname(), str2.ToCStr()); } // ------------------------------------------------------------- diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index e2fec69190c..064b65ddc39 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -436,6 +436,14 @@ bool CBaseCombatWeapon::ClassMatches(const char* pszClassOrWildcard) { return Matcher_NamesMatch(pszClassOrWildcard, GetClassname()); } + +//----------------------------------------------------------------------------- +// Purpose: This is used by FClassnameIs to compare classname strings, we replace it with script name. +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::ClassMatches(string_t nameStr) +{ + return Matcher_NamesMatch(nameStr.ToCStr(), GetClassname()); +} #endif //----------------------------------------------------------------------------- @@ -2040,6 +2048,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) Warning("Error reading weapon data file \"%s\".\n", pszNewScript); return; } + + pKV->deleteThis(); //free up memory //copy new data for networked and stored Q_strncpy(m_iszWeaponScriptName.GetForModify(), pszNewScript, MAX_WEAPON_STRING); @@ -3574,10 +3584,6 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) SendPropInt( SENDINFO( m_nViewModelIndex ), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bFlipViewModel ) ), - -#ifdef MAPBASE - SendPropInt( SENDINFO( m_iNeedsUpdate ) ), -#endif #if defined( TF_DLL ) SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), @@ -3592,10 +3598,6 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) RecvPropInt( RECVINFO( m_nViewModelIndex ) ), RecvPropBool( RECVINFO( m_bFlipViewModel ) ), - -#ifdef MAPBASE - RecvPropInt( RECVINFO(m_iNeedsUpdate) ), -#endif #endif END_NETWORK_TABLE() diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index d3962d73783..e83faa2fdf8 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -219,6 +219,7 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); virtual const char* GetClassname(); virtual bool ClassMatches( const char* pszClassOrWildcard ); + virtual bool ClassMatches( string_t nameStr ); #endif void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ); From b856318951a7166c9302e65a290b81053949411c Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:38:45 +0200 Subject: [PATCH 10/29] prev commit fixes - Added client compile checks for ClassMatches(string_t nameStr) to make the solution compile. - Revrerted back m_iNeedsUpdate for DT_LocalWeaponData (because client HUD doesn't updates without it somehow). --- sp/src/game/shared/basecombatweapon_shared.cpp | 18 ++++++++++++++---- sp/src/game/shared/basecombatweapon_shared.h | 4 +++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 064b65ddc39..e6587a4fe9e 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -429,20 +429,22 @@ const char* CBaseCombatWeapon::GetClassname() return BaseClass::GetClassname(); } +#if !defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Purpose: This is used by FClassnameIs to compare classname strings, we replace it with script name. //----------------------------------------------------------------------------- -bool CBaseCombatWeapon::ClassMatches(const char* pszClassOrWildcard) +bool CBaseCombatWeapon::ClassMatches(string_t nameStr) { - return Matcher_NamesMatch(pszClassOrWildcard, GetClassname()); + return Matcher_NamesMatch(nameStr.ToCStr(), GetClassname()); } +#endif //----------------------------------------------------------------------------- // Purpose: This is used by FClassnameIs to compare classname strings, we replace it with script name. //----------------------------------------------------------------------------- -bool CBaseCombatWeapon::ClassMatches(string_t nameStr) +bool CBaseCombatWeapon::ClassMatches(const char* pszClassOrWildcard) { - return Matcher_NamesMatch(nameStr.ToCStr(), GetClassname()); + return Matcher_NamesMatch(pszClassOrWildcard, GetClassname()); } #endif @@ -3594,8 +3596,16 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip2 )), RecvPropInt( RECVINFO(m_iPrimaryAmmoType )), RecvPropInt( RECVINFO(m_iSecondaryAmmoType )), + +#ifdef MAPBASE + SendPropInt(SENDINFO(m_iNeedsUpdate)), //needed for certain local weapon data (such as icons, weapon bucket pos, etc) +#endif RecvPropInt( RECVINFO( m_nViewModelIndex ) ), + +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_iNeedsUpdate)), +#endif RecvPropBool( RECVINFO( m_bFlipViewModel ) ), diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index e83faa2fdf8..2347dfad9b8 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -218,9 +218,11 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual bool KeyValue( const char *szKeyName, const char *szValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); virtual const char* GetClassname(); - virtual bool ClassMatches( const char* pszClassOrWildcard ); +#if !defined( CLIENT_DLL ) virtual bool ClassMatches( string_t nameStr ); #endif + virtual bool ClassMatches( const char* pszClassOrWildcard ); +#endif void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ); From fb971e4be1b9a057194ffc3e454abf14651b5fac Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 20:20:52 +0200 Subject: [PATCH 11/29] compile fix --- sp/src/game/server/hl2/npc_playercompanion.cpp | 2 +- sp/src/game/shared/basecombatweapon_shared.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 53dc8126fd6..1c0b37d475a 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -2816,7 +2816,7 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) #ifdef MAPBASE if (EntIsClass(pWeapon, gm_iszShotgunClassname)) #else - f(pWeapon->ClassMatches(gm_iszShotgunClassname.ToCStr())) + if (pWeapon->ClassMatches(gm_iszShotgunClassname.ToCStr())) #endif { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index e6587a4fe9e..62f993076f7 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -3591,24 +3591,22 @@ BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), #endif +#ifdef MAPBASE + SendPropInt(SENDINFO(m_iNeedsUpdate)), //needed for certain local weapon data (such as icons, weapon bucket pos, etc) +#endif + #else RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip1 )), RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip2 )), RecvPropInt( RECVINFO(m_iPrimaryAmmoType )), RecvPropInt( RECVINFO(m_iSecondaryAmmoType )), - -#ifdef MAPBASE - SendPropInt(SENDINFO(m_iNeedsUpdate)), //needed for certain local weapon data (such as icons, weapon bucket pos, etc) -#endif - RecvPropInt( RECVINFO( m_nViewModelIndex ) ), + RecvPropBool( RECVINFO( m_bFlipViewModel ) ), #ifdef MAPBASE RecvPropInt(RECVINFO(m_iNeedsUpdate)), #endif - RecvPropBool( RECVINFO( m_bFlipViewModel ) ), - #endif END_NETWORK_TABLE() From a83a1fa956e687e874f211fbe30a3dc3c0deae08 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 23:10:38 +0200 Subject: [PATCH 12/29] more fixes - Added a better way to find out if weapon script string is empty in GetClassname(). - Used a more safe way to get and print classname in the warning msg (alth not sure if we need it here). - Used m_iClassname directly for weapon respawn. - Reworked EntIsClass( CBaseEntity *ent, string_t str2 ) to check first if the entity isn't a combat weapon, else it will use ClassMatches from CBaseCombatWeapon. - GetName now will return script name if is not empty. - Reverted few changes. --- sp/src/game/client/weapon_selection.cpp | 2 +- sp/src/game/server/basecombatweapon.cpp | 2 +- sp/src/game/server/mapbase/GlobalStrings.h | 9 +++++++-- sp/src/game/server/player_command.cpp | 2 +- sp/src/game/shared/basecombatweapon_shared.cpp | 9 ++++++--- sp/src/game/shared/weapon_parse.h | 3 --- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sp/src/game/client/weapon_selection.cpp b/sp/src/game/client/weapon_selection.cpp index ad0d0c37afd..c7520b0bdd6 100644 --- a/sp/src/game/client/weapon_selection.cpp +++ b/sp/src/game/client/weapon_selection.cpp @@ -507,7 +507,7 @@ void CBaseHudWeaponSelection::SetWeaponSelected( void ) Assert( GetSelectedWeapon() ); //Mark selection so that it's placed into next CUserCmd created if isn't active (or we'll get prediction glitch with custom scripts) - if(GetSelectedWeapon() != GetActiveWeapon()) + if (GetSelectedWeapon() != GetActiveWeapon()) input->MakeWeaponSelection( GetSelectedWeapon() ); } diff --git a/sp/src/game/server/basecombatweapon.cpp b/sp/src/game/server/basecombatweapon.cpp index db6666018af..8b460cb9e10 100644 --- a/sp/src/game/server/basecombatweapon.cpp +++ b/sp/src/game/server/basecombatweapon.cpp @@ -199,7 +199,7 @@ CBaseEntity* CBaseCombatWeapon::Respawn( void ) { // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code // will decide when to make the weapon visible and touchable. - CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() ); + CBaseEntity *pNewWeapon = CBaseEntity::Create( STRING(m_iClassname), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() ); if (pNewWeapon) { diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index 13721e56866..e7b35340221 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -70,11 +70,16 @@ extern string_t gm_isz_name_activator; // This function is for comparing global strings and allows us to change how we compare them quickly. inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) { - //return ent->ClassMatches(str2); + if (!ent) + return false; // Since classnames are pooled, the global string and the entity's classname should point to the same string in memory. // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. - return FStrEq(ent->GetClassname(), str2.ToCStr()); + // The the exception is weapons, because weapons replace classname with script name, check if is weapon before comparing by pointer. + if (!ent->IsBaseCombatWeapon()) + return ent->m_iClassname == str2; + + return static_cast(ent)->ClassMatches(str2); } // ------------------------------------------------------------- diff --git a/sp/src/game/server/player_command.cpp b/sp/src/game/server/player_command.cpp index 7614babee02..d5a6a1f08d8 100644 --- a/sp/src/game/server/player_command.cpp +++ b/sp/src/game/server/player_command.cpp @@ -393,7 +393,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper if ( weapon ) { VPROF( "player->SelectItem()" ); - player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype ); + player->SelectItem( weapon->GetName(), ucmd->weaponsubtype ); } } diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 62f993076f7..0e545c7ba4c 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -341,7 +341,7 @@ void CBaseCombatWeapon::Precache( void ) { // Couldn't read data file, remove myself #ifdef MAPBASE - Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", m_iClassname, GetClassname()); + Warning( "Error reading weapon data file for classname \"%s\" with script \"%s\".\n", STRING(m_iClassname), GetClassname()); #else Warning( "Error reading weapon data file for: %s\n", GetClassname() ); #endif @@ -421,7 +421,7 @@ bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int i //----------------------------------------------------------------------------- const char* CBaseCombatWeapon::GetClassname() { - if (Q_strcmp(m_iszWeaponScriptName.Get(), "") > 0) + if (m_iszWeaponScriptName.Get()[0] != '\0') { return m_iszWeaponScriptName.Get(); } @@ -637,10 +637,13 @@ int CBaseCombatWeapon::GetPosition( void ) const } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: Get classname from wpn data or use script name directly if not empty. //----------------------------------------------------------------------------- const char *CBaseCombatWeapon::GetName( void ) const { + if (m_iszWeaponScriptName.Get()[0] != '\0') + return m_iszWeaponScriptName.Get(); + return GetWpnData().szClassName; } diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 1fed922c72f..0d74d95b2c3 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -105,9 +105,6 @@ class FileWeaponInfo_t // SHARED char szClassName[MAX_WEAPON_STRING]; char szPrintName[MAX_WEAPON_STRING]; // Name for showing in HUD, etc. -#ifdef MAPBASE - char szWeaponScriptName[MAX_WEAPON_STRING]; -#endif char szViewModel[MAX_WEAPON_STRING]; // View model of this weapon char szWorldModel[MAX_WEAPON_STRING]; // Model of this weapon seen carried by the player From e80cd4bacfef251a83cd843949aa738a3fdc6877 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 23:39:50 +0200 Subject: [PATCH 13/29] few more fixes - Var m_iszWeaponScriptName renamed to m_iszWeaponScript to avoid issues with m_iszWeaponScriptName from weapon_custom_scripted1. - sv_weapon_clips_reset_mode and sv_weapon_vm_anim_reset_mode aren't cheats anymore, so you can change both in skill.cfg or in maps. - Replaced ClassMatches with FStrEq in EntIsClass for better speed. --- sp/src/game/server/mapbase/GlobalStrings.h | 2 +- .../game/shared/basecombatweapon_shared.cpp | 22 +++++++++---------- sp/src/game/shared/basecombatweapon_shared.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index e7b35340221..15cca372458 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -79,7 +79,7 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) if (!ent->IsBaseCombatWeapon()) return ent->m_iClassname == str2; - return static_cast(ent)->ClassMatches(str2); + return FStrEq(ent->GetClassname(), str2.ToCStr()); } // ------------------------------------------------------------- diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 0e545c7ba4c..525c2080a78 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -67,13 +67,13 @@ ConVar tf_weapon_criticals_bucket_default( "tf_weapon_criticals_bucket_default", #ifdef MAPBASE ConVar sv_weapon_clips_reset_mode("sv_weapon_clips_reset_mode", "1", - FCVAR_REPLICATED | FCVAR_CHEAT, + FCVAR_REPLICATED, "Sets the way to reset clips:\n0 - No reset at all.\n1 - Set max clip value.\n2 - Set max clip value if more then max clip.", true, 0.0f, true, 2.0f ); ConVar sv_weapon_vm_anim_reset_mode("sv_weapon_vm_anim_reset_mode", "0", - FCVAR_REPLICATED | FCVAR_CHEAT, + FCVAR_REPLICATED, "Animation to set after weapon script change, 0 to use idle animation, 1 to use deploy animation.", true, 0, true, 1 ); @@ -388,7 +388,7 @@ bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) { if (szValue[0] != '\0') //if not empty - use val, else get real classname { - Q_strncpy(m_iszWeaponScriptName.GetForModify(), szValue, MAX_WEAPON_STRING); + Q_strncpy(m_iszWeaponScript.GetForModify(), szValue, MAX_WEAPON_STRING); } return true; @@ -421,9 +421,9 @@ bool CBaseCombatWeapon::GetKeyValue( const char *szKeyName, char *szValue, int i //----------------------------------------------------------------------------- const char* CBaseCombatWeapon::GetClassname() { - if (m_iszWeaponScriptName.Get()[0] != '\0') + if (m_iszWeaponScript.Get()[0] != '\0') { - return m_iszWeaponScriptName.Get(); + return m_iszWeaponScript.Get(); } return BaseClass::GetClassname(); @@ -641,8 +641,8 @@ int CBaseCombatWeapon::GetPosition( void ) const //----------------------------------------------------------------------------- const char *CBaseCombatWeapon::GetName( void ) const { - if (m_iszWeaponScriptName.Get()[0] != '\0') - return m_iszWeaponScriptName.Get(); + if (m_iszWeaponScript.Get()[0] != '\0') + return m_iszWeaponScript.Get(); return GetWpnData().szClassName; } @@ -2057,7 +2057,7 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) pKV->deleteThis(); //free up memory //copy new data for networked and stored - Q_strncpy(m_iszWeaponScriptName.GetForModify(), pszNewScript, MAX_WEAPON_STRING); + Q_strncpy(m_iszWeaponScript.GetForModify(), pszNewScript, MAX_WEAPON_STRING); //finish reload before update if (m_bInReload) @@ -3422,7 +3422,7 @@ BEGIN_DATADESC( CBaseCombatWeapon ) DEFINE_FIELD( m_flHudHintMinDisplayTime, FIELD_TIME ), #ifdef MAPBASE - DEFINE_AUTO_ARRAY( m_iszWeaponScriptName, FIELD_CHARACTER ), + DEFINE_AUTO_ARRAY( m_iszWeaponScript, FIELD_CHARACTER ), DEFINE_FIELD( m_iNeedsUpdate, FIELD_INTEGER ), #endif @@ -3624,7 +3624,7 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) #ifdef MAPBASE SendPropModelIndex( SENDINFO(m_iDroppedModelIndex) ), - SendPropString( SENDINFO(m_iszWeaponScriptName) ), + SendPropString( SENDINFO(m_iszWeaponScript) ), SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ), SendPropInt( SENDINFO(m_iNeedsUpdate), 0, SPROP_UNSIGNED ), #endif @@ -3641,7 +3641,7 @@ BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) RecvPropInt( RECVINFO(m_iDroppedModelIndex) ), RecvPropInt( RECVINFO( m_spawnflags ) ), RecvPropInt( RECVINFO(m_iNeedsUpdate) ), - RecvPropString( RECVINFO(m_iszWeaponScriptName) ), + RecvPropString( RECVINFO(m_iszWeaponScript) ), #endif #endif diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index 2347dfad9b8..0b99806b367 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -744,7 +744,7 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM #ifdef CLIENT_DLL int m_iOldNeedsUpdate = 0; //client's variable to compare with networked and decide if update is needed #endif - CNetworkString( m_iszWeaponScriptName, MAX_WEAPON_STRING ); //networked weapon script name + CNetworkString( m_iszWeaponScript, MAX_WEAPON_STRING ); //networked weapon script name CNetworkVar(int, m_iNeedsUpdate); //mark for client in case if weapon script update is wanted #endif From 3d3a0461c8855a7207cf91d2d0c66f94c7b12029 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sat, 7 Mar 2026 23:53:56 +0200 Subject: [PATCH 14/29] reverts for ToCStr() + better wrn msg for weapon respawn --- sp/src/game/server/basecombatweapon.cpp | 2 +- sp/src/game/server/hl2/npc_alyx_episodic.cpp | 6 +++--- sp/src/game/server/hl2/npc_combine.cpp | 10 +++++----- sp/src/game/server/hl2/npc_playercompanion.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sp/src/game/server/basecombatweapon.cpp b/sp/src/game/server/basecombatweapon.cpp index 8b460cb9e10..4756ec4d4ad 100644 --- a/sp/src/game/server/basecombatweapon.cpp +++ b/sp/src/game/server/basecombatweapon.cpp @@ -220,7 +220,7 @@ CBaseEntity* CBaseCombatWeapon::Respawn( void ) } else { - Warning("Respawn failed to create %s!\n", GetClassname()); + Warning("Respawn failed to create weapon \"%s\" with script \"%s\"!\n", STRING(m_iClassname), GetClassname()); } return pNewWeapon; diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.cpp b/sp/src/game/server/hl2/npc_alyx_episodic.cpp index 609bb67ed4b..dd5a35b0dda 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/sp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -2585,7 +2585,7 @@ void CNPC_Alyx::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarge { BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); - if(pWeapon && pWeapon->ClassMatches(CLASSNAME_ALYXGUN.ToCStr())) + if( pWeapon && pWeapon->ClassMatches( CLASSNAME_ALYXGUN ) ) { pWeapon->SUB_Remove(); } @@ -3256,7 +3256,7 @@ void CNPC_Alyx::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombat void CNPC_Alyx::OnGivenWeapon( CBaseCombatWeapon *pNewWeapon ) { // HACK: This causes Alyx to pull her gun from a holstered position - if (pNewWeapon->ClassMatches(CLASSNAME_ALYXGUN.ToCStr())) + if ( pNewWeapon->ClassMatches( CLASSNAME_ALYXGUN ) ) { // Put it away so we can pull it out properly GetActiveWeapon()->Holster(); @@ -3279,7 +3279,7 @@ void CNPC_Alyx::Weapon_Equip( CBaseCombatWeapon *pWeapon ) //----------------------------------------------------------------------------- bool CNPC_Alyx::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { - if(!pWeapon->ClassMatches(CLASSNAME_SHOTGUN.ToCStr())) + if( !pWeapon->ClassMatches( CLASSNAME_SHOTGUN ) ) return false; return BaseClass::Weapon_CanUse( pWeapon ); diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index d5781cf4adb..d2fe2f909d7 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -3824,7 +3824,7 @@ void CNPC_Combine::OnEndMoveAndShoot() WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { #ifdef MAPBASE - if( pWeapon->ClassMatches( gm_isz_class_AR2.ToCStr() ) ) + if( pWeapon->ClassMatches( gm_isz_class_AR2 ) ) #else if( FClassnameIs( pWeapon, "weapon_ar2" ) ) #endif @@ -3839,7 +3839,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea } } #ifdef MAPBASE - else if( pWeapon->ClassMatches( gm_isz_class_Shotgun.ToCStr() ) ) + else if( pWeapon->ClassMatches( gm_isz_class_Shotgun ) ) #else else if( FClassnameIs( pWeapon, "weapon_shotgun" ) ) #endif @@ -3854,7 +3854,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea return WEAPON_PROFICIENCY_PERFECT; } #ifdef MAPBASE - else if( pWeapon->ClassMatches( gm_isz_class_SMG1.ToCStr() ) ) + else if( pWeapon->ClassMatches( gm_isz_class_SMG1 ) ) #else else if( FClassnameIs( pWeapon, "weapon_smg1" ) ) #endif @@ -3862,7 +3862,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea return WEAPON_PROFICIENCY_GOOD; } #ifdef MAPBASE - else if ( pWeapon->ClassMatches( gm_isz_class_Pistol.ToCStr() ) ) + else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) ) { // Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer. return WEAPON_PROFICIENCY_VERY_GOOD; @@ -3876,7 +3876,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea //----------------------------------------------------------------------------- bool CNPC_Combine::HasShotgun() { - if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), s_iszShotgunClassname.ToCStr())) + if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), s_iszShotgunClassname)) { return true; } diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 1c0b37d475a..660cb218aec 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -401,8 +401,8 @@ Disposition_t CNPC_PlayerCompanion::IRelationType( CBaseEntity *pTarget ) (EntIsClass( ((CAI_BaseNPC *)pTarget)->GetActiveWeapon(), gm_iszShotgunClassname ) && ( !GetActiveWeapon() || !EntIsClass( GetActiveWeapon(), gm_iszShotgunClassname ) ) ) ) #else - ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) && - ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname.ToCStr() ) ) ) + ((CAI_BaseNPC *)pTarget)->GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) && + ( !GetActiveWeapon() || !GetActiveWeapon()->ClassMatches( gm_iszShotgunClassname ) ) ) #endif { if ( (pTarget->GetAbsOrigin() - GetAbsOrigin()).LengthSqr() < Square( 25 * 12 ) ) @@ -2816,7 +2816,7 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) #ifdef MAPBASE if (EntIsClass(pWeapon, gm_iszShotgunClassname)) #else - if (pWeapon->ClassMatches(gm_iszShotgunClassname.ToCStr())) + if( pWeapon->ClassMatches( gm_iszShotgunClassname ) ) #endif { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); From 0da4386694f2ade60bee25e6a450916cce19bf6f Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:17:17 +0200 Subject: [PATCH 15/29] Update entitylist.cpp --- sp/src/game/server/entitylist.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 29d87f50ab6..a254aa3d84f 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -570,7 +570,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEn continue; } - if ( pEntity->m_iClassname == iszClassname) + if (FClassnameIs(pEntity, iszClassname.ToCStr())) { return pEntity; } @@ -1418,7 +1418,7 @@ CBaseEntity *CGlobalEntityList::FindEntityNearestFacing( const Vector &origin, c continue; // Ignore if worldspawn - if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent")) + if (!FStrEq(ent->GetClassname(), "worldspawn") && !FStrEq(ent->GetClassname(), "soundent")) { bestDot = dot; best_ent = ent; From a28155135f9c1e338f3041e2059d2bb562a6ef46 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:37:33 +0200 Subject: [PATCH 16/29] Update GlobalStrings.h --- sp/src/game/server/mapbase/GlobalStrings.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index 15cca372458..f882dbe9457 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -75,8 +75,9 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) // Since classnames are pooled, the global string and the entity's classname should point to the same string in memory. // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. - // The the exception is weapons, because weapons replace classname with script name, check if is weapon before comparing by pointer. - if (!ent->IsBaseCombatWeapon()) + // The the exception is weapons, because weapons replace classname with script name. + // Check if it is not a weapon or it doesn't use a script before comparing by pointer. + if (!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] != '\0') return ent->m_iClassname == str2; return FStrEq(ent->GetClassname(), str2.ToCStr()); From 00ce0c0d6cda8ca26731fa4e1a52fe773b49961e Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:49:31 +0200 Subject: [PATCH 17/29] Update GlobalStrings.h --- sp/src/game/server/mapbase/GlobalStrings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index f882dbe9457..27602e30b1f 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -77,7 +77,7 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 ) // As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary. // The the exception is weapons, because weapons replace classname with script name. // Check if it is not a weapon or it doesn't use a script before comparing by pointer. - if (!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] != '\0') + if (!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] == '\0') return ent->m_iClassname == str2; return FStrEq(ent->GetClassname(), str2.ToCStr()); From b98790e4f9309383f1146f568e56d3ed622844b4 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:57:06 +0200 Subject: [PATCH 18/29] Update entitylist.cpp --- sp/src/game/server/entitylist.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index a254aa3d84f..66c4d04536b 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -570,7 +570,12 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEn continue; } - if (FClassnameIs(pEntity, iszClassname.ToCStr())) + if ((!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] != '\0')) + { + if (pEntity->m_iClassname == iszClassname) + return pEntity; + } + else if (FClassnameIs(pEntity, iszClassname.ToCStr())) { return pEntity; } From 5561bc453933ee1dbe4ba993656d80f5d3d2d1f7 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 01:26:52 +0200 Subject: [PATCH 19/29] Update entitylist.cpp --- sp/src/game/server/entitylist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 66c4d04536b..b1236093d3a 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -570,7 +570,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEn continue; } - if ((!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] != '\0')) + if (!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] == '\0') { if (pEntity->m_iClassname == iszClassname) return pEntity; From 7f3ca9e3af837c69d927d21f62dbfe7dd2396090 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:38:29 +0200 Subject: [PATCH 20/29] Update entitylist.cpp --- sp/src/game/server/entitylist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index b1236093d3a..b6c579a2462 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -570,7 +570,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByClassnameFast( CBaseEntity *pStartEn continue; } - if (!ent->IsBaseCombatWeapon() || static_cast(ent)->m_iszWeaponScript.Get()[0] == '\0') + if (!pEntity->IsBaseCombatWeapon() || static_cast(pEntity)->m_iszWeaponScript.Get()[0] == '\0') { if (pEntity->m_iClassname == iszClassname) return pEntity; From fd5ce2757e18aa338132bc5d2a185081bda8949b Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 15:34:21 +0200 Subject: [PATCH 21/29] Update entitylist.cpp that change doesn't make much sense --- sp/src/game/server/entitylist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index b6c579a2462..ad5c1c3f578 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -1375,7 +1375,7 @@ CBaseEntity *CGlobalEntityList::FindEntityClassNearestFacing( const Vector &orig if (FClassnameIs(ent,classname)) { // Ignore if worldspawn - if (!FClassnameIs( ent, "worldspawn" ) && !FClassnameIs( ent, "soundent")) + if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent")) { bestDot = dot; best_ent = ent; From 9d5b43a03eff8c0fc64c6d8ff34789b3c36daba8 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:52:13 +0200 Subject: [PATCH 22/29] hope these are pre last fixes - Added m_bJustRestored in C_BaseCombatWeapon::OnDataChanged to prevent false updates. - Now when update on client is wanted, the client will also run code to reset clip ammo to math results, but only in sp, because fsr it is sp only issue. - Compile fix in CNPC_Combine::HasShotgun(). - Removed gamestringpool.h include from basecombatweapon_shared.cpp. - Fixed memory leak in an unsuccessful ChangeScript case. --- sp/src/game/client/c_basecombatweapon.cpp | 34 +++++++++++++++++-- sp/src/game/server/entitylist.cpp | 2 +- sp/src/game/server/hl2/npc_combine.cpp | 2 +- .../game/shared/basecombatweapon_shared.cpp | 2 +- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index 0f884356131..10e3c76de4d 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -198,11 +198,41 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) Precache(); //cache weapon on client again, otherwise the client will always use default script name while server not } - //update script in case if wanted - if (m_iOldNeedsUpdate != m_iNeedsUpdate) + //update script in case if wanted, but not when restored (because value isn't saved) + if (m_iOldNeedsUpdate != m_iNeedsUpdate && !m_bJustRestored) { Precache(); m_iOldNeedsUpdate = m_iNeedsUpdate; //assign new value to prevent updates on client when we don't want + + //don't reset local ammo value in mp, needed only for sp + if (gpGlobals->maxClients > 1) + return; + + ConVarRef resetmode("sv_weapon_clips_reset_mode"); + + //we don't want to reset clips at all, return + if (!resetmode.IsValid() || resetmode.GetInt() == 0) + return; + + //we want to set max clips vals + if (resetmode.GetInt() == 1) + { + if (UsesClipsForAmmo1()) + m_iClip1 = GetMaxClip1(); + + if (UsesClipsForAmmo2()) + m_iClip2 = GetMaxClip2(); + } + + //we want to set max clip only if this weapon has more ammo in clips than max + if (resetmode.GetInt() == 2) + { + if (UsesClipsForAmmo1() && m_iClip1 > GetMaxClip1()) + m_iClip1 = GetMaxClip1(); + + if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) + m_iClip2 = GetMaxClip2(); + } } #endif } diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index ad5c1c3f578..43d780e6435 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -1423,7 +1423,7 @@ CBaseEntity *CGlobalEntityList::FindEntityNearestFacing( const Vector &origin, c continue; // Ignore if worldspawn - if (!FStrEq(ent->GetClassname(), "worldspawn") && !FStrEq(ent->GetClassname(), "soundent")) + if (!FStrEq( STRING(ent->m_iClassname), "worldspawn") && !FStrEq( STRING(ent->m_iClassname), "soundent")) { bestDot = dot; best_ent = ent; diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index d2fe2f909d7..833153ad59a 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -3876,7 +3876,7 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea //----------------------------------------------------------------------------- bool CNPC_Combine::HasShotgun() { - if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), s_iszShotgunClassname)) + if (GetActiveWeapon() && FClassnameIs(GetActiveWeapon(), s_iszShotgunClassname.ToCStr())) { return true; } diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 525c2080a78..650c9e699f5 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -39,7 +39,6 @@ #endif #ifdef MAPBASE - #include "gamestringpool.h" #include "mapbase_matchers_base.h" #endif @@ -2050,6 +2049,7 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) //don't if file doesn't exists if (!pKV) { + pKV->deleteThis(); //free up memory Warning("Error reading weapon data file \"%s\".\n", pszNewScript); return; } From cb1ec162d68f6213a133eb7fc11707ed2ee61a59 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:09:12 +0200 Subject: [PATCH 23/29] prev commit impr --- sp/src/game/client/c_basecombatweapon.cpp | 6 +++++- sp/src/game/shared/basecombatweapon_shared.cpp | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index 10e3c76de4d..4709bf58f58 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -198,7 +198,7 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) Precache(); //cache weapon on client again, otherwise the client will always use default script name while server not } - //update script in case if wanted, but not when restored (because value isn't saved) + //update script in case if wanted, but not when restored (or it will cause hud issues) if (m_iOldNeedsUpdate != m_iNeedsUpdate && !m_bJustRestored) { Precache(); @@ -222,6 +222,8 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) if (UsesClipsForAmmo2()) m_iClip2 = GetMaxClip2(); + + return; } //we want to set max clip only if this weapon has more ammo in clips than max @@ -232,6 +234,8 @@ void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) m_iClip2 = GetMaxClip2(); + + return; } } #endif diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 650c9e699f5..11846133a78 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -2049,7 +2049,6 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) //don't if file doesn't exists if (!pKV) { - pKV->deleteThis(); //free up memory Warning("Error reading weapon data file \"%s\".\n", pszNewScript); return; } @@ -2118,6 +2117,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2()) m_iClip2 = GetMaxClip2(); + + return; } //we want to set max clip only if this weapon has more ammo in clips than max @@ -2128,6 +2129,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) m_iClip2 = GetMaxClip2(); + + return; } } #endif From fb3b7ff717336bbea57d839668bd25365f6ce979 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:51:24 +0200 Subject: [PATCH 24/29] final fix (maybe) - Fixed weapon model not updated for NPCs after changescript input. Imagine, but I wasted entire day to find out that actually shooting by NPC from smg1 with shotgun script is something wrong wrong, while not shoting if the same weapon was picked up is right. - Replaced direct classname compare with FClassnameIs() to compare with replaced script name. --- sp/src/game/server/ai_basenpc.cpp | 10 ++-- .../game/shared/basecombatweapon_shared.cpp | 54 ++++++++++--------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index b690f9a06d5..20971dcb632 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -8349,8 +8349,7 @@ void CAI_BaseNPC::InputUnholsterWeapon( inputdata_t &inputdata ) { for (int i=0;im_iClassname == inputdata.value.StringID() ) + if ( m_hMyWeapons[i].Get() && FClassnameIs(m_hMyWeapons[i], inputdata.value.StringID().ToCStr()) ) { //Weapon_Switch(m_hMyWeapons[i]); //DoHolster(); @@ -8504,8 +8503,7 @@ void CAI_BaseNPC::InputChangeWeapon( inputdata_t &inputdata ) int iSwitchTo; // Index in m_hMyWeapons for (int i=0;im_iClassname == inputdata.value.StringID() ) + if ( m_hMyWeapons[i].Get() && FClassnameIs(m_hMyWeapons[i], inputdata.value.StringID().ToCStr()) ) { pSwitchTo = m_hMyWeapons[i]; iSwitchTo = i; @@ -15976,7 +15974,7 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) if (Q_strstr(myweapon, "WEPCLASS")) pass = (GetActiveWeapon()->WeaponClassFromString(myweapon) == GetActiveWeapon()->WeaponClassify()) ? !pass : pass; else - pass = (GetActiveWeapon()->m_iClassname == pInteraction->iszMyWeapon) ? !pass : pass; + pass = (FClassnameIs(pNPC->GetActiveWeapon(), pInteraction->iszTheirWeapon.ToCStr())) ? !pass : pass; if (!pass) continue; @@ -16005,7 +16003,7 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) if (Q_strstr(theirweapon, "WEPCLASS")) pass = (pNPC->GetActiveWeapon()->WeaponClassFromString(theirweapon) == pNPC->GetActiveWeapon()->WeaponClassify()) ? !pass : pass; else - pass = (pNPC->GetActiveWeapon()->m_iClassname == pInteraction->iszTheirWeapon) ? !pass : pass; + pass = (FClassnameIs(pNPC->GetActiveWeapon(), pInteraction->iszTheirWeapon.ToCStr())) ? !pass : pass; if (!pass) continue; diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 11846133a78..b9dbc75d2e0 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -2021,7 +2021,7 @@ void CBaseCombatWeapon::InputForceSecondaryFire( inputdata_t &inputdata ) } //----------------------------------------------------------------------------- -// Purpose: Input to change the weapon script name and re-Precache +// Purpose: Input to change the weapon script. //----------------------------------------------------------------------------- void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) { @@ -2033,8 +2033,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) //don't update if other weapon that owns my owner uses the same script if (GetOwner() && GetOwner()->Weapon_OwnsThisType(pszNewScript)) - return; - + return; + char sz[128]; Q_snprintf(sz, sizeof(sz), "scripts/%s", pszNewScript); @@ -2052,7 +2052,7 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) Warning("Error reading weapon data file \"%s\".\n", pszNewScript); return; } - + pKV->deleteThis(); //free up memory //copy new data for networked and stored @@ -2065,34 +2065,42 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) } m_iNeedsUpdate++; //trigger client update - + Precache(); //update with new script - if (GetOwner() && GetOwner()->IsPlayer()) + if (GetOwner()) { - //this updates wpn's vm at the same time as wpn's script, instead of waiting 2-6 seconds - if (GetOwner()->GetActiveWeapon() == this) - SetViewModel(); + if (GetOwner()->IsPlayer() == false) + { + //update model data for npc + SetModel(GetWorldModel()); + } + else + { + //this updates wpn's vm at the same time as wpn's script, instead of waiting 2-6 seconds + if (GetOwner()->GetActiveWeapon() == this) + SetViewModel(); - SetModel(GetViewModel()); //this fixes wrong sequence nums (DOESN'T AFFECT WORLD MODEL) + SetModel(GetViewModel()); //this fixes wrong sequence nums (DOESN'T AFFECT WORLD MODEL) - //use deploy anim if we want - if (GetOwner()->GetActiveWeapon() == this) - { - if (sv_weapon_vm_anim_reset_mode.GetBool()) + //use deploy anim if we want + if (GetOwner()->GetActiveWeapon() == this) { - Deploy(); - } - else - { - SendWeaponAnim(ACT_VM_IDLE); + if (sv_weapon_vm_anim_reset_mode.GetBool()) + { + Deploy(); + } + else + { + SendWeaponAnim(ACT_VM_IDLE); + } } } } //if i have no owner - reset collsion model with bbox + check if my new wm has collision //NOTE: no need if owned by NPC or plr as they update collision when drop weapons - if (!GetOwner()) + else { SetModel(GetWorldModel()); VPhysicsDestroyObject(); @@ -2108,7 +2116,7 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) //we don't want to reset clips at all, return if (sv_weapon_clips_reset_mode.GetInt() == 0) return; - + //we want to set max clipw vals if (sv_weapon_clips_reset_mode.GetInt() == 1) { @@ -2117,8 +2125,6 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2()) m_iClip2 = GetMaxClip2(); - - return; } //we want to set max clip only if this weapon has more ammo in clips than max @@ -2129,8 +2135,6 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) m_iClip2 = GetMaxClip2(); - - return; } } #endif From efd465c6cb27f10bca16b18c55d13ace8026f7f7 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:53:25 +0200 Subject: [PATCH 25/29] Update basecombatweapon_shared.cpp --- sp/src/game/shared/basecombatweapon_shared.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index b9dbc75d2e0..b5aa9bbbb51 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -2125,6 +2125,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2()) m_iClip2 = GetMaxClip2(); + + return; } //we want to set max clip only if this weapon has more ammo in clips than max @@ -2135,6 +2137,8 @@ void CBaseCombatWeapon::InputChangeScript(inputdata_t& inputdata) if (UsesClipsForAmmo2() && m_iClip2 > GetMaxClip2()) m_iClip2 = GetMaxClip2(); + + return; } } #endif From 7c7575a50480c6dd8a738af48f59eef0c512aaee Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:42:13 +0200 Subject: [PATCH 26/29] Update ai_basenpc.cpp --- sp/src/game/server/ai_basenpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 20971dcb632..194309325fe 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -15974,7 +15974,7 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) if (Q_strstr(myweapon, "WEPCLASS")) pass = (GetActiveWeapon()->WeaponClassFromString(myweapon) == GetActiveWeapon()->WeaponClassify()) ? !pass : pass; else - pass = (FClassnameIs(pNPC->GetActiveWeapon(), pInteraction->iszTheirWeapon.ToCStr())) ? !pass : pass; + pass = (FClassnameIs(GetActiveWeapon(), pInteraction->iszMyWeapon.ToCStr())) ? !pass : pass; if (!pass) continue; From 34c254e3991bdd0c4137e2e4a45facc8fdf6048a Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:52:12 +0200 Subject: [PATCH 27/29] Update basecombatweapon_shared.cpp added check if script file exists in KeyValue(). --- .../game/shared/basecombatweapon_shared.cpp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index b5aa9bbbb51..e077f2207e2 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -385,8 +385,28 @@ bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) } if (FStrEq(szKeyName, "weaponscriptname")) { - if (szValue[0] != '\0') //if not empty - use val, else get real classname + if (szValue[0] != '\0') //if not empty - use if file exists { + char sz[128]; + Q_snprintf(sz, sizeof(sz), "scripts/%s", szValue); + + KeyValues* pKV = ReadEncryptedKVFile(filesystem, sz, GetEncryptionKey(), + #if defined( DOD_DLL ) + true // Only read .ctx files! + #else + false + #endif + ); + + //don't if file doesn't exists + if (!pKV) + { + Warning("Error reading weapon data file \"%s\".\n", szValue); + return; + } + + pKV->deleteThis(); //free up memory + Q_strncpy(m_iszWeaponScript.GetForModify(), szValue, MAX_WEAPON_STRING); } From 60070617beb721a72d2b2defc9575d7e8a6fcb85 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:51:11 +0200 Subject: [PATCH 28/29] Update basecombatweapon_shared.cpp --- sp/src/game/shared/basecombatweapon_shared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index e077f2207e2..8178961f970 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -402,7 +402,7 @@ bool CBaseCombatWeapon::KeyValue( const char *szKeyName, const char *szValue ) if (!pKV) { Warning("Error reading weapon data file \"%s\".\n", szValue); - return; + return true; } pKV->deleteThis(); //free up memory From e6f41d0e0b2b9e2d69f76c8f217e267e23b7f797 Mon Sep 17 00:00:00 2001 From: "MyGamepedia (Sorry For My English)" <103366204+MyGamepedia@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:57:35 +0200 Subject: [PATCH 29/29] Update multiplay_gamerules.cpp i'm not sure it's that wanted, but i guess somebody may want to add "weapon" param in "player_death" event. --- sp/src/game/shared/multiplay_gamerules.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sp/src/game/shared/multiplay_gamerules.cpp b/sp/src/game/shared/multiplay_gamerules.cpp index 49ed4efe371..b1d7c12305c 100644 --- a/sp/src/game/shared/multiplay_gamerules.cpp +++ b/sp/src/game/shared/multiplay_gamerules.cpp @@ -846,13 +846,21 @@ ConVarRef suitcharger( "sk_suitcharger" ); } else { +#ifdef MAPBASE + killer_weapon_name = pInflictor->GetClassname(); +#else killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy +#endif } } } else { +#ifdef MAPBASE + killer_weapon_name = pInflictor->GetClassname(); +#else killer_weapon_name = STRING( pInflictor->m_iClassname ); +#endif } // strip the NPC_* or weapon_* from the inflictor's classname