From efdc7c38f7f13b75b5b8bf8f13fb566fa75e31b1 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:54:42 +0100 Subject: [PATCH 1/4] Ammo count and a new cvar has been added --- sp/src/game/server/hl2/npc_turret_floor.cpp | 99 +++++++++++++++------ sp/src/game/server/hl2/npc_turret_floor.h | 8 ++ 2 files changed, 82 insertions(+), 25 deletions(-) diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index f365dfd74ab..5c074d07399 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -38,6 +38,10 @@ const char *GetMassEquivalent(float flMass); //Debug visualization ConVar g_debug_turret( "g_debug_turret", "0" ); +#ifdef MAPBASE +ConVar sk_turret_dmg( "sk_turret_dmg", "0" ); +#endif + extern ConVar physcannon_tracelength; #if defined(MAPBASE) && defined(HL2_EPISODIC) @@ -85,6 +89,9 @@ int ACT_FLOOR_TURRET_FIRE; BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iAmmoCount, FIELD_INTEGER, "ammocount"), +#endif DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ), DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ), @@ -144,6 +151,7 @@ BEGIN_DATADESC( CNPC_FloorTurret ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "CreateSprite", InputCreateSprite ), DEFINE_INPUTFUNC( FIELD_VOID, "DestroySprite", InputDestroySprite ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "ChangeAmmoCount", InputChangeAmmoCount ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestruct", InputSelfDestruct ), @@ -189,6 +197,10 @@ CNPC_FloorTurret::CNPC_FloorTurret( void ) : m_vecGoalAngles.Init(); m_vecEnemyLKP = vec3_invalid; + +#ifdef MAPBASE + KeyValue("ammocount", "-1"); +#endif } //----------------------------------------------------------------------------- @@ -1152,34 +1164,52 @@ void CNPC_FloorTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy, { FireBulletsInfo_t info; - if ( !bStrict && GetEnemy() != NULL ) + if ( m_iAmmoCount > 0 || m_iAmmoCount == -1 ) { - Vector vecDir = GetActualShootTrajectory( vecSrc ); - info.m_vecSrc = vecSrc; - info.m_vecDirShooting = vecDir; - info.m_iTracerFreq = 1; - info.m_iShots = 1; - info.m_pAttacker = this; - info.m_vecSpread = VECTOR_CONE_PRECALCULATED; - info.m_flDistance = MAX_COORD_RANGE; - info.m_iAmmoType = m_iAmmoType; - } - else - { - info.m_vecSrc = vecSrc; - info.m_vecDirShooting = vecDirToEnemy; - info.m_iTracerFreq = 1; - info.m_iShots = 1; - info.m_pAttacker = this; - info.m_vecSpread = GetAttackSpread( NULL, GetEnemy() ); - info.m_flDistance = MAX_COORD_RANGE; - info.m_iAmmoType = m_iAmmoType; - } + if ( !bStrict && GetEnemy() != NULL ) + { + Vector vecDir = GetActualShootTrajectory( vecSrc ); + + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDir; + info.m_iTracerFreq = 1; +#ifdef MAPBASE + info.m_flDamage = sk_turret_dmg.GetFloat(); +#endif + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = VECTOR_CONE_PRECALCULATED; + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + else + { + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDirToEnemy; + info.m_iTracerFreq = 1; +#ifdef MAPBASE + info.m_flDamage = sk_turret_dmg.GetFloat(); +#endif + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = GetAttackSpread( NULL, GetEnemy() ); + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + + FireBullets( info ); + EmitSound( "NPC_FloorTurret.ShotSounds", m_ShotSounds ); + DoMuzzleFlash(); - FireBullets( info ); - EmitSound( "NPC_FloorTurret.ShotSounds", m_ShotSounds ); - DoMuzzleFlash(); +#ifdef MAPBASE + if( m_iAmmoCount > 0 ) + m_iAmmoCount--; + + if ( m_iAmmoCount == 0 && !HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) ) + AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); +#endif + } } //----------------------------------------------------------------------------- @@ -1791,6 +1821,9 @@ void CNPC_FloorTurret::InputDisable( inputdata_t &inputdata ) void CNPC_FloorTurret::InputDepleteAmmo( inputdata_t &inputdata ) { AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); +#ifdef MAPBASE + m_iAmmoCount = 0; +#endif } //----------------------------------------------------------------------------- @@ -1799,9 +1832,25 @@ void CNPC_FloorTurret::InputDepleteAmmo( inputdata_t &inputdata ) void CNPC_FloorTurret::InputRestoreAmmo( inputdata_t &inputdata ) { RemoveSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); +#ifdef MAPBASE + m_iAmmoCount = -1; +#endif } #ifdef MAPBASE +void CNPC_FloorTurret::InputChangeAmmoCount( inputdata_t &inputdata ) +{ + if ( inputdata.value.Int() > 0 || inputdata.value.Int() == -1 ) + { + if ( HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) ) + RemoveSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); + } + else if ( inputdata.value.Int() == 0 ) + AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); + + m_iAmmoCount = inputdata.value.Int(); +} + //----------------------------------------------------------------------------- // Purpose: Creates the sprite if it has been destroyed //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_turret_floor.h b/sp/src/game/server/hl2/npc_turret_floor.h index af006a01434..99b9d68457f 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.h +++ b/sp/src/game/server/hl2/npc_turret_floor.h @@ -45,6 +45,7 @@ enum eyeState_t #define SF_FLOOR_TURRET_CITIZEN 0x00000200 // Citizen modified turret #ifdef MAPBASE #define SF_FLOOR_TURRET_NO_SPRITE 0x00000400 +#define SF_FLOOR_TURRET_DIE_PERMANENTLY 0x00000800 #endif class CTurretTipController; @@ -214,6 +215,10 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul void DryFire( void ); void UpdateMuzzleMatrix(); +#ifdef MAPBASE + void InputChangeAmmoCount( inputdata_t& inputdata ); +#endif + protected: matrix3x4_t m_muzzleToWorld; int m_muzzleToWorldTick; @@ -238,6 +243,9 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul #ifndef MAPBASE // Replaced with m_nSkin. int m_iKeySkin; #endif +#ifdef MAPBASE + int m_iAmmoCount; +#endif CHandle m_hLastNPCToKickMe; // Stores the last NPC who tried to knock me over float m_flKnockOverFailedTime; // Time at which we should tell the NPC that he failed to knock me over From bbebc55b633a45c6cecb6ebb3c8f90c9b7e9c26d Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Fri, 27 Mar 2026 22:02:24 +0100 Subject: [PATCH 2/4] Added new keyvalues --- sp/src/game/server/hl2/npc_turret_floor.cpp | 78 ++++++++++++++++++++- sp/src/game/server/hl2/npc_turret_floor.h | 11 +++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index 5c074d07399..93a54d30062 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -39,7 +39,8 @@ const char *GetMassEquivalent(float flMass); ConVar g_debug_turret( "g_debug_turret", "0" ); #ifdef MAPBASE -ConVar sk_turret_dmg( "sk_turret_dmg", "0" ); +ConVar sk_floor_turret_dmg( "sk_turret_dmg", "0" ); +ConVar sk_floor_turret_health( "sk_turret_health", "100" ); #endif extern ConVar physcannon_tracelength; @@ -91,6 +92,9 @@ BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), #ifdef MAPBASE DEFINE_KEYFIELD( m_iAmmoCount, FIELD_INTEGER, "ammocount"), + DEFINE_KEYFIELD( m_bKillable, FIELD_BOOLEAN, "killable"), + DEFINE_KEYFIELD( m_bImmovable, FIELD_BOOLEAN, "immovable"), + DEFINE_KEYFIELD( m_bBehaviorOnFall, FIELD_INTEGER, "behavioronfall"), #endif DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ), DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), @@ -200,6 +204,9 @@ CNPC_FloorTurret::CNPC_FloorTurret( void ) : #ifdef MAPBASE KeyValue("ammocount", "-1"); + KeyValue("killable", "0"); + KeyValue("immovable", "0"); + KeyValue("behavioronfall", "0"); #endif } @@ -331,8 +338,22 @@ void CNPC_FloorTurret::Spawn( void ) m_HackedGunPos = Vector( 0, 0, 12.75 ); SetViewOffset( EyeOffset( ACT_IDLE ) ); m_flFieldOfView = 0.4f; // 60 degrees +#ifdef MAPBASE + if ( m_bKillable ) + m_takedamage = DAMAGE_YES; + else + m_takedamage = DAMAGE_EVENTS_ONLY; + + if ( m_bImmovable ) + { + SetSolid( SOLID_VPHYSICS ); + } + + m_iHealth = sk_floor_turret_health.GetInt(); +#else m_takedamage = DAMAGE_EVENTS_ONLY; m_iHealth = 100; +#endif m_iMaxHealth = 100; AddEFlags( EFL_NO_DISSOLVE ); @@ -379,7 +400,12 @@ void CNPC_FloorTurret::Spawn( void ) // Don't allow us to skip animation setup because our attachments are critical to us! SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); +#ifdef MAPBASE + if ( !m_bImmovable ) + CreateVPhysics(); +#else CreateVPhysics(); +#endif SetState(NPC_STATE_IDLE); } @@ -1175,7 +1201,7 @@ void CNPC_FloorTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy, info.m_vecDirShooting = vecDir; info.m_iTracerFreq = 1; #ifdef MAPBASE - info.m_flDamage = sk_turret_dmg.GetFloat(); + info.m_flDamage = sk_floor_turret_dmg.GetFloat(); #endif info.m_iShots = 1; info.m_pAttacker = this; @@ -1189,7 +1215,7 @@ void CNPC_FloorTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy, info.m_vecDirShooting = vecDirToEnemy; info.m_iTracerFreq = 1; #ifdef MAPBASE - info.m_flDamage = sk_turret_dmg.GetFloat(); + info.m_flDamage = sk_floor_turret_dmg.GetFloat(); #endif info.m_iShots = 1; info.m_pAttacker = this; @@ -1430,6 +1456,9 @@ void CNPC_FloorTurret::InactiveThink( void ) //----------------------------------------------------------------------------- void CNPC_FloorTurret::ReturnToLife( void ) { + if ( m_bBehaviorOnFall == 1 ) + return; + m_flThrashTime = 0; // Enable the tip controller @@ -1935,6 +1964,45 @@ int CNPC_FloorTurret::VPhysicsTakeDamage( const CTakeDamageInfo &info ) return BaseClass::VPhysicsTakeDamage( info ); } +#ifdef MAPBASE +void CNPC_FloorTurret::Explode() +{ + if ( !m_bImmovable ) + CreateVPhysics(); + + m_flDestructStartTime = gpGlobals->curtime; + m_flPingTime = gpGlobals->curtime; + m_bSelfDestructing = true; + + SetThink( &CNPC_FloorTurret::SelfDestructThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); + + // Create the dust effect in place + m_hFizzleEffect = ( CParticleSystem* ) CreateEntityByName( "info_particle_system" ); + if ( m_hFizzleEffect != NULL ) + { + Vector vecUp; + GetVectors( NULL, NULL, &vecUp ); + + // Setup our basic parameters + m_hFizzleEffect->KeyValue( "start_active", "1" ); + m_hFizzleEffect->KeyValue( "effect_name", "explosion_turret_fizzle" ); + m_hFizzleEffect->SetParent( this ); + m_hFizzleEffect->SetAbsOrigin( WorldSpaceCenter() + ( vecUp * 12.0f ) ); + DispatchSpawn( m_hFizzleEffect ); + m_hFizzleEffect->Activate(); + } +} + +void CNPC_FloorTurret::Event_Killed( const CTakeDamageInfo& info ) +{ + if ( (m_bKillable && m_iHealth < 0) || m_bBehaviorOnFall == 2 ) + Explode(); + + BaseClass::Event_Killed( info ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : &info - @@ -2245,6 +2313,9 @@ void CNPC_FloorTurret::SelfDestructThink( void ) //----------------------------------------------------------------------------- void CNPC_FloorTurret::InputSelfDestruct( inputdata_t &inputdata ) { +#ifdef MAPBASE + Explode(); +#else // Ka-boom! m_flDestructStartTime = gpGlobals->curtime; m_flPingTime = gpGlobals->curtime; @@ -2268,6 +2339,7 @@ void CNPC_FloorTurret::InputSelfDestruct( inputdata_t &inputdata ) DispatchSpawn( m_hFizzleEffect ); m_hFizzleEffect->Activate(); } +#endif } // diff --git a/sp/src/game/server/hl2/npc_turret_floor.h b/sp/src/game/server/hl2/npc_turret_floor.h index 99b9d68457f..e826b87cf62 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.h +++ b/sp/src/game/server/hl2/npc_turret_floor.h @@ -71,6 +71,10 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul virtual void PlayerPenetratingVPhysics( void ); virtual int VPhysicsTakeDamage( const CTakeDamageInfo &info ); virtual bool CanBecomeServerRagdoll( void ) { return false; } +#ifdef MAPBASE + virtual void Event_Killed( const CTakeDamageInfo& info ); + void Explode(); +#endif #ifdef HL2_EPISODIC // We don't want to be NPCSOLID because we'll collide with NPC clips @@ -245,6 +249,13 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul #endif #ifdef MAPBASE int m_iAmmoCount; + bool m_bImmovable; + bool m_bKillable; + + int m_bBehaviorOnFall; + // 0 - normal + // 1 - deactivate and never come back + // 2 - explode #endif CHandle m_hLastNPCToKickMe; // Stores the last NPC who tried to knock me over From 86c52dcbdddf14bcdd619137051fdbbbe05fe5a7 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:27:38 +0100 Subject: [PATCH 3/4] corrected an error with convars --- sp/src/game/server/hl2/npc_turret_floor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index 93a54d30062..9e9e74c4db2 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -39,8 +39,8 @@ const char *GetMassEquivalent(float flMass); ConVar g_debug_turret( "g_debug_turret", "0" ); #ifdef MAPBASE -ConVar sk_floor_turret_dmg( "sk_turret_dmg", "0" ); -ConVar sk_floor_turret_health( "sk_turret_health", "100" ); +ConVar sk_floor_turret_dmg( "sk_floor_turret_dmg", "3" ); +ConVar sk_floor_turret_health( "sk_floor_turret_health", "100" ); #endif extern ConVar physcannon_tracelength; From b04a059a5eeb511ca6ba98ae64e103358f964ca8 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sat, 28 Mar 2026 15:53:30 +0100 Subject: [PATCH 4/4] Added outputs --- sp/src/game/server/hl2/npc_turret_floor.cpp | 12 ++++++++++++ sp/src/game/server/hl2/npc_turret_floor.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index 9e9e74c4db2..966fd67f993 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -166,6 +166,8 @@ BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ), #ifdef MAPBASE DEFINE_OUTPUT( m_OnStartTipped, "OnStartTipped" ), + DEFINE_OUTPUT( m_OnDepletedAmmo, "OnDepletedAmmo" ), + DEFINE_OUTPUT( m_OnExploded, "OnExploded" ), #endif DEFINE_BASENPCINTERACTABLE_DATADESC(), @@ -1233,7 +1235,10 @@ void CNPC_FloorTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy, m_iAmmoCount--; if ( m_iAmmoCount == 0 && !HasSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ) ) + { AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); + m_OnDepletedAmmo.FireOutput( this, this ); + } #endif } } @@ -1852,6 +1857,7 @@ void CNPC_FloorTurret::InputDepleteAmmo( inputdata_t &inputdata ) AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); #ifdef MAPBASE m_iAmmoCount = 0; + m_OnDepletedAmmo.FireOutput( this, this ); #endif } @@ -1875,9 +1881,12 @@ void CNPC_FloorTurret::InputChangeAmmoCount( inputdata_t &inputdata ) RemoveSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); } else if ( inputdata.value.Int() == 0 ) + { AddSpawnFlags( SF_FLOOR_TURRET_OUT_OF_AMMO ); + } m_iAmmoCount = inputdata.value.Int(); + m_OnDepletedAmmo.FireOutput( this, this ); } //----------------------------------------------------------------------------- @@ -2248,6 +2257,9 @@ void CNPC_FloorTurret::BreakThink( void ) } // We're done! +#ifdef MAPBASE + m_OnExploded.FireOutput( this, this ); +#endif UTIL_Remove( this ); } diff --git a/sp/src/game/server/hl2/npc_turret_floor.h b/sp/src/game/server/hl2/npc_turret_floor.h index e826b87cf62..babe0ea1c34 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.h +++ b/sp/src/game/server/hl2/npc_turret_floor.h @@ -286,6 +286,8 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul COutputEvent m_OnPhysGunDrop; #ifdef MAPBASE COutputEvent m_OnStartTipped; + COutputEvent m_OnDepletedAmmo; + COutputEvent m_OnExploded; #endif bool m_bHackedByAlyx;