diff --git a/build/msvc_2019/game.vcxproj b/build/msvc_2019/game.vcxproj index ca35ae6..0fc4702 100644 --- a/build/msvc_2019/game.vcxproj +++ b/build/msvc_2019/game.vcxproj @@ -255,6 +255,7 @@ + diff --git a/build/msvc_2019/game.vcxproj.filters b/build/msvc_2019/game.vcxproj.filters index 0fa8cda..3251ada 100644 --- a/build/msvc_2019/game.vcxproj.filters +++ b/build/msvc_2019/game.vcxproj.filters @@ -245,6 +245,9 @@ game\mods\features + + game\mods\features + game\mods\features\pingcomp diff --git a/build/qvm_build/build_game.bat b/build/qvm_build/build_game.bat index 60b719c..af46d52 100644 --- a/build/qvm_build/build_game.bat +++ b/build/qvm_build/build_game.bat @@ -65,6 +65,7 @@ set src=game\mods&set name=g_mod_stubs&call :compile set src=game\mods&set name=g_mod_utils&call :compile set src=game\mods\features&set name=feature_altswap_handler&call :compile set src=game\mods\features&set name=feature_player_move&call :compile +set src=game\mods\features&set name=feature_spect_passthrough&call :compile set src=game\mods\features\pingcomp&set name=pc_client_predict&call :compile set src=game\mods\features\pingcomp&set name=pc_dead_move&call :compile set src=game\mods\features\pingcomp&set name=pc_instant_weapons&call :compile diff --git a/code/game/g_mover.c b/code/game/g_mover.c index 9e13810..b873c40 100644 --- a/code/game/g_mover.c +++ b/code/game/g_mover.c @@ -755,6 +755,10 @@ static void Touch_DoorTriggerSpectator( gentity_t *ent, gentity_t *other, trace_ int i, axis; vec3_t origin, dir, angles; + if ( modfn.AdjustGeneralConstant( GC_SKIP_SPECTATOR_DOOR_TELEPORT, 0 ) ) { + return; + } + axis = ent->count; VectorClear(dir); if (fabs(other->s.origin[axis] - ent->r.absmax[axis]) < diff --git a/code/game/mods/features/feature_spect_passthrough.c b/code/game/mods/features/feature_spect_passthrough.c new file mode 100644 index 0000000..0e5f87c --- /dev/null +++ b/code/game/mods/features/feature_spect_passthrough.c @@ -0,0 +1,97 @@ +/* +* Spectator Pass-Through +* +* This module changes free-roaming spectator behavior to allow clipping through all dynamic +* entities. It replaces the standard method of auto-teleporting through doors, which is very +* unreliable and prone to getting stuck. +*/ + +#include "mods/g_mod_local.h" + +#define PREFIX( x ) ModSpectPassThrough_##x +#define MOD_STATE PREFIX( state ) + +static struct { + void ( *Prev_Trace )( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, + const vec3_t end, int passEntityNum, int contentMask ); + + // For mod function stacking + ModFNType_PmoveInit Prev_PmoveInit; + ModFNType_AdjustGeneralConstant Prev_AdjustGeneralConstant; +} *MOD_STATE; + +/* +================ +ModSpectPassThrough_Trace + +Trace with entities ignored so everything except the main map is pass-through. +================ +*/ +static void ModSpectPassThrough_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, + const vec3_t end, int passEntityNum, int contentMask ) { + int entityNum; + MOD_STATE->Prev_Trace( results, start, mins, maxs, end, passEntityNum, contentMask ); + entityNum = results->entityNum; + + if ( entityNum >= 0 && entityNum < ENTITYNUM_MAX_NORMAL && EF_WARN_ASSERT( g_entities[entityNum].r.contents ) ) { + // Repeat trace with contacted entity temporarily set to empty contents. + int oldContents = g_entities[entityNum].r.contents; + g_entities[entityNum].r.contents = 0; + ModSpectPassThrough_Trace( results, start, mins, maxs, end, passEntityNum, contentMask ); + g_entities[entityNum].r.contents = oldContents; + } +} + +/* +================ +(ModFN) PmoveInit + +Override trace function for spectators. +================ +*/ +LOGFUNCTION_SVOID( PREFIX(PmoveInit), ( int clientNum, pmove_t *pmove ), + ( clientNum, pmove ), "G_MODFN_PMOVEINIT" ) { + const gclient_t *client = &level.clients[clientNum]; + + MOD_STATE->Prev_PmoveInit( clientNum, pmove ); + + if ( client->sess.sessionTeam == TEAM_SPECTATOR || ( client->ps.eFlags & EF_ELIMINATED ) ) { + MOD_STATE->Prev_Trace = pmove->trace; + pmove->trace = ModSpectPassThrough_Trace; + } +} + +/* +================ +(ModFN) AdjustGeneralConstant +================ +*/ +int PREFIX(AdjustGeneralConstant)( generalConstant_t gcType, int defaultValue ) { + if ( gcType == GC_SKIP_SPECTATOR_DOOR_TELEPORT ) { + return 1; + } + + return MOD_STATE->Prev_AdjustGeneralConstant( gcType, defaultValue ); +} + +/* +================ +ModSpectPassThrough_Init +================ +*/ + +#define INIT_FN_STACKABLE( name ) \ + MOD_STATE->Prev_##name = modfn.name; \ + modfn.name = PREFIX(name); + +#define INIT_FN_OVERRIDE( name ) \ + modfn.name = PREFIX(name); + +LOGFUNCTION_VOID( ModSpectPassThrough_Init, ( void ), (), "G_MOD_INIT" ) { + if ( !MOD_STATE ) { + MOD_STATE = G_Alloc( sizeof( *MOD_STATE ) ); + + INIT_FN_STACKABLE( PmoveInit ); + INIT_FN_STACKABLE( AdjustGeneralConstant ); + } +} diff --git a/code/game/mods/g_mod_local.h b/code/game/mods/g_mod_local.h index d8be7f0..d931ac6 100644 --- a/code/game/mods/g_mod_local.h +++ b/code/game/mods/g_mod_local.h @@ -18,6 +18,7 @@ int G_ModUtils_GetLatchedValue( const char *cvar_name, const char *default_value void ModAltSwapHandler_Init( void ); void ModPingcomp_Init( void ); void ModPlayerMove_Init( void ); +void ModSpectPassThrough_Init( void ); // // Ping Compensation (pc_main.c) diff --git a/code/game/mods/g_mod_main.c b/code/game/mods/g_mod_main.c index 9a88787..44cc202 100644 --- a/code/game/mods/g_mod_main.c +++ b/code/game/mods/g_mod_main.c @@ -32,5 +32,6 @@ LOGFUNCTION_VOID( G_ModsInit, ( void ), (), "G_MOD_INIT" ) { ModPlayerMove_Init(); ModAltSwapHandler_Init(); ModPingcomp_Init(); + ModSpectPassThrough_Init(); } } diff --git a/code/game/mods/g_mod_public.h b/code/game/mods/g_mod_public.h index 4586ffe..e24b02f 100644 --- a/code/game/mods/g_mod_public.h +++ b/code/game/mods/g_mod_public.h @@ -11,6 +11,7 @@ typedef enum { GC_NONE, GC_SKIP_RUN_MISSILE, GC_EVENT_TIME_OFFSET, + GC_SKIP_SPECTATOR_DOOR_TELEPORT, } generalConstant_t; typedef enum {