timer.Simple(0, function()

local reg = debug.getregistry()

local math = math

local GetTable = reg.Entity.GetTable

local GetMoveType = reg.Entity.GetMoveType
local OnGround = reg.Entity.OnGround
local WaterLevel = reg.Entity.WaterLevel
local AnimRestartMainSequence = reg.Player.AnimRestartMainSequence
local Length2D = reg.Vector.Length2DSqr

function GAMEMODE:HandlePlayerJumping(ply, velocity, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (GetMoveType(ply) == MOVETYPE_NOCLIP) then
		ply_tb.m_bJumping = false
		return
	end

	-- airwalk more like hl2mp, we airwalk until we have 0 velocity, then it's the jump animation
	-- underwater we're alright we airwalking
	if (!ply_tb.m_bJumping and !OnGround(ply) and WaterLevel(ply) <= 0) then

		if (!ply_tb.m_fGroundTime) then

			ply_tb.m_fGroundTime = CurTime()

		elseif (CurTime() - ply_tb.m_fGroundTime) > 0 and Length2D(velocity) < 0.25 then

			ply_tb.m_bJumping = true
			ply_tb.m_bFirstJumpFrame = false
			ply_tb.m_flJumpStartTime = 0

		end
	end

	if ply_tb.m_bJumping then

		if ply_tb.m_bFirstJumpFrame then

			ply_tb.m_bFirstJumpFrame = false
			AnimRestartMainSequence(ply)

		end

		if (WaterLevel(ply) >= 2) or ((CurTime() - ply_tb.m_flJumpStartTime) > 0.2 and OnGround(ply)) then

			ply_tb.m_bJumping = false
			ply_tb.m_fGroundTime = nil
			AnimRestartMainSequence(ply)

		end

		if ply_tb.m_bJumping then
			ply_tb.CalcIdeal = ACT_MP_JUMP
			return true
		end
	end

	return false

end

local IsFlagSet = reg.Entity.IsFlagSet
function GAMEMODE:HandlePlayerDucking(ply, velocity, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (!IsFlagSet(ply, FL_ANIMDUCKING)) then return false end

	if (Length2D(velocity) > 0.25) then
		ply_tb.CalcIdeal = ACT_MP_CROUCHWALK
	else
		ply_tb.CalcIdeal = ACT_MP_CROUCH_IDLE
	end

	return true

end

local InVehicle = reg.Player.InVehicle
local AnimResetGestureSlot = reg.Player.AnimResetGestureSlot
local AnimRestartGesture = reg.Player.AnimRestartGesture
local SetIK = reg.Entity.SetIK
function GAMEMODE:HandlePlayerNoClipping(ply, velocity, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (GetMoveType(ply) != MOVETYPE_NOCLIP or InVehicle(ply)) then

		if (ply_tb.m_bWasNoclipping) then

			ply_tb.m_bWasNoclipping = nil
			AnimResetGestureSlot(ply, GESTURE_SLOT_CUSTOM)
			if (CLIENT) then SetIK(ply, true) end

		end

		return

	end

	if (!ply_tb.m_bWasNoclipping) then

		AnimRestartGesture(ply, GESTURE_SLOT_CUSTOM, ACT_GMOD_NOCLIP_LAYER, false)
		if (CLIENT) then SetIK(ply, false) end

	end

	return true

end

local Length = reg.Vector.LengthSqr
function GAMEMODE:HandlePlayerVaulting(ply, velocity, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (Length(velocity) < 1000000) then return end
	if (OnGround(ply)) then return end

	ply_tb.CalcIdeal = ACT_MP_SWIM

	return true

end

function GAMEMODE:HandlePlayerSwimming(ply, velocity, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (WaterLevel(ply) < 2 or OnGround(ply)) then
		ply_tb.m_bInSwim = false
		return false
	end

	ply_tb.CalcIdeal = ACT_MP_SWIM
	ply_tb.m_bInSwim = true

	return true

end

function GAMEMODE:HandlePlayerLanding(ply, velocity, WasOnGround, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	if (GetMoveType(ply) == MOVETYPE_NOCLIP) then return end

	if (OnGround(ply) and !WasOnGround) then
		AnimRestartGesture(ply, GESTURE_SLOT_JUMP, ACT_LAND, true)
	end

end

local GetVehicle = reg.Player.GetVehicle
local GetParent = reg.Entity.GetParent
local IsValid = IsValid
local GetClass = reg.Entity.GetClass
local LookupSequence = reg.Entity.LookupSequence
local GetAllowWeaponsInVehicle = reg.Player.GetAllowWeaponsInVehicle
local GetActiveWeapon = reg.Player.GetActiveWeapon
local GetHoldType = reg.Weapon.GetHoldType
function GAMEMODE:HandlePlayerDriving(ply, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	-- The player must have a parent to be in a vehicle. If there's no parent, we are in the exit anim, so don't do sitting in 3rd person anymore
	if (!InVehicle(ply) or !IsValid(GetParent(ply))) then return false end

	local pVehicle = GetVehicle(ply)

	local pVehicle_tb = GetTable(pVehicle)

	if (!pVehicle_tb.HandleAnimation and pVehicle_tb.GetVehicleClass) then
		local c = pVehicle:GetVehicleClass()
		local t = list.Get("Vehicles")[c]
		if (t and t.Members and t.Members.HandleAnimation) then
			pVehicle_tb.HandleAnimation = t.Members.HandleAnimation
		else
			pVehicle_tb.HandleAnimation = true -- Prevent this if block from trying to assign HandleAnimation again.
		end
	end

	if (isfunction(pVehicle_tb.HandleAnimation)) then
		local seq = pVehicle:HandleAnimation(ply)
		if (seq != nil) then
			ply_tb.CalcSeqOverride = seq
		end
	end

	if (ply_tb.CalcSeqOverride == -1) then -- pVehicle.HandleAnimation did not give us an animation
		local class = GetClass(pVehicle)
		if (class == "prop_vehicle_jeep") then
			ply_tb.CalcSeqOverride = LookupSequence(ply, "drive_jeep")
		elseif (class == "prop_vehicle_airboat") then
			ply_tb.CalcSeqOverride = LookupSequence(ply, "drive_airboat")
		elseif (class == "prop_vehicle_prisoner_pod" and pVehicle:GetModel() == "models/vehicles/prisoner_pod_inner.mdl") then
			-- HACK!!
			ply_tb.CalcSeqOverride = LookupSequence(ply, "drive_pd")
		else
			ply_tb.CalcSeqOverride = LookupSequence(ply, "sit_rollercoaster")
		end
	end

	local use_anims = (ply_tb.CalcSeqOverride == LookupSequence(ply, "sit_rollercoaster") or ply_tb.CalcSeqOverride == LookupSequence(ply, "sit"))
	if (use_anims and GetAllowWeaponsInVehicle(ply) and IsValid(GetActiveWeapon(ply))) then
		local holdtype = GetHoldType(GetActiveWeapon(ply))
		if (holdtype == "smg") then holdtype = "smg1" end

		local seqid = LookupSequence(ply, "sit_" .. holdtype)
		if (seqid != -1) then
			ply_tb.CalcSeqOverride = seqid
		end
	end

	return true
end

local SetPlaybackRate = reg.Entity.SetPlaybackRate
local CLIENT = CLIENT
local GetVelocity = reg.Entity.GetVelocity
local GetPoseParameter = reg.Entity.GetPoseParameter
local SetPoseParameter = reg.Entity.SetPoseParameter

local Length_Slow = reg.Vector.Length

local math = math
local math_min = math.min
local math_max = math.max

--[[---------------------------------------------------------
   Name: gamemode:UpdateAnimation()
   Desc: Animation updates (pose params etc) should be done here
-----------------------------------------------------------]]
function GAMEMODE:UpdateAnimation(ply, velocity, maxseqgroundspeed)

	local len = Length_Slow(velocity)
	local movement = 1

	if (len > 0.2) then
		movement = len / maxseqgroundspeed
	end

	local rate = math_min(movement, 2)

	-- if we're under water we want to constantly be swimming..
	if (WaterLevel(ply) >= 2) then
		rate = math_max(rate, 0.5)
	elseif (!OnGround(ply) and len >= 1000) then
		rate = 0.1
	end

	SetPlaybackRate(ply, rate)

	-- We only need to do this clientside..
	if (CLIENT) then
		if (InVehicle(ply)) then
			--
			-- This is used for the 'rollercoaster' arms
			--
			local Vehicle = GetVehicle(ply)
			local Velocity = GetVelocity(Vehicle)
			local fwd = Vehicle:GetUp()
			local dp = fwd:Dot(Vector(0, 0, 1))

			SetPoseParameter(ply, "vertical_velocity", (dp < 0 and dp or 0) + fwd:Dot(Velocity) * 0.005)

			-- Pass the vehicles steer param down to the player
			local steer = GetPoseParameter(Vehicle, "vehicle_steer")
			steer = steer * 2 - 1 -- convert from 0..1 to -1..1
			if (Vehicle:GetClass() == "prop_vehicle_prisoner_pod") then
				steer = 0
				SetPoseParameter(ply, "aim_yaw", math.NormalizeAngle(ply:GetAimVector():Angle().y - Vehicle:GetAngles().y - 90))
			end
			SetPoseParameter(ply, "vehicle_steer", steer)

		end
		GAMEMODE:GrabEarAnimation(ply, ply_tb)
		GAMEMODE:MouthMoveAnimation(ply, ply_tb)
	end

end

local IsPlayingTaunt = reg.Player.IsPlayingTaunt
local IsTyping = reg.Player.IsTyping
local AnimSetGestureWeight = reg.Player.AnimSetGestureWeight

--
-- If you don't want the player to grab his ear in your gamemode then
-- just override this.
--
function GAMEMODE:GrabEarAnimation(ply, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	ply_tb.ChatGestureWeight = ply_tb.ChatGestureWeight or 0

	-- Don't show this when we're playing a taunt!
	if (IsPlayingTaunt(ply)) then return end

	if (IsTyping(ply)) then
		ply_tb.ChatGestureWeight = math.Approach(ply_tb.ChatGestureWeight, 1, FrameTime() * 5.0)
	else
		ply_tb.ChatGestureWeight = math.Approach(ply_tb.ChatGestureWeight, 0, FrameTime() * 5.0)
	end

	if (ply_tb.ChatGestureWeight > 0) then

		AnimRestartGesture(ply, GESTURE_SLOT_VCD, ACT_GMOD_IN_CHAT, true)
		AnimSetGestureWeight(ply, GESTURE_SLOT_VCD, ply_tb.ChatGestureWeight)

	end

end

local GetFlexIDByName = reg.Entity.GetFlexIDByName
local IsSpeaking = reg.Player.IsSpeaking
local VoiceVolume = reg.Player.VoiceVolume
local SetFlexWeight = reg.Entity.SetFlexWeight

local math_Clamp = math.Clamp

--
-- Moves the mouth when talking on voicecom
--
function GAMEMODE:MouthMoveAnimation(ply, ply_tb)

	ply_tb = ply_tb or GetTable(ply)

	local flexes = {
		GetFlexIDByName(ply, "jaw_drop"),
		GetFlexIDByName(ply, "left_part"),
		GetFlexIDByName(ply, "right_part"),
		GetFlexIDByName(ply, "left_mouth_drop"),
		GetFlexIDByName(ply, "right_mouth_drop")
	}

	local weight = IsSpeaking(ply) and math_Clamp(VoiceVolume(ply) * 2, 0, 2) or 0

	for k, v in pairs(flexes) do

		SetFlexWeight(ply, v, weight)

	end

end

function GAMEMODE:CalcMainActivity(ply, velocity)

	local ply_tb = GetTable(ply)

	ply_tb.CalcIdeal = ACT_MP_STAND_IDLE
	ply_tb.CalcSeqOverride = -1

	self:HandlePlayerLanding(ply, velocity, ply_tb.m_bWasOnGround, ply_tb)

	if !(self:HandlePlayerNoClipping(ply, velocity, ply_tb) or
		self:HandlePlayerDriving(ply, ply_tb) or
		self:HandlePlayerVaulting(ply, velocity, ply_tb) or
		self:HandlePlayerJumping(ply, velocity, ply_tb) or
		self:HandlePlayerSwimming(ply, velocity, ply_tb) or
		self:HandlePlayerDucking(ply, velocity, ply_tb)) then

		local len2d = Length2D(velocity)
		if (len2d > 22500) then ply_tb.CalcIdeal = ACT_MP_RUN elseif (len2d > 0.25) then ply_tb.CalcIdeal = ACT_MP_WALK end

	end

	ply_tb.m_bWasOnGround = OnGround(ply)
	ply_tb.m_bWasNoclipping = (GetMoveType(ply) == MOVETYPE_NOCLIP and !InVehicle(ply))

	return ply_tb.CalcIdeal, ply_tb.CalcSeqOverride

end

local TranslateWeaponActivity = reg.Player.TranslateWeaponActivity

local IdleActivity = ACT_HL2MP_IDLE
local IdleActivityTranslate = {}
IdleActivityTranslate[ACT_MP_STAND_IDLE] = IdleActivity
IdleActivityTranslate[ACT_MP_WALK] = IdleActivity + 1
IdleActivityTranslate[ACT_MP_RUN] = IdleActivity + 2
IdleActivityTranslate[ACT_MP_CROUCH_IDLE] = IdleActivity + 3
IdleActivityTranslate[ACT_MP_CROUCHWALK] = IdleActivity + 4
IdleActivityTranslate[ACT_MP_ATTACK_STAND_PRIMARYFIRE] = IdleActivity + 5
IdleActivityTranslate[ACT_MP_ATTACK_CROUCH_PRIMARYFIRE]	= IdleActivity + 5
IdleActivityTranslate[ACT_MP_RELOAD_STAND] = IdleActivity + 6
IdleActivityTranslate[ACT_MP_RELOAD_CROUCH] = IdleActivity + 6
IdleActivityTranslate[ACT_MP_JUMP] = ACT_HL2MP_JUMP_SLAM
IdleActivityTranslate[ACT_MP_SWIM] = IdleActivity + 9
IdleActivityTranslate[ACT_LAND] = ACT_LAND

-- it is preferred you return ACT_MP_* in CalcMainActivity, and if you have a specific need to not tranlsate through the weapon do it here
function GAMEMODE:TranslateActivity(ply, act)

	local newact = TranslateWeaponActivity(ply, act)

	-- select idle anims if the weapon didn't decide
	if (act == newact) then
		return IdleActivityTranslate[act]
	end

	return newact

end

function GAMEMODE:DoAnimationEvent(ply, event, data)

	if (event == PLAYERANIMEVENT_ATTACK_PRIMARY) then

		if IsFlagSet(ply, FL_ANIMDUCKING) then
			AnimRestartGesture(ply, GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, true)
		else
			AnimRestartGesture(ply, GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_ATTACK_STAND_PRIMARYFIRE, true)
		end

		return ACT_VM_PRIMARYATTACK

	elseif (event == PLAYERANIMEVENT_ATTACK_SECONDARY) then

		-- there is no gesture, so just fire off the VM event
		return ACT_VM_SECONDARYATTACK

	elseif (event == PLAYERANIMEVENT_RELOAD) then

		if IsFlagSet(ply, FL_ANIMDUCKING) then
			AnimRestartGesture(ply, GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_RELOAD_CROUCH, true)
		else
			AnimRestartGesture(ply, GESTURE_SLOT_ATTACK_AND_RELOAD, ACT_MP_RELOAD_STAND, true)
		end

		return ACT_INVALID

	elseif (event == PLAYERANIMEVENT_JUMP) then

		local ply_tb = GetTable(ply)

		ply_tb.m_bJumping = true
		ply_tb.m_bFirstJumpFrame = true
		ply_tb.m_flJumpStartTime = CurTime()

		AnimRestartMainSequence(ply)

		return ACT_INVALID

	elseif (event == PLAYERANIMEVENT_CANCEL_RELOAD) then

		AnimResetGestureSlot(ply, GESTURE_SLOT_ATTACK_AND_RELOAD)

		return ACT_INVALID
	end

end

print("[MG Animations Optimisations] Included anim overrides for better performance!")
end)