Merge pull request #377 from Blixibon/mapbase/feature/protagonist-system

HL2 player character switching system
This commit is contained in:
Blixibon 2025-02-28 21:16:01 -06:00 committed by GitHub
commit 51afe9316e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1226 additions and 3 deletions

View File

@ -49,6 +49,8 @@ $Project
$File "$SRCDIR\game\shared\mapbase\matchers.h"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.cpp"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]

View File

@ -31,6 +31,9 @@ ConVar cl_npc_speedmod_outtime( "cl_npc_speedmod_outtime", "1.5", FCVAR_CLIENTDL
IMPLEMENT_CLIENTCLASS_DT(C_BaseHLPlayer, DT_HL2_Player, CHL2_Player)
RecvPropDataTable( RECVINFO_DT(m_HL2Local),0, &REFERENCE_RECV_TABLE(DT_HL2Local) ),
RecvPropBool( RECVINFO( m_fIsSprinting ) ),
#ifdef MAPBASE
RecvPropInt( RECVINFO( m_nProtagonistIndex ) ),
#endif
#ifdef SP_ANIM_STATE
RecvPropFloat( RECVINFO( m_flAnimRenderYaw ) ),
RecvPropFloat( RECVINFO( m_flAnimRenderZ ) ),
@ -79,6 +82,8 @@ C_BaseHLPlayer::C_BaseHLPlayer()
#ifdef MAPBASE
ConVarRef scissor("r_flashlightscissor");
scissor.SetValue("0");
m_nProtagonistIndex = -1;
#endif
}

View File

@ -63,6 +63,10 @@ public:
bool IsWeaponLowered( void ) { return m_HL2Local.m_bWeaponLowered; }
#ifdef MAPBASE
int GetProtagonistIndex() const { return m_nProtagonistIndex; }
#endif
#ifdef SP_ANIM_STATE
virtual const Vector& GetRenderOrigin();
virtual const QAngle& GetRenderAngles( void );
@ -89,6 +93,10 @@ private:
bool m_bPlayUseDenySound; // Signaled by PlayerUse, but can be unset by HL2 ladder code...
float m_flSpeedMod;
float m_flExitSpeedMod;
#ifdef MAPBASE
int m_nProtagonistIndex;
#endif
#ifdef MAPBASE_MP
CSinglePlayerAnimState *m_pPlayerAnimState;

View File

@ -58,6 +58,7 @@
#ifdef MAPBASE
#include "triggers.h"
#include "mapbase/variant_tools.h"
#include "mapbase/protagonist_system.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -593,6 +594,8 @@ BEGIN_DATADESC( CHL2_Player )
DEFINE_INPUTFUNC( FIELD_VOID, "DisableGeigerCounter", InputDisableGeigerCounter ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowSquadHUD", InputShowSquadHUD ),
DEFINE_INPUTFUNC( FIELD_VOID, "HideSquadHUD", InputHideSquadHUD ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetProtagonist", InputSetProtagonist ),
#endif
DEFINE_SOUNDPATCH( m_sndLeeches ),
@ -607,6 +610,10 @@ BEGIN_DATADESC( CHL2_Player )
DEFINE_FIELD( m_flTimeNextLadderHint, FIELD_TIME ),
#ifdef MAPBASE
DEFINE_KEYFIELD( m_iszProtagonistName, FIELD_STRING, "ProtagonistName" ),
#endif
//DEFINE_FIELD( m_hPlayerProxy, FIELD_EHANDLE ), //Shut up class check!
END_DATADESC()
@ -625,6 +632,9 @@ BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." )
DEFINE_SCRIPTFUNC( RemoveCustomSuitDevice, "Removes a custom suit device ID. (1-3)" )
DEFINE_SCRIPTFUNC( IsCustomSuitDeviceActive, "Checks if a custom suit device is active." )
DEFINE_SCRIPTFUNC( GetProtagonistName, "Gets the player's protagonist name." )
DEFINE_SCRIPTFUNC( SetProtagonist, "Sets the player's protagonist entry." )
#ifdef SP_ANIM_STATE
DEFINE_SCRIPTFUNC( AddAnimStateLayer, "Adds a custom sequence index as a misc. layer for the singleplayer anim state, wtih parameters for blending in/out, setting the playback rate, holding the animation at the end, and only playing when the player is still." )
#endif
@ -640,6 +650,10 @@ CHL2_Player::CHL2_Player()
m_flArmorReductionTime = 0.0f;
m_iArmorReductionFrom = 0;
#ifdef MAPBASE
m_nProtagonistIndex = -1;
#endif
}
//
@ -674,6 +688,9 @@ CSuitPowerDevice SuitDeviceCustom[] =
IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player)
SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable),
SendPropBool( SENDINFO(m_fIsSprinting) ),
#ifdef MAPBASE
SendPropInt( SENDINFO( m_nProtagonistIndex ), 8, SPROP_UNSIGNED ),
#endif
#ifdef SP_ANIM_STATE
SendPropFloat( SENDINFO(m_flAnimRenderYaw), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flAnimRenderZ), 0, SPROP_NOSCALE ),
@ -1259,6 +1276,11 @@ void CHL2_Player::Activate( void )
#endif
GetPlayerProxy();
#ifdef MAPBASE
if (m_iszProtagonistName != NULL_STRING)
SetProtagonist( STRING( m_iszProtagonistName ) );
#endif
}
//------------------------------------------------------------------------------
@ -1500,6 +1522,8 @@ CStudioHdr *CHL2_Player::OnNewModel()
extern char g_szDefaultPlayerModel[MAX_PATH];
extern bool g_bDefaultPlayerLegs;
extern bool g_bDefaultPlayerDrawExternally;
extern char g_szDefaultProtagonist[MAX_PROTAGONIST_NAME];
#endif
//-----------------------------------------------------------------------------
@ -1533,6 +1557,9 @@ void CHL2_Player::Spawn(void)
SetDrawPlayerLegs( g_bDefaultPlayerLegs );
SetDrawPlayerModelExternally( g_bDefaultPlayerDrawExternally );
if (m_iszProtagonistName == NULL_STRING && *g_szDefaultProtagonist)
m_iszProtagonistName = MAKE_STRING( g_szDefaultProtagonist );
#endif
//
@ -4087,6 +4114,10 @@ bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex
StopZooming();
}
#ifdef MAPBASE
RefreshProtagonistWeaponData( pWeapon );
#endif
return BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
}
@ -4482,6 +4513,144 @@ bool CHL2_Player::IsCustomSuitDeviceActive( int iDeviceID )
return SuitPower_IsDeviceActive( SuitDeviceCustom[iDeviceID] );
}
//-----------------------------------------------------------------------------
// Purpose: Gets our protagonist name, if we have one
//-----------------------------------------------------------------------------
const char *CHL2_Player::GetProtagonistName() const
{
return STRING( m_iszProtagonistName );
}
//-----------------------------------------------------------------------------
// Purpose: Gets our protagonist index, if we have one
//-----------------------------------------------------------------------------
int CHL2_Player::GetProtagonistIndex() const
{
return m_nProtagonistIndex;
}
//-----------------------------------------------------------------------------
// Purpose: Sets our protagonist to the specified entry
//-----------------------------------------------------------------------------
void CHL2_Player::InputSetProtagonist( inputdata_t &inputdata )
{
SetProtagonist( inputdata.value.String() );
}
//-----------------------------------------------------------------------------
// Purpose: Sets our protagonist to the specified entry
//-----------------------------------------------------------------------------
void CHL2_Player::SetProtagonist( const char *pszProtagonist )
{
if (!pszProtagonist || !*pszProtagonist)
{
ResetProtagonist();
return;
}
int nIndex = g_ProtagonistSystem.FindProtagonistIndex( pszProtagonist );
if (nIndex == -1)
{
Warning( "\"%s\" is not a valid protagonist\n", pszProtagonist );
return;
}
if (m_nProtagonistIndex != -1)
{
// Flush any pre-existing data
ResetProtagonist();
}
m_nProtagonistIndex = nIndex;
m_iszProtagonistName = AllocPooledString( pszProtagonist );
RefreshProtagonistData();
}
//-----------------------------------------------------------------------------
// Purpose: Resets protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::ResetProtagonist()
{
SetModel( g_szDefaultPlayerModel );
m_nSkin = 0;
m_nBody = 0;
CBaseViewModel *vm = GetViewModel( 1 );
if (vm)
{
extern char g_szDefaultHandsModel[MAX_PATH];
vm->SetWeaponModel( g_szDefaultHandsModel, NULL );
vm->m_nSkin = 0;
vm->m_nBody = 0;
}
// RemoveContext will automatically remove contexts by name, regardless of how values are specified
char szContexts[128] = { 0 };
g_ProtagonistSystem.GetProtagonist_ResponseContexts( this, szContexts, sizeof( szContexts ) );
if (szContexts[0])
RemoveContext( szContexts );
m_iszProtagonistName = NULL_STRING;
m_nProtagonistIndex = -1;
}
//-----------------------------------------------------------------------------
// Purpose: Refreshes protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::RefreshProtagonistData()
{
if (m_nProtagonistIndex == -1)
return;
g_ProtagonistSystem.PrecacheProtagonist( this, m_nProtagonistIndex );
const char *pszProtagModel = g_ProtagonistSystem.GetProtagonist_PlayerModel( this );
if (pszProtagModel)
SetModel( pszProtagModel );
m_nSkin = g_ProtagonistSystem.GetProtagonist_PlayerModelSkin( this );
m_nBody = g_ProtagonistSystem.GetProtagonist_PlayerModelBody( this );
char szContexts[128] = { 0 };
g_ProtagonistSystem.GetProtagonist_ResponseContexts( this, szContexts, sizeof( szContexts ) );
if (szContexts[0])
AddContext( szContexts );
RefreshProtagonistWeaponData( GetActiveWeapon() );
}
//-----------------------------------------------------------------------------
// Purpose: Refreshes protagonist data
//-----------------------------------------------------------------------------
void CHL2_Player::RefreshProtagonistWeaponData( CBaseCombatWeapon *pWeapon )
{
if (m_nProtagonistIndex == -1)
return;
CBaseViewModel *vm = GetViewModel( 1 );
if (vm)
{
const char *pszHandModel = g_ProtagonistSystem.GetProtagonist_HandModel( this, pWeapon );
if (pszHandModel)
{
vm->SetWeaponModel( pszHandModel, NULL );
vm->m_nSkin = g_ProtagonistSystem.GetProtagonist_HandModelSkin( this, pWeapon );
vm->m_nBody = g_ProtagonistSystem.GetProtagonist_HandModelBody( this, pWeapon );
}
else
{
extern char g_szDefaultHandsModel[MAX_PATH];
vm->SetWeaponModel( g_szDefaultHandsModel, NULL );
vm->m_nSkin = 0;
vm->m_nBody = 0;
}
}
}
#endif
//-----------------------------------------------------------------------------

View File

@ -358,6 +358,15 @@ public:
void AddCustomSuitDevice( int iDeviceID );
void RemoveCustomSuitDevice( int iDeviceID );
bool IsCustomSuitDeviceActive( int iDeviceID );
// Protagonist system
const char *GetProtagonistName() const;
int GetProtagonistIndex() const;
void InputSetProtagonist( inputdata_t &inputdata );
void SetProtagonist( const char *pszProtagonist );
void ResetProtagonist();
void RefreshProtagonistData();
void RefreshProtagonistWeaponData( CBaseCombatWeapon *pWeapon );
#endif
CSoundPatch *m_sndLeeches;
@ -442,6 +451,12 @@ private:
friend class CHL2GameMovement;
#ifdef MAPBASE
// Protagonist used by protagonist_system.h
string_t m_iszProtagonistName;
CNetworkVar( int, m_nProtagonistIndex );
#endif
#ifdef SP_ANIM_STATE
CMapbasePlayerAnimState* m_pPlayerAnimState;

View File

@ -4475,6 +4475,11 @@ void CBasePlayer::SetSuitUpdate(const char *name, int fgroup, int iNoRepeatTime)
return;
}
#ifdef MAPBASE
if ( HasContext("silent_suit", "1") )
return;
#endif
// if name == NULL, then clear out the queue
if (!name)

View File

@ -48,6 +48,8 @@ $Project
$File "$SRCDIR\game\shared\mapbase\matchers.h"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.cpp"
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]

View File

@ -507,6 +507,11 @@ bool CBaseCombatWeapon::UsesHands() const
{
return GetWpnData().m_bUsesHands;
}
int CBaseCombatWeapon::GetHandRig() const
{
return GetWpnData().m_nHandRig;
}
#endif
//-----------------------------------------------------------------------------
@ -3127,6 +3132,9 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all
DEFINE_SCRIPTFUNC( GetViewModel, "Get the weapon's view model." )
DEFINE_SCRIPTFUNC( GetDroppedModel, "Get the weapon's unique dropped model if it has one." )
DEFINE_SCRIPTFUNC( UsesHands, "" )
DEFINE_SCRIPTFUNC( GetHandRig, "" )
DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." )
DEFINE_SCRIPTFUNC( GetPrintName, "" )

View File

@ -423,12 +423,13 @@ public:
virtual bool UsesClipsForAmmo2( void ) const;
bool IsMeleeWeapon() const;
#ifdef MAPBASE
float GetViewmodelFOVOverride() const;
virtual float GetViewmodelFOVOverride() const;
float GetBobScale() const;
float GetSwayScale() const;
float GetSwaySpeedScale() const;
virtual const char *GetDroppedModel( void ) const;
bool UsesHands( void ) const;
virtual bool UsesHands( void ) const;
virtual int GetHandRig( void ) const;
#endif
// derive this function if you mod uses encrypted weapon info files

View File

@ -6,6 +6,9 @@
#include "cbase.h"
#include "basehlcombatweapon_shared.h"
#ifdef MAPBASE
#include "mapbase/protagonist_system.h"
#endif
#include "hl2_player_shared.h"
@ -436,4 +439,66 @@ const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetDefaultProficiencyValues(
return g_BaseWeaponProficiencyTable;
}
#endif
#endif
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CBaseHLCombatWeapon::GetViewModel( int viewmodelindex ) const
{
if (GetOwner() && GetOwner()->IsPlayer() && viewmodelindex == 0)
{
const char *pszProtagVM = g_ProtagonistSystem.GetProtagonist_ViewModel( static_cast<CBasePlayer *>(GetOwner()), this );
if (pszProtagVM)
return pszProtagVM;
}
return BaseClass::GetViewModel( viewmodelindex );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CBaseHLCombatWeapon::GetViewmodelFOVOverride() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
float *flVMFOV = g_ProtagonistSystem.GetProtagonist_ViewModelFOV( static_cast<CBasePlayer *>(GetOwner()), this );
if (flVMFOV)
return *flVMFOV;
}
return BaseClass::GetViewmodelFOVOverride();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseHLCombatWeapon::UsesHands() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
bool *bProtagUsesHands = g_ProtagonistSystem.GetProtagonist_UsesHands( static_cast<CBasePlayer *>(GetOwner()), this );
if (bProtagUsesHands)
return *bProtagUsesHands;
}
return BaseClass::UsesHands();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseHLCombatWeapon::GetHandRig() const
{
if (GetOwner() && GetOwner()->IsPlayer())
{
int *nProtagHandRig = g_ProtagonistSystem.GetProtagonist_HandRig( static_cast<CBasePlayer *>(GetOwner()), this );
if (nProtagHandRig)
return *nProtagHandRig;
}
return BaseClass::GetHandRig();
}
#endif

View File

@ -53,6 +53,13 @@ public:
virtual void ItemHolsterFrame( void );
#ifdef MAPBASE
virtual const char *GetViewModel( int viewmodelindex = 0 ) const;
virtual float GetViewmodelFOVOverride() const;
virtual bool UsesHands( void ) const;
virtual int GetHandRig( void ) const;
#endif
int m_iPrimaryAttacks; // # of primary attacks performed with this weapon
int m_iSecondaryAttacks; // # of secondary attacks performed with this weapon

View File

@ -31,6 +31,9 @@
#include "gameinterface.h"
#include "mapbase/choreosentence.h"
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
#include "protagonist_system.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -92,6 +95,11 @@ bool g_bDefaultPlayerDrawExternally;
char g_szDefaultHandsModel[MAX_PATH];
int g_iDefaultHandsSkin;
int g_iDefaultHandsBody;
#ifdef HL2_DLL
// See protagonist_system.h
char g_szDefaultProtagonist[MAX_PROTAGONIST_NAME];
#endif
#endif
enum
@ -115,6 +123,9 @@ enum
#ifdef MAPBASE_VSCRIPT
MANIFEST_VSCRIPT,
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
MANIFEST_PROTAGONISTS, // See protagonist_system.h
#endif
// Must always be kept below
MANIFEST_NUM_TYPES,
@ -155,6 +166,9 @@ static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = {
#ifdef MAPBASE_VSCRIPT
{ "vscript", "mapbase_load_vscript", "Should we load map-specific VScript map spawn files? e.g. \"maps/<mapname>_mapspawn.nut\"" },
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
{ "protagonists", "mapbase_load_protagonists", "Should we load map-specific protagonist files? e.g. \"maps/<mapname>_protagonists.txt\"" },
#endif
};
//-----------------------------------------------------------------------------
@ -243,6 +257,10 @@ public:
Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", "models/weapons/v_hands.mdl" ), sizeof( g_szDefaultHandsModel ) );
g_iDefaultHandsSkin = gameinfo->GetInt( "player_default_hands_skin", 0 );
g_iDefaultHandsBody = gameinfo->GetInt( "player_default_hands_body", 0 );
#ifdef HL2_DLL
Q_strncpy( g_szDefaultProtagonist, gameinfo->GetString( "player_default_protagonist", "" ), sizeof( g_szDefaultProtagonist ) );
#endif
#endif
}
gameinfo->deleteThis();
@ -473,6 +491,9 @@ public:
#endif
#ifdef MAPBASE_VSCRIPT
case MANIFEST_VSCRIPT: { VScriptRunScript(value, false); } break;
#endif
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
case MANIFEST_PROTAGONISTS: { g_ProtagonistSystem.LoadProtagonistFile( value ); } break;
#endif
}
}

View File

@ -0,0 +1,732 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: System to easily switch between player characters.
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "protagonist_system.h"
#include "weapon_parse.h"
#include "filesystem.h"
#include "hl2_player_shared.h"
#ifdef HL2MP
#include "hl2mp_gamerules.h"
#endif
CProtagonistSystem g_ProtagonistSystem;
//=============================================================================
//=============================================================================
bool CProtagonistSystem::Init()
{
return true;
}
void CProtagonistSystem::Shutdown()
{
PurgeProtagonists();
}
void CProtagonistSystem::LevelInitPreEntity()
{
LoadProtagonistManifest( "scripts/protagonists/protagonists_manifest.txt" );
}
void CProtagonistSystem::LevelShutdownPostEntity()
{
PurgeProtagonists();
}
//----------------------------------------------------------------------------
void CProtagonistSystem::LoadProtagonistManifest( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "ProtagonistsManifest" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
FOR_EACH_SUBKEY( pManifest, pSubKey )
{
LoadProtagonistFile( pSubKey->GetString() );
}
}
pManifest->deleteThis();
}
void CProtagonistSystem::LoadProtagonistFile( const char *pszFile )
{
KeyValues *pManifest = new KeyValues( "Protagonists" );
if (pManifest->LoadFromFile( filesystem, pszFile ))
{
FOR_EACH_SUBKEY( pManifest, pProtagKey )
{
if (!pProtagKey->GetName()[0])
continue;
ProtagonistData_t *pProtag = FindProtagonist( pProtagKey->GetName() );
if (!pProtag)
{
pProtag = &m_Protagonists[m_Protagonists.AddToTail()];
V_strncpy( pProtag->szName, pProtagKey->GetName(), sizeof( pProtag->szName ) );
}
FOR_EACH_SUBKEY( pProtagKey, pSubKey )
{
const char *pszSubKeyName = pSubKey->GetName();
//----------------------------------------------------------------------------
// Metadata
//----------------------------------------------------------------------------
if (FStrEq( pszSubKeyName, "inherits_from" ))
{
char szParents[128];
V_strncpy( szParents, pSubKey->GetString(), sizeof( szParents ) );
char *pszToken = strtok( szParents, ";" );
for (; pszToken != NULL; pszToken = strtok( NULL, ";" ))
{
if (!pszToken || !*pszToken)
continue;
int nParent = FindProtagonistIndex( pszToken );
if (nParent >= 0)
{
pProtag->vecParents.FindAndRemove( nParent ); // If it already exists, it will be moved to the front
pProtag->vecParents.AddToTail( nParent );
}
}
}
#ifdef CLIENT_DLL
#else
//----------------------------------------------------------------------------
// Playermodel
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "playermodel", 11 ) == 0)
{
pszSubKeyName += 11;
if (!pszSubKeyName[0])
{
// Model
pProtag->pszPlayerModel = AllocateString( pSubKey->GetString() );
}
else if (FStrEq( pszSubKeyName, "_skin" ))
{
// Skin
pProtag->nPlayerSkin = pSubKey->GetInt();
}
else if (FStrEq( pszSubKeyName, "_body" ))
{
// Bodygroup
pProtag->nPlayerBody = pSubKey->GetInt();
}
}
//----------------------------------------------------------------------------
// Hands
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "hands", 5 ) == 0)
{
pszSubKeyName += 5;
if (!pszSubKeyName[0])
{
// Model
pProtag->pszHandModels[HANDRIG_DEFAULT] = AllocateString( pSubKey->GetString() );
}
else if (FStrEq( pszSubKeyName, "_skin" ))
{
// Skin
pProtag->nHandSkin = pSubKey->GetInt();
}
else if (FStrEq( pszSubKeyName, "_body" ))
{
// Bodygroup
pProtag->nHandBody = pSubKey->GetInt();
}
else
{
// Other Rigs
pszSubKeyName += 1;
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (V_stricmp( pszSubKeyName, pHandRigs[i] ) == 0)
{
pProtag->pszHandModels[i] = AllocateString( pSubKey->GetString() );
break;
}
}
}
}
//----------------------------------------------------------------------------
// Responses
//----------------------------------------------------------------------------
else if (FStrEq( pszSubKeyName, "response_contexts" ))
{
pProtag->pszResponseContexts = AllocateString( pSubKey->GetString() );
}
//----------------------------------------------------------------------------
// Multiplayer
//----------------------------------------------------------------------------
else if (FStrEq( pszSubKeyName, "team" ))
{
#ifdef HL2MP
if (FStrEq( pszSubKeyName, "combine" ))
{
pProtag->nTeam = TEAM_COMBINE;
}
else if (FStrEq( pszSubKeyName, "rebels" ))
{
pProtag->nTeam = TEAM_REBELS;
}
else
#endif
{
// Try to get a direct integer
pProtag->nTeam = atoi( pszSubKeyName );
}
}
#endif
//----------------------------------------------------------------------------
// Weapon Data
//----------------------------------------------------------------------------
else if (V_strnicmp( pszSubKeyName, "wpn_viewmodels", 14 ) == 0)
{
// "wpn_viewmodels_c" = models which support arms
bool bHands = FStrEq( pszSubKeyName + 14, "_c" );
FOR_EACH_SUBKEY( pSubKey, pWeaponKey )
{
int i = pProtag->dictWpnData.Find( pWeaponKey->GetName() );
if (i == pProtag->dictWpnData.InvalidIndex())
i = pProtag->dictWpnData.Insert( pWeaponKey->GetName() );
pProtag->dictWpnData[i].pszVM = AllocateString( pWeaponKey->GetString() );
pProtag->dictWpnData[i].bUsesHands = bHands;
}
}
else if (FStrEq( pszSubKeyName, "wpn_data" )) // More expanded/explicit
{
FOR_EACH_SUBKEY( pSubKey, pWeaponKey )
{
int i = pProtag->dictWpnData.Find( pWeaponKey->GetName() );
if (i == pProtag->dictWpnData.InvalidIndex())
i = pProtag->dictWpnData.Insert( pWeaponKey->GetName() );
const char *pszVM = pWeaponKey->GetString( "viewmodel", NULL );
if (pszVM)
pProtag->dictWpnData[i].pszVM = AllocateString( pszVM );
const char *pszHandRig = pWeaponKey->GetString( "hand_rig", NULL );
if (pszHandRig)
{
// If there's a specific rig, then it must use hands
pProtag->dictWpnData[i].bUsesHands = true;
for (int j = 0; j < NUM_HAND_RIG_TYPES; j++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (V_stricmp( pszHandRig, pHandRigs[j] ) == 0)
{
pProtag->dictWpnData[i].nHandRig = j;
break;
}
}
}
else
{
KeyValues *pUsesHands = pWeaponKey->FindKey( "uses_hands" );
if (pUsesHands)
pProtag->dictWpnData[i].bUsesHands = pUsesHands->GetBool();
}
KeyValues *pVMFOV = pWeaponKey->FindKey( "viewmodel_fov" );
if (pVMFOV)
pProtag->dictWpnData[i].flVMFOV = pVMFOV->GetFloat();
}
}
}
}
}
pManifest->deleteThis();
}
//----------------------------------------------------------------------------
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::GetPlayerProtagonist( const CBasePlayer *pPlayer )
{
#ifdef CLIENT_DLL
const C_BaseHLPlayer *pHL2Player = static_cast<const C_BaseHLPlayer *>(pPlayer);
#else
const CHL2_Player *pHL2Player = static_cast<const CHL2_Player *>(pPlayer);
#endif
if (!pHL2Player)
return NULL;
int i = pHL2Player->GetProtagonistIndex();
if (i >= 0 && i < m_Protagonists.Count())
{
return &g_ProtagonistSystem.m_Protagonists[i];
}
return NULL;
}
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::FindProtagonist( const char *pszName )
{
FOR_EACH_VEC( m_Protagonists, i )
{
if (FStrEq( pszName, m_Protagonists[i].szName ))
{
return &m_Protagonists[i];
}
}
return NULL;
}
CProtagonistSystem::ProtagonistData_t *CProtagonistSystem::FindProtagonist( int nIndex )
{
if (nIndex < 0 || nIndex >= m_Protagonists.Count())
return NULL;
return &m_Protagonists[nIndex];
}
int CProtagonistSystem::FindProtagonistIndex( const char *pszName )
{
FOR_EACH_VEC( m_Protagonists, i )
{
if (FStrEq( pszName, m_Protagonists[i].szName ))
{
return i;
}
}
return -1;
}
const char *CProtagonistSystem::FindProtagonistByModel( const char *pszModelName )
{
#ifndef CLIENT_DLL
FOR_EACH_VEC( m_Protagonists, i )
{
if (m_Protagonists[i].pszPlayerModel && FStrEq( pszModelName, m_Protagonists[i].pszPlayerModel ))
{
return m_Protagonists[i].szName;
}
}
#endif
return NULL;
}
void CProtagonistSystem::PrecacheProtagonist( CBaseEntity *pSource, int nIdx )
{
#ifndef CLIENT_DLL
if (nIdx < 0)
return;
ProtagonistData_t &pProtag = m_Protagonists[nIdx];
// Playermodel
if (pProtag.pszPlayerModel)
{
pSource->PrecacheModel( pProtag.pszPlayerModel );
}
// Hands
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
if (pProtag.pszHandModels[i])
{
pSource->PrecacheModel( pProtag.pszHandModels[i] );
}
}
// Weapon Data
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
if (pProtag.dictWpnData[i].pszVM)
{
pSource->PrecacheModel( pProtag.dictWpnData[i].pszVM );
}
}
#endif
}
//----------------------------------------------------------------------------
#define GetProtagParamInner( name, ... ) DoGetProtagonist_##name( *pProtag, ##__VA_ARGS__ )
#define GetProtagParam( name, type, invoke, ... ) \
type CProtagonistSystem::GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = GetPlayerProtagonist( pPlayer ); \
if (!pProtag) \
return NULL; \
return invoke; \
} \
type CProtagonistSystem::GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = FindProtagonist( nProtagonistIndex ); \
if (!pProtag) \
return NULL; \
return invoke; \
} \
#define GetProtagParamBody( name, type, invoke, body, ... ) \
type CProtagonistSystem::GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = GetPlayerProtagonist( pPlayer ); \
if (!pProtag) \
return NULL; \
type returnVal = invoke; \
body \
return returnVal; \
} \
type CProtagonistSystem::GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ) \
{ \
ProtagonistData_t *pProtag = FindProtagonist( nProtagonistIndex ); \
if (!pProtag) \
return NULL; \
type returnVal = invoke; \
body \
return returnVal; \
} \
#ifdef CLIENT_DLL
#else
GetProtagParam( PlayerModel, const char*, GetProtagParamInner( PlayerModel ) )
GetProtagParam( PlayerModelSkin, int, GetProtagParamInner( PlayerModelSkin ) )
GetProtagParam( PlayerModelBody, int, GetProtagParamInner( PlayerModelBody ) )
GetProtagParam( HandModel, const char*, GetProtagParamInner( HandModel, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandModelSkin, int, GetProtagParamInner( HandModelSkin, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandModelBody, int, GetProtagParamInner( HandModelBody, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParamBody( ResponseContexts, bool, GetProtagParamInner( ResponseContexts, pszContexts, nContextsSize ), {
if (pszContexts[0] != '\0')
{
// Replace trailing comma
int nLast = V_strlen( pszContexts )-1;
if (pszContexts[nLast] == ',')
{
Msg( "Removing trailing comma from \"%s\"\n", pszContexts );
pszContexts[nLast] = '\0';
}
}
}, char *pszContexts, int nContextsSize )
GetProtagParam( Team, int, GetProtagParamInner( Team ) )
#endif
GetProtagParam( ViewModel, const char*, GetProtagParamInner( ViewModel, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( ViewModelFOV, float*, GetProtagParamInner( ViewModelFOV, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( UsesHands, bool*, GetProtagParamInner( UsesHands, pWeapon ), const CBaseCombatWeapon *pWeapon )
GetProtagParam( HandRig, int*, GetProtagParamInner( HandRig, pWeapon ), const CBaseCombatWeapon *pWeapon )
//----------------------------------------------------------------------------
#define GetProtagonistRecurse( funcName, ... ) \
FOR_EACH_VEC( pProtag.vecParents, i ) \
{ \
ProtagonistData_t *pParent = FindProtagonist(pProtag.vecParents[i]); \
if (!pParent) \
continue; \
auto returnVar = funcName( *pParent, ##__VA_ARGS__ ); \
if (returnVar) \
return returnVar; \
} \
#define GetProtagonistRecurseNoReturn( funcName, ... ) \
FOR_EACH_VEC( pProtag.vecParents, i ) \
{ \
ProtagonistData_t *pParent = FindProtagonist(pProtag.vecParents[i]); \
if (!pParent) \
continue; \
funcName( *pParent, ##__VA_ARGS__ ); \
} \
#ifdef CLIENT_DLL
#else
const char *CProtagonistSystem::DoGetProtagonist_PlayerModel( ProtagonistData_t &pProtag )
{
if (pProtag.pszPlayerModel)
return pProtag.pszPlayerModel;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModel )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_PlayerModelSkin( ProtagonistData_t &pProtag )
{
if (pProtag.nPlayerSkin >= 0)
return pProtag.nPlayerSkin;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModelSkin )
return 0;
}
int CProtagonistSystem::DoGetProtagonist_PlayerModelBody( ProtagonistData_t &pProtag )
{
if (pProtag.nPlayerBody >= 0)
return pProtag.nPlayerBody;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_PlayerModelBody )
return 0;
}
const char *CProtagonistSystem::DoGetProtagonist_HandModel( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
int nRigType = pWeapon ? pWeapon->GetHandRig() : HANDRIG_DEFAULT;
if (pProtag.pszHandModels[nRigType])
return pProtag.pszHandModels[nRigType];
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModel, pWeapon )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_HandModelSkin( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
if (pProtag.nHandSkin >= 0)
return pProtag.nHandSkin;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModelSkin, pWeapon )
return NULL;
}
int CProtagonistSystem::DoGetProtagonist_HandModelBody( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
if (pProtag.nHandBody >= 0)
return pProtag.nHandBody;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandModelBody, pWeapon )
return NULL;
}
bool CProtagonistSystem::DoGetProtagonist_ResponseContexts( ProtagonistData_t &pProtag, char *pszContexts, int nContextsSize )
{
if (pProtag.pszResponseContexts)
{
V_strncat( pszContexts, pProtag.pszResponseContexts, nContextsSize );
V_strncat( pszContexts, ",", nContextsSize );
}
// Recursively search parent protagonists
GetProtagonistRecurseNoReturn( DoGetProtagonist_ResponseContexts, pszContexts, nContextsSize )
return pszContexts[0] != '\0';
}
int CProtagonistSystem::DoGetProtagonist_Team( ProtagonistData_t &pProtag )
{
if (pProtag.nTeam >= -1)
return pProtag.nTeam;
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_Team )
return TEAM_ANY;
}
#endif
const char *CProtagonistSystem::DoGetProtagonist_ViewModel( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
if (pProtag.dictWpnData[i].pszVM)
return pProtag.dictWpnData[i].pszVM;
break;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_ViewModel, pWeapon )
return NULL;
}
float *CProtagonistSystem::DoGetProtagonist_ViewModelFOV( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].flVMFOV;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_ViewModelFOV, pWeapon )
return NULL;
}
bool *CProtagonistSystem::DoGetProtagonist_UsesHands( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].bUsesHands;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_UsesHands, pWeapon )
return NULL;
}
int *CProtagonistSystem::DoGetProtagonist_HandRig( ProtagonistData_t &pProtag, const CBaseCombatWeapon *pWeapon )
{
FOR_EACH_DICT_FAST( pProtag.dictWpnData, i )
{
// HACKHACK: GetClassname is not const
if (!FStrEq( pProtag.dictWpnData.GetElementName( i ), const_cast<CBaseCombatWeapon*>(pWeapon)->GetClassname() ))
continue;
return &pProtag.dictWpnData[i].nHandRig;
}
// Recursively search parent protagonists
GetProtagonistRecurse( DoGetProtagonist_HandRig, pWeapon )
return NULL;
}
//----------------------------------------------------------------------------
const char *CProtagonistSystem::FindString( const char *string )
{
unsigned short i = m_Strings.Find( string );
return i == m_Strings.InvalidIndex() ? NULL : m_Strings[i];
}
const char *CProtagonistSystem::AllocateString( const char *string )
{
int i = m_Strings.Find( string );
if (i != m_Strings.InvalidIndex())
{
return m_Strings[i];
}
int len = Q_strlen( string );
char *out = new char[len + 1];
Q_memcpy( out, string, len );
out[len] = 0;
return m_Strings[m_Strings.Insert( out )];
}
//----------------------------------------------------------------------------
void CProtagonistSystem::PurgeProtagonists()
{
m_Protagonists.RemoveAll();
for (unsigned int i = 0; i < m_Strings.Count(); i++)
{
delete m_Strings[i];
}
m_Strings.Purge();
}
void CProtagonistSystem::PrintProtagonistData()
{
Msg( "PROTAGONISTS\n\n" );
FOR_EACH_VEC( m_Protagonists, i )
{
ProtagonistData_t &pProtag = m_Protagonists[i];
Msg( "\t\"%s\"\n", pProtag.szName );
Msg( "\t\tParents: %i\n", pProtag.vecParents.Count() );
if (pProtag.vecParents.Count() > 0)
{
FOR_EACH_VEC( pProtag.vecParents, j )
{
ProtagonistData_t *pParent = FindProtagonist( pProtag.vecParents[j] );
if (!pParent)
continue;
Msg( "\t\t\t\"%s\"\n", pParent->szName );
}
}
#ifdef CLIENT_DLL
#else
if (pProtag.pszPlayerModel)
Msg( "\t\tPlayer model: \"%s\" (%i, %i)\n", pProtag.pszPlayerModel, pProtag.nPlayerSkin, pProtag.nPlayerBody );
for (int j = 0; j < NUM_HAND_RIG_TYPES; j++)
{
extern const char *pHandRigs[NUM_HAND_RIG_TYPES];
if (pProtag.pszHandModels[j])
Msg( "\t\tHand %s model: \"%s\" (%i, %i)\n", pHandRigs[j], pProtag.pszHandModels[j], pProtag.nPlayerSkin, pProtag.nPlayerBody );
}
// Weapon Data
Msg( "\t\tWeapon Data: %i\n", pProtag.dictWpnData.Count() );
FOR_EACH_DICT_FAST( pProtag.dictWpnData, j )
{
Msg( "\t\t\t%s\n", pProtag.dictWpnData.GetElementName(j) );
if (pProtag.dictWpnData[j].pszVM)
Msg( "\t\t\t\tViewmodel: \"%s\"\n", pProtag.dictWpnData[j].pszVM );
Msg( "\t\t\t\tUses hands: %d, hand rig: %i\n", pProtag.dictWpnData[j].bUsesHands, pProtag.dictWpnData[j].nHandRig );
}
#endif
}
}
//----------------------------------------------------------------------------
CON_COMMAND_SHARED( protagonist_reload, "Reloads protagonist data" )
{
#ifdef CLIENT_DLL
if (C_BasePlayer::GetLocalPlayer() == NULL)
{
Msg( "Must be in a level\n" );
return;
}
#endif
g_ProtagonistSystem.PurgeProtagonists();
g_ProtagonistSystem.LoadProtagonistManifest( "scripts/protagonists/protagonists_manifest.txt" );
}
CON_COMMAND_SHARED( protagonist_dump, "Dumps protagonist data" )
{
#ifdef CLIENT_DLL
if (C_BasePlayer::GetLocalPlayer() == NULL)
{
Msg( "Must be in a level\n" );
return;
}
#endif
g_ProtagonistSystem.PrintProtagonistData();
}

View File

@ -0,0 +1,153 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: System to easily switch between player characters.
//
// Author: Blixibon
//
//=============================================================================//
#ifndef PROTAGONIST_SYSTEM_H
#define PROTAGONIST_SYSTEM_H
#ifdef _WIN32
#pragma once
#endif
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#else
#include "player.h"
#endif
#define MAX_PROTAGONIST_NAME 32
//=============================================================================
//=============================================================================
class CProtagonistSystem : public CAutoGameSystem
{
public:
CProtagonistSystem() : m_Strings( 256, 0, &StringLessThan ) { }
~CProtagonistSystem() { PurgeProtagonists(); }
private:
struct ProtagonistData_t
{
ProtagonistData_t()
{
#ifndef CLIENT_DLL
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
pszHandModels[i] = NULL;
#endif
}
char szName[MAX_PROTAGONIST_NAME];
CUtlVector<int> vecParents;
#ifdef CLIENT_DLL
#else
// Playermodel
const char *pszPlayerModel = NULL;
int nPlayerSkin = -1;
int nPlayerBody = -1;
// Hands
const char *pszHandModels[NUM_HAND_RIG_TYPES];
int nHandSkin = -1;
int nHandBody = -1;
// Responses
const char *pszResponseContexts = NULL;
// Multiplayer
int nTeam = TEAM_ANY;
#endif
// Weapon Data
struct WeaponDataOverride_t
{
const char *pszVM = NULL;
bool bUsesHands = false;
int nHandRig = 0;
float flVMFOV = 0.0f;
};
CUtlDict<WeaponDataOverride_t> dictWpnData;
};
public:
bool Init();
void Shutdown();
void LevelInitPreEntity();
void LevelShutdownPostEntity();
//----------------------------------------------------------------------------
void LoadProtagonistManifest( const char *pszFile );
void LoadProtagonistFile( const char *pszFile );
//----------------------------------------------------------------------------
int FindProtagonistIndex( const char *pszName );
const char *FindProtagonistByModel( const char *pszModelName );
void PrecacheProtagonist( CBaseEntity *pSource, int nIdx );
//----------------------------------------------------------------------------
#define DeclareProtagonistFunc(type, name, ...) \
type GetProtagonist_##name( const CBasePlayer *pPlayer, ##__VA_ARGS__ ); \
type GetProtagonist_##name( const int nProtagonistIndex, ##__VA_ARGS__ ); \
private: \
type DoGetProtagonist_##name( ProtagonistData_t &pProtag, ##__VA_ARGS__ ); \
public: \
#ifdef CLIENT_DLL
#else
DeclareProtagonistFunc( const char*, PlayerModel )
DeclareProtagonistFunc( int, PlayerModelSkin )
DeclareProtagonistFunc( int, PlayerModelBody )
DeclareProtagonistFunc( const char*, HandModel, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int, HandModelSkin, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int, HandModelBody, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( bool, ResponseContexts, char *pszContexts, int nContextsSize )
DeclareProtagonistFunc( int, Team )
#endif
DeclareProtagonistFunc( const char*, ViewModel, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( float*, ViewModelFOV, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( bool*, UsesHands, const CBaseCombatWeapon *pWeapon )
DeclareProtagonistFunc( int*, HandRig, const CBaseCombatWeapon *pWeapon )
//----------------------------------------------------------------------------
void PurgeProtagonists();
void PrintProtagonistData();
//----------------------------------------------------------------------------
private:
ProtagonistData_t *GetPlayerProtagonist( const CBasePlayer *pPlayer );
ProtagonistData_t *FindProtagonist( const char *pszName );
ProtagonistData_t *FindProtagonist( int nIndex );
//----------------------------------------------------------------------------
const char *FindString( const char *string );
const char *AllocateString( const char *string );
//----------------------------------------------------------------------------
private:
CUtlVector<ProtagonistData_t> m_Protagonists;
// Dedicated strings, copied from game string pool
CUtlRBTree<const char *> m_Strings;
};
extern CProtagonistSystem g_ProtagonistSystem;
#endif // PROTAGONIST_SYSTEM_H

View File

@ -408,6 +408,7 @@ FileWeaponInfo_t::FileWeaponInfo_t()
m_flSwaySpeedScale = 1.0f;
szDroppedModel[0] = 0;
m_bUsesHands = false;
m_nHandRig = HANDRIG_DEFAULT;
m_nWeaponRestriction = WPNRESTRICT_NONE;
#endif
}
@ -417,6 +418,12 @@ extern ConVar hud_fastswitch;
#endif
#ifdef MAPBASE
const char* pHandRigs[NUM_HAND_RIG_TYPES] = {
"default",
"css",
"blender",
};
const char* pWeaponRestrictions[NUM_WEAPON_RESTRICTION_TYPES] = {
"none",
"player_only",
@ -493,6 +500,19 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam
m_bUsesHands = ( pKeyValuesData->GetInt( "uses_hands", 0 ) != 0 ) ? true : false;
const char* pszHandRig = pKeyValuesData->GetString("hand_rig", nullptr);
if (pszHandRig)
{
for (int i = 0; i < NUM_HAND_RIG_TYPES; i++)
{
if (V_stricmp(pszHandRig, pHandRigs[i]) == 0)
{
m_nHandRig = i;
break;
}
}
}
const char* pszRestrictString = pKeyValuesData->GetString("usage_restriction", nullptr);
if (pszRestrictString)
{

View File

@ -59,6 +59,15 @@ class CHudTexture;
class KeyValues;
#ifdef MAPBASE
enum HandRigTypes_e
{
HANDRIG_DEFAULT, // Default HL2 rig
HANDRIG_CSS, // CS:S viewmodel rig
HANDRIG_BLENDER, // Blender IK rig
NUM_HAND_RIG_TYPES
};
enum WeaponUsageRestricions_e
{
WPNRESTRICT_NONE = 0,
@ -136,6 +145,7 @@ public:
char szDroppedModel[MAX_WEAPON_STRING]; // Model of this weapon when dropped on the ground
bool m_bUsesHands;
int m_nHandRig;
int m_nWeaponRestriction;
#endif