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..5e41f871857 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 ); @@ -820,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/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..f86a28e8d92 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -924,18 +924,27 @@ void CNPC_Citizen::FixupMattWeapon() if ( pWeapon && pWeapon->ClassMatches( "weapon_crowbar" ) && NameMatches( "matt" ) ) #endif { +#ifdef MAPBASE + 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); +#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; 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..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; @@ -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..53dc8126fd6 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 ) && - ( !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..e2fec69190c 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -38,6 +38,11 @@ #endif +#ifdef MAPBASE + #include "gamestringpool.h" + #include "mapbase_matchers_base.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -60,6 +65,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 @@ -321,7 +340,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", m_iClassname, GetClassname()); +#else + Warning( "Error reading weapon data file for: %s\n", GetClassname() ); +#endif // Remove( ); //don't remove, this gets released soon! } } @@ -361,6 +384,15 @@ 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); + } + + return true; + } else if ( FStrEq(szKeyName, "spawnflags") ) { m_spawnflags = atoi(szValue); @@ -382,6 +414,28 @@ 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. +//----------------------------------------------------------------------------- +const char* CBaseCombatWeapon::GetClassname() +{ + 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 //----------------------------------------------------------------------------- @@ -1953,6 +2007,114 @@ void CBaseCombatWeapon::InputForceSecondaryFire( inputdata_t &inputdata ) { InputForceFire(inputdata, true); } + +//----------------------------------------------------------------------------- +// 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; + + 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); + + //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 +3405,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 +3437,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 +3574,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 +3592,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 +3606,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 +3621,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..d3962d73783 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 ); + virtual const char* GetClassname(); + 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