Skip to content

Commit 722d1d8

Browse files
committed
Add HL2MP bullet FX fixes for NPCs and other entities
1 parent 0624710 commit 722d1d8

File tree

8 files changed

+217
-0
lines changed

8 files changed

+217
-0
lines changed

src/game/client/c_ai_basenpc.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#endif
1414

1515
#ifdef MAPBASE_MP
16+
#include "takedamageinfo.h"
1617
#ifdef HL2MP
1718
#include "c_hl2mp_playerresource.h"
1819
#endif
@@ -25,6 +26,20 @@
2526

2627
#define PING_MAX_TIME 2.0
2728

29+
#ifdef MAPBASE_MP
30+
BEGIN_RECV_TABLE_NOBASE( C_AI_BaseNPC, DT_BaseNPCGameData )
31+
RecvPropInt( RECVINFO( m_iHealth ) ),
32+
RecvPropInt( RECVINFO( m_takedamage ) ),
33+
RecvPropInt( RECVINFO( m_bloodColor ) ),
34+
//RecvPropString( RECVINFO( m_szNetname ) ), // Transmitted by player resource now
35+
RecvPropInt( RECVINFO( m_nDefaultPlayerRelationship ) ),
36+
END_RECV_TABLE();
37+
#endif
38+
39+
#ifdef CAI_BaseNPC
40+
#undef CAI_BaseNPC
41+
#endif
42+
2843
IMPLEMENT_CLIENTCLASS_DT( C_AI_BaseNPC, DT_AI_BaseNPC, CAI_BaseNPC )
2944
RecvPropInt( RECVINFO( m_lifeState ) ),
3045
RecvPropBool( RECVINFO( m_bPerformAvoidance ) ),
@@ -37,6 +52,9 @@ IMPLEMENT_CLIENTCLASS_DT( C_AI_BaseNPC, DT_AI_BaseNPC, CAI_BaseNPC )
3752
RecvPropInt( RECVINFO( m_bSpeedModActive ) ),
3853
RecvPropBool( RECVINFO( m_bImportanRagdoll ) ),
3954
RecvPropFloat( RECVINFO( m_flTimePingEffect ) ),
55+
#ifdef MAPBASE_MP
56+
RecvPropDataTable( "npc_gamedata", 0, 0, &REFERENCE_RECV_TABLE( DT_BaseNPCGameData ) ),
57+
#endif
4058
END_RECV_TABLE()
4159

4260
extern ConVar cl_npc_speedmod_intime;
@@ -53,6 +71,9 @@ bool NPC_IsImportantNPC( C_BaseAnimating *pAnimating )
5371

5472
C_AI_BaseNPC::C_AI_BaseNPC()
5573
{
74+
#ifdef MAPBASE_MP
75+
SetBloodColor( DONT_BLEED );
76+
#endif
5677
}
5778

5879
//-----------------------------------------------------------------------------
@@ -205,5 +226,89 @@ const char *C_AI_BaseNPC::GetPlayerName( void ) const
205226

206227
return BaseClass::GetPlayerName();
207228
}
229+
230+
//-----------------------------------------------------------------------------
231+
// Purpose:
232+
//-----------------------------------------------------------------------------
233+
void C_AI_BaseNPC::DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
234+
{
235+
m_fNoDamageDecal = false;
236+
237+
if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
238+
{
239+
if ( IsPlayerAlly( ToBasePlayer( info.GetAttacker() ) ) )
240+
{
241+
m_fNoDamageDecal = true;
242+
return;
243+
}
244+
}
245+
246+
BaseClass::DispatchTraceAttack( info, vecDir, ptr, pAccumulator );
247+
}
248+
249+
//-----------------------------------------------------------------------------
250+
// Purpose:
251+
//-----------------------------------------------------------------------------
252+
void C_AI_BaseNPC::DecalTrace( trace_t *pTrace, char const *decalName )
253+
{
254+
if ( m_fNoDamageDecal )
255+
{
256+
// Don't do impact decals when we shouldn't
257+
// (adapts an existing hack from singleplayer HL2, see serverside counterpart)
258+
m_fNoDamageDecal = false;
259+
return;
260+
}
261+
BaseClass::DecalTrace( pTrace, decalName );
262+
}
263+
264+
//-----------------------------------------------------------------------------
265+
// Purpose:
266+
//-----------------------------------------------------------------------------
267+
void C_AI_BaseNPC::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
268+
{
269+
if ( m_fNoDamageDecal )
270+
{
271+
// Don't do impact decals when we shouldn't
272+
// (adapts an existing hack from singleplayer HL2, see serverside counterpart)
273+
m_fNoDamageDecal = false;
274+
return;
275+
}
276+
BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
277+
}
278+
279+
//-----------------------------------------------------------------------------
280+
// Purpose:
281+
//-----------------------------------------------------------------------------
282+
bool C_AI_BaseNPC::IsPlayerAlly( C_BasePlayer *pPlayer )
283+
{
284+
if ( pPlayer == NULL )
285+
{
286+
pPlayer = C_BasePlayer::GetLocalPlayer();
287+
}
288+
289+
if ( pPlayer->GetTeamNumber() == TEAM_UNASSIGNED )
290+
{
291+
// AI relationship code isn't available here, so we currently transmit a var from the server to determine if we're, at least generically, an ally
292+
return (m_nDefaultPlayerRelationship == GR_TEAMMATE);
293+
}
294+
else if (GetTeamNumber() == pPlayer->GetTeamNumber())
295+
{
296+
// Same team probably means allies
297+
return true;
298+
}
299+
300+
return false;
301+
}
302+
303+
//-----------------------------------------------------------------------------
304+
// Purpose:
305+
//-----------------------------------------------------------------------------
306+
bool C_AI_BaseNPC::IsNeutralTo( C_BasePlayer *pPlayer )
307+
{
308+
if ( IsPlayerAlly( pPlayer ) )
309+
return false;
310+
311+
return (m_nDefaultPlayerRelationship == GR_NOTTEAMMATE);
312+
}
208313
#endif
209314

src/game/client/c_ai_basenpc.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414

1515
#include "c_basecombatcharacter.h"
16+
#ifdef MAPBASE_MP
17+
#include "ai_debug_shared.h"
18+
#endif
1619

1720
// NOTE: Moved all controller code into c_basestudiomodel
1821
class C_AI_BaseNPC : public C_BaseCombatCharacter
@@ -42,7 +45,14 @@ class C_AI_BaseNPC : public C_BaseCombatCharacter
4245
bool ImportantRagdoll( void ) { return m_bImportanRagdoll; }
4346

4447
#ifdef MAPBASE_MP
48+
virtual int GetHealth() const { return m_iHealth; }
49+
4550
virtual const char *GetPlayerName( void ) const;
51+
virtual void DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator = NULL );
52+
void DecalTrace( trace_t *pTrace, char const *decalName );
53+
void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
54+
virtual bool IsPlayerAlly( C_BasePlayer *pPlayer = NULL );
55+
virtual bool IsNeutralTo( C_BasePlayer *pPlayer = NULL );
4656
#endif
4757

4858
private:
@@ -59,6 +69,19 @@ class C_AI_BaseNPC : public C_BaseCombatCharacter
5969
bool m_bFadeCorpse;
6070
bool m_bSpeedModActive;
6171
bool m_bImportanRagdoll;
72+
73+
#ifdef MAPBASE_MP
74+
// User-friendly name used for death notices, etc.
75+
// Now transmitted by player resource
76+
//char m_szNetname[32];
77+
78+
// Used to determine whether to draw blood, target ID, etc. on the client
79+
// Uses first 3 gamerules relationship return codes (GR_TEAMMATE, GR_NOTTEAMMATE, and GR_ENEMY)
80+
int m_nDefaultPlayerRelationship;
81+
82+
// Based on the existing decal hack from singleplayer HL2
83+
bool m_fNoDamageDecal;
84+
#endif
6285
};
6386

6487

src/game/server/ai_basenpc.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4641,6 +4641,23 @@ void CAI_BaseNPC::NPCThink( void )
46414641
#ifdef MAPBASE_MP
46424642
if (bInPVS)
46434643
{
4644+
// Recalculate player relationship for client
4645+
CBasePlayer *pPlayer = UTIL_GetNearestPlayer( GetAbsOrigin() );
4646+
if (pPlayer)
4647+
{
4648+
Disposition_t nDisposition = pPlayer->IRelationType( this );
4649+
switch (nDisposition)
4650+
{
4651+
case D_LI: m_nDefaultPlayerRelationship = GR_TEAMMATE; break;
4652+
case D_FR:
4653+
case D_HT: m_nDefaultPlayerRelationship = GR_ENEMY; break;
4654+
default:
4655+
case D_NU: m_nDefaultPlayerRelationship = GR_NOTTEAMMATE; break;
4656+
}
4657+
}
4658+
else
4659+
m_nDefaultPlayerRelationship = GR_NOTTEAMMATE; // Neutral
4660+
46444661
#ifdef HL2MP
46454662
// Make sure the player resource has us listed if we just entered PVS
46464663
if (g_HL2MP_PR->GetNPCIndex( entindex() ) == -1)
@@ -12857,6 +12874,33 @@ BEGIN_SIMPLE_DATADESC( AIScheduleState_t )
1285712874
END_DATADESC()
1285812875

1285912876

12877+
#ifdef MAPBASE_MP
12878+
void *SendProxy_SendBaseNPCGameDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
12879+
{
12880+
// Only send if we can reasonably expect to be a "participant" in the game
12881+
// Invisible NPCs are already EF_NODRAW, so this mainly denies generic_actor, npc_furniture, etc.
12882+
CAI_BaseNPC *pNPC = ( CAI_BaseNPC * )pStruct;
12883+
if ( pNPC != NULL )
12884+
{
12885+
Class_T nClass = pNPC->Classify();
12886+
if (nClass == CLASS_NONE)
12887+
{
12888+
return NULL;
12889+
}
12890+
}
12891+
return (void *)pVarData;
12892+
}
12893+
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendBaseNPCGameDataTable );
12894+
12895+
BEGIN_SEND_TABLE_NOBASE( CAI_BaseNPC, DT_BaseNPCGameData )
12896+
SendPropInt( SENDINFO( m_iHealth ), -1, SPROP_VARINT | SPROP_CHANGES_OFTEN ),
12897+
SendPropInt( SENDINFO( m_takedamage ), 2, SPROP_UNSIGNED ),
12898+
SendPropInt( SENDINFO( m_bloodColor ), 3, SPROP_UNSIGNED ),
12899+
//SendPropString( SENDINFO( m_szNetname ) ), // Transmitted by player resource now
12900+
SendPropInt( SENDINFO( m_nDefaultPlayerRelationship ), 2, SPROP_UNSIGNED ),
12901+
END_SEND_TABLE();
12902+
#endif
12903+
1286012904
IMPLEMENT_SERVERCLASS_ST( CAI_BaseNPC, DT_AI_BaseNPC )
1286112905
SendPropInt( SENDINFO( m_lifeState ), 3, SPROP_UNSIGNED ),
1286212906
SendPropBool( SENDINFO( m_bPerformAvoidance ) ),
@@ -12869,6 +12913,9 @@ IMPLEMENT_SERVERCLASS_ST( CAI_BaseNPC, DT_AI_BaseNPC )
1286912913
SendPropInt( SENDINFO( m_iSpeedModSpeed ) ),
1287012914
SendPropBool( SENDINFO( m_bImportanRagdoll ) ),
1287112915
SendPropFloat( SENDINFO( m_flTimePingEffect ) ),
12916+
#ifdef MAPBASE_MP
12917+
SendPropDataTable( "npc_gamedata", 0, &REFERENCE_SEND_TABLE( DT_BaseNPCGameData ), SendProxy_SendBaseNPCGameDataTable ),
12918+
#endif
1287212919
END_SEND_TABLE()
1287312920

1287412921
//-------------------------------------

src/game/server/ai_basenpc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,14 @@ class CAI_BaseNPC : public CBaseCombatCharacter,
21892189

21902190
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_lifeState );
21912191
#ifdef MAPBASE_MP
2192+
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_iHealth );
2193+
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_takedamage );
2194+
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_bloodColor );
2195+
2196+
// Used to determine whether to draw blood, target ID, etc. on the client
2197+
// Uses first 3 gamerules relationship return codes (GR_TEAMMATE, GR_NOTTEAMMATE, and GR_ENEMY)
2198+
CNetworkVar( int, m_nDefaultPlayerRelationship );
2199+
21922200
// User-friendly name used for death notices, etc.
21932201
// Now transmitted by player resource
21942202
//CNetworkString( m_szNetname, 32 );

src/game/server/basecombatcharacter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,11 @@ class CBaseCombatCharacter : public CBaseFlex
595595
void DestroyGlowEffect( void );
596596

597597
protected:
598+
#ifdef MAPBASE_MP
599+
CNetworkVarForDerived( int, m_bloodColor );
600+
#else
598601
int m_bloodColor; // color of blood particless
602+
#endif
599603

600604
// -------------------
601605
// combat ability data

src/game/server/hl2/npc_turret_floor.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,10 @@ void CNPC_FloorTurret::Spawn( void )
370370
CreateVPhysics();
371371

372372
SetState(NPC_STATE_IDLE);
373+
374+
#ifdef MAPBASE_MP
375+
SetBloodColor( DONT_BLEED );
376+
#endif
373377
}
374378

375379
//-----------------------------------------------------------------------------

src/game/server/hl2/npc_turret_ground.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ void CNPC_GroundTurret::Spawn( void )
150150

151151
GetAttachment( "light", vecPos );
152152
m_vecLightOffset = vecPos - GetAbsOrigin();
153+
154+
#ifdef MAPBASE_MP
155+
SetBloodColor( DONT_BLEED );
156+
#endif
153157
}
154158

155159
//-----------------------------------------------------------------------------

src/game/shared/baseentity_shared.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICAT
8585

8686
#if defined(MAPBASE) && defined(GAME_DLL)
8787
ConVar ai_shot_notify_targets( "ai_shot_notify_targets", "0", FCVAR_NONE, "Allows fired bullets to notify the NPCs and players they are targeting, regardless of whether they hit them or not. Can be used for custom AI and speech." );
88+
89+
#if defined(MAPBASE_MP) && defined(HL2MP)
90+
ConVar mp_use_server_bulletfx( "mp_use_server_bulletfx", "2", FCVAR_NONE, "Whether or not to use serverside bullet FX in multiplayer.\n"
91+
"The default for HL2:DM is 0, but this causes issues with entities that don't transfer their damage filters or blood color to the client, such as NPCs.\n"
92+
"While 1 universally re-enables serverside bullet FX, 2 only uses serverside bullet FX for non-players." );
93+
#endif
8894
#endif
8995

9096
// Utility func to throttle rate at which the "reasonable position" spew goes out
@@ -1643,8 +1649,24 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
16431649
bool bDoServerEffects = true;
16441650

16451651
#if defined( HL2MP ) && defined( GAME_DLL )
1652+
#ifdef MAPBASE_MP
1653+
switch (mp_use_server_bulletfx.GetInt())
1654+
{
1655+
case 0:
1656+
default:
1657+
bDoServerEffects = false;
1658+
break;
1659+
case 1:
1660+
bDoServerEffects = true;
1661+
break;
1662+
case 2:
1663+
bDoServerEffects = !IsPlayer();
1664+
break;
1665+
}
1666+
#else
16461667
bDoServerEffects = false;
16471668
#endif
1669+
#endif
16481670

16491671
#if defined( GAME_DLL )
16501672
if( IsPlayer() )

0 commit comments

Comments
 (0)