Hand Viewmodels: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (formatting and spelling fix)
(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!


Put this file in your shared folder or shared game folder.
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  
// For whatever reason the parent doesn't get sent  
// I don't really want to mess with the baseviewmodel
// 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>


I'd put something like this in your '''server/player.cpp''' or in your mod's server '''player.cpp''' below <code>CreateViewModel</code>.
=== 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 ===


=== server/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>


You should also add this to your server or mod's '''player.h''' below <code>CreateViewModel</code>.
Now we need to call it. So inside the <code>Spawn</code> function, and below <code>CreateViewModel();</code>, add:
 
=== server/player.h ===
<source lang=cpp>
<source lang=cpp>
virtual void         CreateHandModel( int viewmodelindex = 1, int iOtherVm = 0 );
CreateHandModel();
</source>
</source>


Now if you want this to actually work you're gonna need to call it.
=== your_player.cpp ===
Call it sometime after <code>CreateViewModel</code> in either <code>CBasePlayer</code> or your mod's player spawn.
 
{{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.}}
 


=== server/player.cpp ===
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>
CreateHandModel();
GetViewModel(1)->SetModel( "models/coolhandsfolder/cool_arms.mdl" );
</source>
</source>


So now that the <code>HandViewModel</code> has been created we need to set its model.
{{Note|This might not work perfectly on the {{As}} Alien Swarm branch since it does weird stuff with bone merging sometimes.}}
Put this in your mod's server's player's spawn sometime after the hand model has been created.
 


=== server/your_mod/your_player.cpp ===
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>
GetViewModel(1)->SetModel("models/coolhandsfolder/cool_arms.mdl");
PrecacheModel( "models/coolhandsfolder/cool_arms.mdl" );
</source>
</source>


Make sure your model has been precached first!
== Fix for multiplayer mods with a round system ==


{{note|This might not work perfectly on the {{As}} Alien Swarm branch since it does weird stuff sometimes with bone merging.}}
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:


=== Fix for MP games with round system ===
<source lang=cpp highlight=39>
If you are using this method for multiplayer and your game (mod) has rounds 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:
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", // our new viewmodel entity
"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:

handviewmodel_shared.cpp

#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

Note.pngNote: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 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" );
Note.pngNote:This might not work perfectly on the Alien Swarm 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 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.