Hand Viewmodels: Difference between revisions
m (formatting and spelling fix) |
GamerDude27 (talk | contribs) (Cleanup, added how to precache the model) |
||
Line 1: | Line 1: | ||
== Introduction == | |||
Have you ever wanted fancy c_ models like all those cool games? | Have you ever wanted fancy c_ models like all those cool games? | ||
Well, now you can! | |||
== Implementation == | |||
Create the following file and put it in the '''src/game/shared''' folder: | |||
=== handviewmodel_shared.cpp === | === handviewmodel_shared.cpp === | ||
<source lang=cpp> | <source lang=cpp> | ||
#include "cbase.h" | #include "cbase.h" | ||
Line 10: | Line 16: | ||
#if defined( CLIENT_DLL ) | #if defined( CLIENT_DLL ) | ||
#define CHandViewModel C_HandViewModel | #define CHandViewModel C_HandViewModel | ||
#endif | #endif | ||
Line 16: | Line 22: | ||
{ | { | ||
DECLARE_CLASS( CHandViewModel, CBaseViewModel ); | DECLARE_CLASS( CHandViewModel, CBaseViewModel ); | ||
public: | public: | ||
DECLARE_NETWORKCLASS(); | DECLARE_NETWORKCLASS(); | ||
private: | private: | ||
}; | }; | ||
LINK_ENTITY_TO_CLASS(hand_viewmodel, CHandViewModel); | LINK_ENTITY_TO_CLASS( hand_viewmodel, CHandViewModel ); | ||
IMPLEMENT_NETWORKCLASS_ALIASED(HandViewModel, DT_HandViewModel) | IMPLEMENT_NETWORKCLASS_ALIASED( HandViewModel, DT_HandViewModel ) | ||
// | // For whatever reason the parent doesn't get sent | ||
// I don't really want to mess with | // And I don't really want to mess with BaseViewModel | ||
// so now it does | // so now it does | ||
BEGIN_NETWORK_TABLE(CHandViewModel, DT_HandViewModel) | BEGIN_NETWORK_TABLE( CHandViewModel, DT_HandViewModel ) | ||
#ifndef CLIENT_DLL | #ifndef CLIENT_DLL | ||
SendPropEHandle(SENDINFO_NAME(m_hMoveParent, moveparent)), | SendPropEHandle( SENDINFO_NAME( m_hMoveParent, moveparent ) ), | ||
#else | #else | ||
RecvPropInt(RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent), | RecvPropInt( RECVINFO_NAME( m_hNetworkMoveParent, moveparent ), 0, RecvProxy_IntToMoveParent ), | ||
#endif | #endif | ||
END_NETWORK_TABLE() | END_NETWORK_TABLE() | ||
</source> | </source> | ||
=== player.h === | |||
In '''src/server/player.h''' you should now add the following below the <code>CreateViewModel( int viewmodelindex = 0 );</code> declaration: | |||
<source lang=cpp> | |||
virtual void CreateHandModel( int viewmodelindex = 1, int iOtherVm = 0 ); | |||
</source> | |||
=== player.cpp === | |||
In '''src/server/player.cpp''' put this below the <code>CreateViewModel( int index /*=0*/ )</code> function: | |||
<source lang=cpp> | <source lang=cpp> | ||
void CBasePlayer::CreateHandModel(int index, int iOtherVm) | //----------------------------------------------------------------------------- | ||
// Purpose: | |||
//----------------------------------------------------------------------------- | |||
void CBasePlayer::CreateHandModel( int index, int iOtherVm ) | |||
{ | { | ||
Assert(index >= 0 && index < MAX_VIEWMODELS && iOtherVm >= 0 && iOtherVm < MAX_VIEWMODELS ); | Assert( index >= 0 && index < MAX_VIEWMODELS && iOtherVm >= 0 && iOtherVm < MAX_VIEWMODELS ); | ||
if (GetViewModel(index)) | if ( GetViewModel( index ) ) | ||
return; | return; | ||
CBaseViewModel *vm = (CBaseViewModel *)CreateEntityByName("hand_viewmodel"); | CBaseViewModel *vm = (CBaseViewModel *)CreateEntityByName( "hand_viewmodel" ); | ||
if (vm) | if ( vm ) | ||
{ | { | ||
vm->SetAbsOrigin(GetAbsOrigin()); | vm->SetAbsOrigin( GetAbsOrigin() ); | ||
vm->SetOwner(this); | vm->SetOwner( this ); | ||
vm->SetIndex(index); | vm->SetIndex( index ); | ||
DispatchSpawn(vm); | DispatchSpawn( vm ); | ||
vm->FollowEntity(GetViewModel(iOtherVm), true); | vm->FollowEntity( GetViewModel( iOtherVm ), true ); | ||
m_hViewModel.Set(index, vm); | m_hViewModel.Set( index, vm ); | ||
} | } | ||
} | } | ||
</source> | </source> | ||
Now we need to call it. So inside the <code>Spawn</code> function, and below <code>CreateViewModel();</code>, add: | |||
<source lang=cpp> | <source lang=cpp> | ||
CreateHandModel(); | |||
</source> | </source> | ||
=== your_player.cpp === | |||
{{Note|Depending on the base you're modifying you'll have to pick the relevant file for this. If it's an SP mod modify '''hl2_player.cpp''', if it's an MP mod modify '''hl2mp_player.cpp''' or if you have your own player class just modify that.}} | |||
So now that the <code>HandViewModel</code> has been created we need to set its model. | |||
Put this in your player's <code>Spawn</code> function sometime after the hand model has been created: | |||
<source lang=cpp> | <source lang=cpp> | ||
GetViewModel(1)->SetModel( "models/coolhandsfolder/cool_arms.mdl" ); | |||
</source> | </source> | ||
{{Note|This might not work perfectly on the {{As}} Alien Swarm branch since it does weird stuff with bone merging sometimes.}} | |||
Also make sure your model has been precached first! To do this add the following into your <code>Precache</code> function: | |||
<source lang=cpp> | <source lang=cpp> | ||
PrecacheModel( "models/coolhandsfolder/cool_arms.mdl" ); | |||
</source> | </source> | ||
== Fix for multiplayer mods with a round system == | |||
If you are using this method for multiplayer and your mod has a round system, you will have to add your new viewmodel's entity name (in this tutorial it's <code>hand_viewmodel</code>) into <code>s_PreserveEnts[]</code> array which is located in the gamerules file ('''hl2mp_gamerules.cpp''' for example) so it will look something like this: | |||
<source lang=cpp highlight=39> | |||
static const char *s_PreserveEnts[] = | |||
<source lang=cpp> | |||
static const char *s_PreserveEnts[] = | |||
{ | { | ||
"ai_network", | "ai_network", | ||
Line 129: | Line 146: | ||
"viewmodel", | "viewmodel", | ||
"predicted_viewmodel", | "predicted_viewmodel", | ||
"hand_viewmodel", // | "hand_viewmodel", // Our new viewmodel entity | ||
"worldspawn", | "worldspawn", | ||
"point_devshot_camera", | "point_devshot_camera", | ||
Line 138: | Line 155: | ||
If you don't do it, your hands viewmodel '''will be removed every time new round has started'''. | If you don't do it, your hands viewmodel '''will be removed every time new round has started'''. | ||
[[Category: Programming]][[Category: Tutorials]] | [[Category: Programming]] | ||
[[Category: Tutorials]] |
Revision as of 09:01, 8 August 2020
Introduction
Have you ever wanted fancy c_ models like all those cool games?
Well, now you can!
Implementation
Create the following file and put it in the src/game/shared folder:
#include "cbase.h"
#include "baseviewmodel_shared.h"
#if defined( CLIENT_DLL )
#define CHandViewModel C_HandViewModel
#endif
class CHandViewModel : public CBaseViewModel
{
DECLARE_CLASS( CHandViewModel, CBaseViewModel );
public:
DECLARE_NETWORKCLASS();
private:
};
LINK_ENTITY_TO_CLASS( hand_viewmodel, CHandViewModel );
IMPLEMENT_NETWORKCLASS_ALIASED( HandViewModel, DT_HandViewModel )
// For whatever reason the parent doesn't get sent
// And I don't really want to mess with BaseViewModel
// so now it does
BEGIN_NETWORK_TABLE( CHandViewModel, DT_HandViewModel )
#ifndef CLIENT_DLL
SendPropEHandle( SENDINFO_NAME( m_hMoveParent, moveparent ) ),
#else
RecvPropInt( RECVINFO_NAME( m_hNetworkMoveParent, moveparent ), 0, RecvProxy_IntToMoveParent ),
#endif
END_NETWORK_TABLE()
player.h
In src/server/player.h you should now add the following below the CreateViewModel( int viewmodelindex = 0 );
declaration:
virtual void CreateHandModel( int viewmodelindex = 1, int iOtherVm = 0 );
player.cpp
In src/server/player.cpp put this below the CreateViewModel( int index /*=0*/ )
function:
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer::CreateHandModel( int index, int iOtherVm )
{
Assert( index >= 0 && index < MAX_VIEWMODELS && iOtherVm >= 0 && iOtherVm < MAX_VIEWMODELS );
if ( GetViewModel( index ) )
return;
CBaseViewModel *vm = (CBaseViewModel *)CreateEntityByName( "hand_viewmodel" );
if ( vm )
{
vm->SetAbsOrigin( GetAbsOrigin() );
vm->SetOwner( this );
vm->SetIndex( index );
DispatchSpawn( vm );
vm->FollowEntity( GetViewModel( iOtherVm ), true );
m_hViewModel.Set( index, vm );
}
}
Now we need to call it. So inside the Spawn
function, and below CreateViewModel();
, add:
CreateHandModel();
your_player.cpp

So now that the HandViewModel
has been created we need to set its model.
Put this in your player's Spawn
function sometime after the hand model has been created:
GetViewModel(1)->SetModel( "models/coolhandsfolder/cool_arms.mdl" );


Also make sure your model has been precached first! To do this add the following into your Precache
function:
PrecacheModel( "models/coolhandsfolder/cool_arms.mdl" );
Fix for multiplayer mods with a round system
If you are using this method for multiplayer and your mod has a round system, you will have to add your new viewmodel's entity name (in this tutorial it's hand_viewmodel
) into s_PreserveEnts[]
array which is located in the gamerules file (hl2mp_gamerules.cpp for example) so it will look something like this:
static const char *s_PreserveEnts[] =
{
"ai_network",
"ai_hint",
"hl2mp_gamerules",
"team_manager",
"player_manager",
"env_soundscape",
"env_soundscape_proxy",
"env_soundscape_triggerable",
"env_sun",
"env_wind",
"env_fog_controller",
"func_brush",
"func_wall",
"func_buyzone",
"func_illusionary",
"infodecal",
"info_projecteddecal",
"info_node",
"info_target",
"info_node_hint",
"info_player_deathmatch",
"info_player_combine",
"info_player_rebel",
"info_map_parameters",
"keyframe_rope",
"move_rope",
"info_ladder",
"player",
"point_viewcontrol",
"scene_manager",
"shadow_control",
"sky_camera",
"soundent",
"trigger_soundscape",
"viewmodel",
"predicted_viewmodel",
"hand_viewmodel", // Our new viewmodel entity
"worldspawn",
"point_devshot_camera",
"", // END Marker
};
If you don't do it, your hands viewmodel will be removed every time new round has started.