Raised This Month: $51 Target: $400
 12% 

[TF2] Proper viewmodel hands animations


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Naydef
Senior Member
Join Date: Dec 2015
Location: Doom Town, Nevada
Old 12-17-2022 , 15:09   [TF2] Proper viewmodel hands animations
Reply With Quote #1

Hello everyone

Recently this script was released for giving weapons to players using Vscript. I noticed that in first person hand animations are properly shown. I've checked the source code of the script, but I get a bit lost while reading (it includes additional code for unrelated things).

Does anyone can show code(or at least the complete steps necessary) of how to achieve nice first person animation when classes use other class' weapons?

From the testing I've done so far I went to the conclusion that one needs to set the model of the tf_viewmodel to the view model of the class the weapon is intended for. After that parent another entity(tf_wearable_vm) with view model of the class the player is. At the end, hide the tf_viewmodel. Also handle weapon switching.

Without doing step 2(parenting another entity) when the player is holding a weapon which is matched for the view model set to the tf_viewmodel entity, there animations are pretty much broken.

What I'm missing here?

What I'm missing?
__________________
My plugins:
*None for now*


Steam:
naydef
Naydef is offline
luki1412
Veteran Member
Join Date: Oct 2008
Location: OnPluginStart()
Old 12-30-2022 , 05:09   Re: [TF2] Proper viewmodel hands animations
Reply With Quote #2

Code copied from code.nut -> https://tf2maps.net/downloads/vscrip..._weapon.14897/
Code:
//-----------------------------------------------------------------------------
// Purpose: Used to update viewmodel arms.
//	-> See top of script for more info
//-----------------------------------------------------------------------------
::CTFPlayer.UpdateArms <- function()
{
	if ( !CVAR_GTFW_USE_VIEWMODEL_FIX ) {
		GTFW_DevPrint("give_tf_weapon: CVAR_GTFW_USE_VIEWMODEL_FIX = false. UpdateArms cancelled.")
		return
	}
	
/* Purpose: This is the special VM fix.
 it creates class arms that bonemerge with VM
*/
		
// Table for bonemerged class arms, per player.
// Adds a class arms model if nothing has parented to the main_viewmodel
	if ( !( this in PlayerLoadoutGlobal_BonemergedArms ) ) {
		local main_viewmodel = NetProps.GetPropEntity(this, "m_hViewModel")
		local wearable_handle = this.GTFW_CreateCustomWeaponModel(false, main_viewmodel, GTFW_MODEL_ARMS[this.GetPlayerClass()] )	// creates player class' arms, parents and does all that stuff
	/*	Purpose: Initialize tables if they aren't already made
		So we don't create more arms than necessary
		Puts into a table "player = arms model" for easy tracking
	*/
		::PlayerLoadoutGlobal_BonemergedArms[this] <- wearable_handle
	}
	
// Purpose: Writes to the tables above, which keep the class arms the weapon needs to update to when switched to.
	for (local i = 0; i < GLOBAL_WEAPON_COUNT; i++)
	{
		local wep = NetProps.GetPropEntityArray(this, "m_hMyWeapons", i)
		
		if ( wep )
		{
			local baseitem = this.ReturnWeaponTable(GetItemID(wep))
			
			if ( baseitem ) {
				if ( baseitem.className == "tf_weapon_pda_spy" ) {
					wep.SetModelSimple( "models/weapons/v_models/v_pda_spy.mdl" )
					wep.SetCustomViewModel( "models/weapons/v_models/v_pda_spy.mdl" )
				}
				else if ( baseitem.className == "tf_weapon_invis" && baseitem.itemID == 30 ) {
					wep.SetModelSimple( "models/weapons/v_models/v_watch_spy.mdl" )
					wep.SetCustomViewModel( "models/weapons/v_models/v_watch_spy.mdl" )
				}
				else if ( baseitem.className == "tf_weapon_invis" && baseitem.itemID == 59 ) {
					wep.SetModelSimple( "models/weapons/v_models/v_watch_pocket_spy.mdl" )
					wep.SetCustomViewModel( "models/weapons/v_models/v_watch_pocket_spy.mdl" )
				}
				else if ( baseitem.className == "tf_weapon_invis" && baseitem.itemID == 60 ) {
					wep.SetModelSimple( "models/weapons/v_models/v_watch_leather_spy.mdl" )
					wep.SetCustomViewModel( "models/weapons/v_models/v_watch_leather_spy.mdl" )
				}
				else if ( baseitem.className == "tf_weapon_invis" && baseitem.itemID == 297 ) {
					wep.SetModelSimple( "models/weapons/v_models/v_ttg_watch_spy.mdl" )
					wep.SetCustomViewModel( "models/weapons/v_models/v_ttg_watch_spy.mdl" )
				}
				else if ( baseitem.className == "tf_weapon_invis" && baseitem.itemID == 947 ) {
					wep.SetModelSimple( "models/workshop_partner/weapons/v_models/v_hm_watch/v_hm_watch.mdl" )
					wep.SetCustomViewModel( "models/workshop_partner/weapons/v_models/v_hm_watch/v_hm_watch.mdl" )
				}
				else if ( this.HasGunslinger() ) {
					wep.SetModelSimple( GTFW_MODEL_ARMS[10] )
					wep.SetCustomViewModel( GTFW_MODEL_ARMS[10] )
				}
				else if ( baseitem.class_arms ) {
					wep.SetModelSimple( baseitem.class_arms )
					wep.SetCustomViewModel( baseitem.class_arms )
				}
				else if ( baseitem.tf_class == 0 || this.GetPlayerClass() == baseitem.tf_class ) {
					wep.SetModelSimple( GTFW_MODEL_ARMS[this.GetPlayerClass()] )
					wep.SetCustomViewModel( GTFW_MODEL_ARMS[this.GetPlayerClass()] )
				}
				else {
					wep.SetModelSimple( GTFW_MODEL_ARMS[baseitem.tf_class] )
					wep.SetCustomViewModel( GTFW_MODEL_ARMS[baseitem.tf_class] )
				}
			}
		}
	}
}
Code:
//-----------------------------------------------------------------------------
// Purpose: Updates viewmodel think script.
// Clears and updates the viewmodel think script for weapon switching, enabling/disabling visibility of custom weapons.
//-----------------------------------------------------------------------------
::CTFPlayer.AddThinkToViewModel <- function()
{
	if ( !CVAR_GTFW_USE_VIEWMODEL_FIX ) {
		return
	}
	local main_viewmodel = NetProps.GetPropEntity(this, "m_hViewModel")
	local wearable_handle = null
	
// Think script itself.
// Reads from several tables to find weapon's class arms.
	if( main_viewmodel.ValidateScriptScope() )
	{
		local wep = null

		local player = this
		
		local entscriptname = "THINK_VM_FIX_"+this.tostring()
		
		local main_viewmodel = NetProps.GetPropEntity(this, "m_hViewModel")
		
		local DisableDrawQueue = null
		local asdf = null
		
		local WEP_TAUNT_FIX = false
		
		const THINK_VMFIX_DELAY = 0
		
		local entityscript = main_viewmodel.GetScriptScope()
		entityscript[entscriptname] <- function()
		{
		// Fixes taunts
		// It clears the parent of the original weapon, and makes it invisible.
		// It is reparented inside the DisableDrawQueue section (a few lines down)
			if ( player.InCond(7) && WEP_TAUNT_FIX ) {
				WEP_TAUNT_FIX = false
				wep = player.GetActiveWeapon()
				
				NetProps.SetPropEntity(DisableDrawQueue, "m_hWeaponAssociatedWith", wep)
				NetProps.SetPropInt(wep, "m_fEffects", 161)
				DoEntFire("!self", "Clearparent", "", 0, null, wep)
				DoEntFire("!self", "DisableShadow", "", 0, null, wep)
				wep.EnableDraw()
				NetProps.SetPropInt(wep, "m_iState", 0)
				wep = null
			}
		// updates weapons' visibility based on which one is being used
			else if ( player.GetActiveWeapon() && !player.InCond(7) && player.GetActiveWeapon() != wep )
			{
				wep = player.GetActiveWeapon()
				local wepC = wep.GetClassname()
				
		// This fixes taunts and switching away from custom weapons.
		// It reparents and sets owner back to the player
				if ( DisableDrawQueue )
				{
					NetProps.SetPropInt(wep, "m_iState", 0)
					DoEntFire("!self", "RunScriptCode", "self.DisableDraw()", 0, null, DisableDrawQueue)	//using delay here purposely. Won't update b/c thinks too fast!!
					NetProps.SetPropEntity(DisableDrawQueue, "m_hWeaponAssociatedWith", null)
					
					DoEntFire("!self", "SetParent", "!activator", 0, player, wep)
					wep.SetOwner(player)
					
					DisableDrawQueue = null
				}

			// These are vars are for the next part
				asdf = null
				DisableDrawQueue = null
				
			//Checks for custom weapon and/or weapon unintended for the class.
			//Passing as a custom weapon means it updates the weapon visibility, disables the base weapon,
			// then adds it to the queue to be disabled when switched away
				if ( IS_CUSTOM(wep, false) )
				{
					main_viewmodel.DisableDraw()		//makes firstperson weapon invisible (as well as other's class arms from the other class)
					WEP_TAUNT_FIX = true
					
					NetProps.SetPropInt(wep, "m_iState", 0)
					wep.EnableDraw()	//Have to send EnableDraw() first before DisableDraw(). Thanks Sorse(tm)!
					DoEntFire("!self", "RunScriptCode", "self.DisableDraw()", 0.01, null, wep)	//using delay here purposely. Won't update b/c thinks too fast!!
					
					asdf = PlayerLoadoutGlobal_CustomWeaponModels_TP[player][wepC]	//reads from table
					asdf.EnableDraw()
					DoEntFire("!self", "RunScriptCode", "self.EnableDraw()", 0.01, null, asdf)	//using delay here purposely. Won't update b/c thinks too fast!!
					
					DisableDrawQueue = asdf	//custom weapon disables visibility at beginning of think script.
				}
				else if ( IS_UNINTENDED(wep, false) ) {
					WEP_TAUNT_FIX = true
					main_viewmodel.DisableDraw() //makes firstperson weapon invisible (as well as other's class arms from the other class)
				}
			// this part reads from the bonemerged arms table
			// who ever the class is, and which ever arms they are using, this will put it as a value...

				if ( player in PlayerLoadoutGlobal_BonemergedArms ) {
					local bonemerged = PlayerLoadoutGlobal_BonemergedArms[player]
					
				// if ShortCircuit, disable the class arms' visibility
					if ( wep.GetClassname() == "tf_weapon_mechanical_arm" ) { // Short Circuit
						bonemerged.DisableDraw()
					}
					else if ( IS_CUSTOM(wep, false) || IS_UNINTENDED(wep, false) ) {
						bonemerged.EnableDraw()
					}
				}
			}
			return THINK_VMFIX_DELAY
		}
		AddThinkToEnt(main_viewmodel, entscriptname)	//adds think script
	}
}
Table from tables.nut
Code:
::GTFW_MODEL_ARMS <-
[
	"models/weapons/c_models/c_medic_arms.mdl", //dummy
	"models/weapons/c_models/c_scout_arms.mdl",
	"models/weapons/c_models/c_sniper_arms.mdl",
	"models/weapons/c_models/c_soldier_arms.mdl",
	"models/weapons/c_models/c_demo_arms.mdl",
	"models/weapons/c_models/c_medic_arms.mdl",
	"models/weapons/c_models/c_heavy_arms.mdl",
	"models/weapons/c_models/c_pyro_arms.mdl",
	"models/weapons/c_models/c_spy_arms.mdl",
	"models/weapons/c_models/c_engineer_arms.mdl",
	"models/weapons/c_models/c_engineer_gunslinger.mdl",	//CIVILIAN/Gunslinger
]
The code above seems to be the fix for viewmodels.
One function updates arms viewmodels, the other adds weapon think in case user switches to/from another weapon.
Code:
	CTFPlayer.UpdateArms()
		Updates all arm models for all equipped weapons. Branches from `GiveWeapon`.
	CTFPlayer.AddThinkToViewModel()
		Updates the think script on tf_viewmodel. Branches from `GiveWeapon`.
__________________

Last edited by luki1412; 12-30-2022 at 05:17.
luki1412 is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 06:12.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode