Make Scout Run

Hello.
So, I’ve managed to make a ragdoll walk by means of constraining its bones to a NPC’s bones, like following its animation cycle(s). But not all models have an NPC script where I can call on certain actions (enumerations). I’m now looking to do stuff with the TF2 characters, for example the Scout. As a start I’m looking to see about making a Scout run around a bit. However, there doesn’t seem to be any Scout NPC that I can send walk/run commands to. Tried creating various npc_ with the Scout model, but either it resulted in ERROR or the NPC’s default model.

I thought of an alternative which is to play the running animation on a prop while applying the appropriate velocity. However, there seem to be a few problems.
1: I try to play a running sequence with ResetSequence (in a timer loop) but the Scout isn’t animating. I tried other animations but none were of that running type.
2: I spawn the Scout as a prop_physics so that SetVelocity or ApplyForce will work on him, but he seems to tilt to one side at first, then rolls around as he is being pushed. What other entity classes can I use instead? Or should I constrain him to another physics object that doesn’t roll?

Yo. TF2 models uses an entirely different set of animations/activities as Garry’s Mod, so you need to make sure you set all those accordingly.

There’s a tool called the animated props tool. Try it. It lets you set animations on props/ragdolls.

You know, I was a little skeptical if buu342 understood what I really wanted, but turns out the tool was more or less what I was looking for. And as wauterboi said, yeah, there does seem to be more needed for “player” animations.

After studying the code from the tool, I found that I just needed to add SetPoseParameter(“move_x”,1) and, well, now the Scout isn’t standing still anymore, but more like running still. That is he seems to be paused on his first frame in the running animation (one leg up).

I changed the class to prop_dynamic and now he is animating properly. Down side is not being able to push him about. But I guess that means I’ll have to attach the Scout to another physics object to push around.

Well, looks like I’ve run into a new problem. When trying to “attach” the bones of the ragdoll Scout onto the dynamic Scout, the limbs do snap to the position of the Scout’s running animation’s First Frame. The ragdoll does not seem to follow the animation fully to making the ragdoll run with the dynamic. This is what I’ve used in a timer loop (0.01 seconds):



for i=0,ragdoll:GetPhysicsObjectCount()-1 do
	local phys = ragdoll:GetPhysicsObjectNum(i)
	local b = ragdoll:TranslatePhysBoneToBone(i)
	local pos,ang = animBase:GetBonePosition(b)
	phys:EnableMotion(true)
	phys:Wake()
	phys:SetAngles(ang)
	phys:SetPos(pos)
	if string.sub(ragdoll:GetBoneName(b),1,4) == "prp_" then
		phys:EnableMotion(false)
		phys:Wake()
	end
end


Are there any alternative “attachment” functions I should look for if I want the ragdoll to mimic the animation being played?

Ah, hey, look what I found from a long time ago!


function GM:HandlePlayerJumping(pl)
	
	if not pl.anim_Jumping and not pl:OnGround() and pl:WaterLevel() <= 0 then
		if not pl.anim_GroundTime then
			pl.anim_GroundTime = CurTime()
		else --[[if CurTime() - pl.anim_GroundTime > 0.2 then]]
			pl.anim_Jumping = true
			pl.anim_FirstJumpFrame = false
			pl.anim_JumpStartTime = 0
		end
	end
	
	if pl.anim_Jumping then
		local firstjumpframe = pl.anim_FirstJumpFrame
		
		if pl.anim_FirstJumpFrame then
			pl.anim_FirstJumpFrame = false
			pl:AnimRestartMainSequence()
		end
		
		if pl:WaterLevel() >= 2 or --[[(CurTime() - pl.anim_JumpStartTime > 0.2 and]] pl:OnGround() --[[)]] then
			pl.anim_Jumping = false
			pl.anim_GroundTime = nil
			pl:AnimRestartMainSequence()
			
			if pl:OnGround() then
				pl:AnimRestartGesture(GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND, true)
			end
		end
		
		if pl.anim_Jumping then
			if pl.anim_JumpStartTime == 0 then
				if pl.anim_Airwalk then
					pl.anim_CalcIdeal = ACT_MP_AIRWALK
				else
					return false
				end
			elseif not firstjumpframe and CurTime() - pl.anim_JumpStartTime > pl:SequenceDuration() then
				pl.anim_CalcIdeal = ACT_MP_JUMP_FLOAT
			else
				pl.anim_CalcIdeal = ACT_MP_JUMP_START
			end
				
			return true
		end
	end
	
	pl.anim_Airwalk = false
	return false
end

function GM:HandlePlayerDucking(pl, vel)
	
	if pl:Crouching() then
		local len2d = vel:Length2D()		
		
		if len2d > 0.5 then
			pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_CROUCH_DEPLOYED) or ACT_MP_CROUCHWALK
		else
			pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_CROUCH_DEPLOYED_IDLE) or ACT_MP_CROUCH_IDLE
		end
		
		return true
	end
	
	return false
end

function GM:HandlePlayerSwimming(pl)
	
	if pl:WaterLevel() >= 2 then
		if pl.anim_FirstSwimFrame then
			pl:AnimRestartMainSequence()
			pl.anim_FirstSwimFrame = false
		end
		
		pl.anim_InSwim = true
		pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_SWIM_DEPLOYED) or ACT_MP_SWIM
		
		return true
	else
		pl.anim_InSwim = false
		if not pl.anim_FirstSwimFrame then
			pl.anim_FirstSwimFrame = true
		end
	end
	
	return false
end

function GM:UpdateAnimation(pl, velocity, maxseqgroundspeed)
    
	local maxspeed = 200
	
	if (pl:OnGround() and pl:Crouching()) then
		maxspeed = maxspeed * 0.3
	elseif pl:WaterLevel() > 1 then
		maxspeed = maxspeed * 0.8
	end
	
	local vel = 1 * velocity
	vel:Rotate(Angle(0,-pl:EyeAngles().y,0))
	vel:Rotate(Angle(-vel:Angle().p,0,0))
	
	pl:SetPoseParameter("move_x", vel.x / maxspeed)
	pl:SetPoseParameter("move_y", -vel.y / maxspeed)
	
	local pitch = math.Clamp(math.NormalizeAngle(-pl:EyeAngles().p), -45, 90)
	pl:SetPoseParameter("body_pitch", pitch)
	
	if not pl.PlayerBodyYaw or not pl.TargetBodyYaw then
		pl.TargetBodyYaw = pl:EyeAngles().y
		pl.PlayerBodyYaw = pl.TargetBodyYaw
	end
	
	local diff
	diff = pl.PlayerBodyYaw - pl:EyeAngles().y
	
	if velocity:Length2D() > 0.5 or diff > 45 or diff < -45 then
		pl.TargetBodyYaw = pl:EyeAngles().y
	end
		
	local d = pl.TargetBodyYaw - pl.PlayerBodyYaw
	if d > 180 then
		pl.PlayerBodyYaw = math.NormalizeAngle(Lerp(0.2, pl.PlayerBodyYaw+360, pl.TargetBodyYaw))
	elseif d < -180 then
		pl.PlayerBodyYaw = math.NormalizeAngle(Lerp(0.2, pl.PlayerBodyYaw-360, pl.TargetBodyYaw))
	else
		pl.PlayerBodyYaw = Lerp(0.2, pl.PlayerBodyYaw, pl.TargetBodyYaw)
	end
	
	pl:SetPoseParameter("body_yaw", diff)
	
	if CLIENT then
		pl:SetRenderAngles(Angle(0, pl.PlayerBodyYaw, 0))
		--pl:SetRenderAngles(Angle(0, pl:EyeAngles().y, 0))
	end
end

function GM:CalcMainActivity(pl, vel)
	
	pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_DEPLOYED_IDLE) or ACT_MP_STAND_IDLE
	pl.anim_CalcSeqOverride = -1
	
	if
		self:HandlePlayerDriving(pl) or
		self:HandlePlayerSwimming(pl) or
		self:HandlePlayerJumping(pl) or
		self:HandlePlayerDucking(pl, vel) then
		-- do nothing
	else
		local len2d = vel:Length2D()
		
		if len2d > 0.5 then
			pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_DEPLOYED) or ACT_MP_RUN
		end
	end
	
	return pl.anim_CalcIdeal, pl.anim_CalcSeqOverride
end

function GM:DoAnimationEvent(pl, event, data)
	
	local w = pl:GetActiveWeapon()
	if event == PLAYERANIMEVENT_JUMP then
		pl.anim_Jumping = true
		pl.anim_FirstJumpFrame = true
		pl.anim_JumpStartTime = CurTime()
		
		pl:AnimRestartMainSequence()
		
		return ACT_INVALID
	end
end

local meta = FindMetaTable("Weapon")

local OldSendWeaponAnim = meta.SendWeaponAnim

function meta:SendWeaponAnim(act)
	if not act then return end
	--MsgN(Format("SendWeaponAnim %d %s",act,tostring(self)))
	if IsValid(self.Owner) and self.Owner:IsPlayer() and IsValid(self.Owner:GetViewModel()) and self.ViewModelOverride then
		self:SetModel(self.ViewModelOverride)
		self.Owner:GetViewModel():SetModel(self.ViewModelOverride)
	end
	OldSendWeaponAnim(self,act)
end

Kilburn helped me put this together. It’s for player animations, but perhaps you can pick stuff from it.

[editline]17th March 2015[/editline]

Oops, it’s missing the translations for activities, and I can’t find TF2-specific activities. HMM. Working on it!