diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index ff8e6d64f98..4709bf58f58 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -191,6 +191,54 @@ 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, but not when restored (or it will cause hud issues) + 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(); + + return; + } + + //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(); + + return; + } + } +#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..c7520b0bdd6 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.cpp b/sp/src/game/server/ai_basenpc.cpp index b690f9a06d5..194309325fe 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(GetActiveWeapon(), pInteraction->iszMyWeapon.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/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..4756ec4d4ad 100644 --- a/sp/src/game/server/basecombatweapon.cpp +++ b/sp/src/game/server/basecombatweapon.cpp @@ -199,10 +199,15 @@ 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 ) + 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 weapon \"%s\" with script \"%s\"!\n", STRING(m_iClassname), 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/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 29d87f50ab6..43d780e6435 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 ( pEntity->m_iClassname == iszClassname) + if (!pEntity->IsBaseCombatWeapon() || static_cast(pEntity)->m_iszWeaponScript.Get()[0] == '\0') + { + if (pEntity->m_iClassname == iszClassname) + return pEntity; + } + else if (FClassnameIs(pEntity, iszClassname.ToCStr())) { return pEntity; } @@ -1370,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; 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_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index c66ca4e5237..869eb9884b1 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -924,18 +924,32 @@ 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 + +#ifndef MAPBASE pWeapon->Activate(); Weapon_Equip( pWeapon ); +#endif } } 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/mapbase/GlobalStrings.h b/sp/src/game/server/mapbase/GlobalStrings.h index 13c4775a6f8..27602e30b1f 100644 --- a/sp/src/game/server/mapbase/GlobalStrings.h +++ b/sp/src/game/server/mapbase/GlobalStrings.h @@ -70,11 +70,17 @@ 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 ent->m_iClassname == str2; + // 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()); } // ------------------------------------------------------------- diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index bfba16e3e04..8178961f970 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 "mapbase_matchers_base.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -60,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, + "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, + "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 +339,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", STRING(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 +383,35 @@ 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 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 true; + } + + pKV->deleteThis(); //free up memory + + Q_strncpy(m_iszWeaponScript.GetForModify(), szValue, MAX_WEAPON_STRING); + } + + return true; + } else if ( FStrEq(szKeyName, "spawnflags") ) { m_spawnflags = atoi(szValue); @@ -382,6 +433,38 @@ 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 (m_iszWeaponScript.Get()[0] != '\0') + { + return m_iszWeaponScript.Get(); + } + + 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(string_t nameStr) +{ + 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(const char* pszClassOrWildcard) +{ + return Matcher_NamesMatch(pszClassOrWildcard, GetClassname()); +} #endif //----------------------------------------------------------------------------- @@ -573,10 +656,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_iszWeaponScript.Get()[0] != '\0') + return m_iszWeaponScript.Get(); + return GetWpnData().szClassName; } @@ -1953,6 +2039,128 @@ void CBaseCombatWeapon::InputForceSecondaryFire( inputdata_t &inputdata ) { InputForceFire(inputdata, true); } + +//----------------------------------------------------------------------------- +// Purpose: Input to change the weapon script. +//----------------------------------------------------------------------------- +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; + } + + pKV->deleteThis(); //free up memory + + //copy new data for networked and stored + Q_strncpy(m_iszWeaponScript.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()) + { + 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) + + //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 + else + { + 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(); + + return; + } + + //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(); + + return; + } +} #endif //----------------------------------------------------------------------------- @@ -3243,6 +3451,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_iszWeaponScript, 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 +3483,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 @@ -3411,15 +3625,21 @@ 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 )), - RecvPropInt( RECVINFO( m_nViewModelIndex ) ), - RecvPropBool( RECVINFO( m_bFlipViewModel ) ), + +#ifdef MAPBASE + RecvPropInt(RECVINFO(m_iNeedsUpdate)), +#endif #endif END_NETWORK_TABLE() @@ -3430,14 +3650,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_iszWeaponScript) ), SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_iNeedsUpdate), 0, SPROP_UNSIGNED ), #endif #else @@ -3445,14 +3665,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_iszWeaponScript) ), #endif #endif diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index d7fd5c47d4e..0b99806b367 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -217,6 +217,11 @@ 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(); +#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 ); @@ -569,6 +574,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 +740,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_iszWeaponScript, 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/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