local vector_origin = Vector()
local angle_zero = Angle()

local Vector = Vector
local Angle = Angle
local math = math
local LerpVector = LerpVector

local sv_cheats = GetConVar("sv_cheats")
local host_timescale = GetConVar("host_timescale")

local cv_fov = GetConVar("fov_desired")
local cl_vm_nearwall = GetConVar("cl_tfa_viewmodel_nearwall")

local cl_tfa_viewmodel_offset_x = GetConVar("cl_tfa_viewmodel_offset_x")
local cl_tfa_viewmodel_offset_y = GetConVar("cl_tfa_viewmodel_offset_y")
local cl_tfa_viewmodel_offset_z = GetConVar("cl_tfa_viewmodel_offset_z")
local cl_tfa_viewmodel_centered = GetConVar("cl_tfa_viewmodel_centered")

local cl_tfa_viewmodel_vp_enabled = GetConVar("cl_tfa_viewmodel_vp_enabled")
local cl_tfa_viewmodel_vp_pitch = GetConVar("cl_tfa_viewmodel_vp_pitch")
local cl_tfa_viewmodel_vp_pitch_is = GetConVar("cl_tfa_viewmodel_vp_pitch_is")
local cl_tfa_viewmodel_vp_vertical = GetConVar("cl_tfa_viewmodel_vp_vertical")
local cl_tfa_viewmodel_vp_vertical_is = GetConVar("cl_tfa_viewmodel_vp_vertical_is")
local cl_tfa_viewmodel_vp_max_vertical = GetConVar("cl_tfa_viewmodel_vp_max_vertical")
local cl_tfa_viewmodel_vp_max_vertical_is = GetConVar("cl_tfa_viewmodel_vp_max_vertical_is")
local cl_tfa_viewmodel_vp_yaw = GetConVar("cl_tfa_viewmodel_vp_yaw")
local cl_tfa_viewmodel_vp_yaw_is = GetConVar("cl_tfa_viewmodel_vp_yaw_is")

local sv_tfa_recoil_legacy = GetConVar("sv_tfa_recoil_legacy")

local cv_customgunbob = GetConVar("cl_tfa_gunbob_custom")

local function Lerp(t, a, b)
	return a + (b - a) * t
end

local function Clamp(a, b, c)
	if a < b then return b end
	if a > c then return c end
	return a
end

local function GetClampedCVarFloat(cvar)
	return Clamp(cvar:GetFloat(), cvar:GetMin(), cvar:GetMax())
end

local math_max = math.max

local cl_vm_flip_cv = GetConVar("cl_tfa_viewmodel_flip")
local fovmod_add = GetConVar("cl_tfa_viewmodel_offset_fov")
local fovmod_mult = GetConVar("cl_tfa_viewmodel_multiplier_fov")

function SWEP:AirWalkScale()
	return (self:OwnerIsValid() and self:GetOwner():IsOnGround()) and 1 or 0.2
end

SWEP.OldPos = Vector(0, 0, 0)
SWEP.OldAng = Angle(0, 0, 0)

function SWEP:GetViewModelPosition(opos, oang, ...)
	local self2 = self:GetTable()

	if not self2.pos_cached then return opos, oang end

	local npos, nang = opos * 1, oang * 1

	nang:RotateAroundAxis(nang:Right(), self2.ang_cached.p)
	nang:RotateAroundAxis(nang:Up(), self2.ang_cached.y)
	nang:RotateAroundAxis(nang:Forward(), self2.ang_cached.r)
	npos:Add(nang:Right() * self2.pos_cached.x)
	npos:Add(nang:Forward() * self2.pos_cached.y)
	npos:Add(nang:Up() * self2.pos_cached.z)

	if cv_customgunbob:GetBool() then
		npos, nang = self:Sway(npos, nang)
		npos, nang = self:SprintBob(npos, nang, Lerp(self2.SprintProgressUnpredicted3 or self2.SprintProgressUnpredicted or self:GetSprintProgress(), 0, self2.SprintBobMult))
	end

	local pos, ang = self2.SightsAttPos, Angle(self2.SightsAttAng)
	if not pos or not ang then return npos, nang end

	local ofpos, ofang = WorldToLocal(npos, nang, opos, oang)

	self2.OldPos = npos
	self2.OldAng = nang

	if self.IronSightsProgressUnpredicted > 0.005 then
		local _opos, _oang = opos * 1, oang * 1

		-- tfa base vm offset
		local right, up, fwd = _oang:Right(), _oang:Up(), _oang:Forward()

		_opos = _opos - ofpos.y * right + ofpos.x * fwd + ofpos.z * up
		_oang:RotateAroundAxis(fwd, ofang.r)
		_oang:RotateAroundAxis(right, -ofang.p)
		_oang:RotateAroundAxis(up, ofang.y)

		-- sight offset

		_oang:RotateAroundAxis(_oang:Forward(), -ang.r)
		_oang:RotateAroundAxis(_oang:Right(), ang.p)
		_oang:RotateAroundAxis(_oang:Up(), -ang.y)

		right, up, fwd = _oang:Right(), _oang:Up(), _oang:Forward()

		_opos = _opos - pos.x * fwd + pos.y * right - pos.z * up

		self2.OldPos = LerpVector(self2.IronSightsProgressUnpredicted, npos, _opos)
		self2.OldAng = LerpAngle(self2.IronSightsProgressUnpredicted, nang, _oang)
	end

	return self2.OldPos, self2.OldAng
end

function SWEP:CalculateViewModelFlip()
	local self2 = self:GetTable()

	if self2.ViewModelFlipDefault == nil then
		self2.ViewModelFlipDefault = self2.ViewModelFlip
	end

	local righthanded = true

	if cl_vm_flip_cv:GetBool() then
		righthanded = false
	end

	local shouldflip = self2.ViewModelFlipDefault

	if not righthanded then
		shouldflip = not self2.ViewModelFlipDefault
	end

	if self2.ViewModelFlip ~= shouldflip then
		self2.ViewModelFlip = shouldflip
	end

	self2.ViewModelFOV_OG = self2.ViewModelFOV_OG or self2.ViewModelFOV

	local cam_fov = self2.LastTranslatedFOV or cv_fov:GetInt() or 90
	local iron_add = cam_fov * (1 - 90 / cam_fov) * math.max(1 - self2.GetStatL(self, "Secondary.OwnerFOV", 90) / 90, 0)

	local ironSightsProgress = TFA.Cosine(self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress())
	self2.ViewModelFOV = Lerp(ironSightsProgress, self2.ViewModelFOV_OG, self2.GetStatL(self, "Secondary.ViewModelFOV", self2.ViewModelFOV_OG)) * GetClampedCVarFloat(fovmod_mult) + GetClampedCVarFloat(fovmod_add) + iron_add * ironSightsProgress
end

function SWEP:UpdateWeaponLength()
	local self2 = self:GetTable()
	if not self2.VMIV(self) then return end
	local vm = self2.OwnerViewModel
	local mzpos = self:GetMuzzlePos()
	if not mzpos then return end
	if not mzpos.Pos then return end
	if GetViewEntity and GetViewEntity() ~= self:GetOwner() then return end
	local mzVec = vm:WorldToLocal(mzpos.Pos)
	self2.WeaponLength = math.abs(mzVec.x)
end

function SWEP:CalculateNearWall(p, a)
	local self2 = self:GetTable()
	if not self:OwnerIsValid() then return p, a end

	if not cl_vm_nearwall:GetBool() then return p, a end

	local ply = self:GetOwner()

	local sp = ply:GetShootPos()
	local ea = ply:EyeAngles()
	local et = util.QuickTrace(sp,ea:Forward()*128,{self,ply})--self:GetOwner():GetEyeTrace()
	local dist = et.HitPos:Distance(sp)

	if dist<1 then
		et=util.QuickTrace(sp,ea:Forward()*128,{self,ply,et.Entity})
		dist = et.HitPos:Distance(sp)
	end

	self:UpdateWeaponLength()

	local nw_offset_vec = LerpVector(self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress(), self2.NearWallVector, self2.NearWallVectorADS)
	local off = self2.WeaponLength - dist
	self2.LastNearWallOffset = self2.LastNearWallOffset or 0

	local ft = RealFrameTime() * game.GetTimeScale() * (sv_cheats:GetBool() and host_timescale:GetFloat() or 1)

	if off > self2.LastNearWallOffset then
		self2.LastNearWallOffset = math.min(self2.LastNearWallOffset + math.max(ft * 66, off * 0.1), off, 34)
	elseif off < self2.LastNearWallOffset then
		self2.LastNearWallOffset = math.max(self2.LastNearWallOffset - ft * 66, off, 0)
	end

	off = TFA.Cosine(self2.LastNearWallOffset / 34) * 34

	if off > 0 then
		p = p + nw_offset_vec * off / 2
		local posCompensated = sp * 1
		posCompensated:Add(ea:Right() * nw_offset_vec.x * off / 2 * (self2.ViewModelFlip and -1 or 1))
		posCompensated:Add(ea:Forward() * nw_offset_vec.y * off / 2)
		posCompensated:Add(ea:Up() * nw_offset_vec.z * off / 2)
		local angleComp = (et.HitPos - posCompensated):Angle()
		a.x = a.x - math.AngleDifference(angleComp.p, ea.p) / 2
		a.y = a.y + math.AngleDifference(angleComp.y, ea.y) / 2
	end

	return p, a
end

local centered_sprintpos = Vector(0, -1, 1)
local centered_sprintang = Vector(-15, 0, 0)

local bezierVectorBuffer = {}

local function bezierVector(t, vec1, vec2, vec3)
	local _1, _2 = vec1.x, vec3.x
	bezierVectorBuffer[1] = _1
	bezierVectorBuffer[2] = _1
	bezierVectorBuffer[3] = _1
	bezierVectorBuffer[4] = _1
	bezierVectorBuffer[5] = vec2.x
	bezierVectorBuffer[6] = _2
	bezierVectorBuffer[7] = _2
	bezierVectorBuffer[8] = _2
	bezierVectorBuffer[9] = _2

	local x = TFA.tbezier(t, bezierVectorBuffer)

	_1, _2 = vec1.y, vec3.y
	bezierVectorBuffer[1] = _1
	bezierVectorBuffer[2] = _1
	bezierVectorBuffer[3] = _1
	bezierVectorBuffer[4] = _1
	bezierVectorBuffer[5] = vec2.y
	bezierVectorBuffer[6] = _2
	bezierVectorBuffer[7] = _2
	bezierVectorBuffer[8] = _2
	bezierVectorBuffer[9] = _2

	local y = TFA.tbezier(t, bezierVectorBuffer)

	_1, _2 = vec1.z, vec3.z
	bezierVectorBuffer[1] = _1
	bezierVectorBuffer[2] = _1
	bezierVectorBuffer[3] = _1
	bezierVectorBuffer[4] = _1
	bezierVectorBuffer[5] = vec2.z
	bezierVectorBuffer[6] = _2
	bezierVectorBuffer[7] = _2
	bezierVectorBuffer[8] = _2
	bezierVectorBuffer[9] = _2

	local z = TFA.tbezier(t, bezierVectorBuffer)

	return Vector(x, y, z)
end

function SWEP:CalculateViewModelOffset(delta)
	local self2 = self:GetTable()

	local target_pos, target_ang
	local additivePos = self2.GetStatL(self, "AdditiveViewModelPosition")

	if additivePos then
		target_pos, target_ang = Vector(), Vector()
	else
		target_pos = Vector(self2.GetStatL(self, "ViewModelPosition"))
		target_ang = Vector(self2.GetStatL(self, "ViewModelAngle"))
	end

	local CenteredViewModelPosition = self2.GetStatL(self, "CenteredViewModelPosition")
	local CenteredViewModelAngle = self2.GetStatL(self, "CenteredViewModelAngle")
	local IronSightsPosition = self2.GetStatL(self, "IronSightsPosition", self2.SightsPos)
	local IronSightsAngle = self2.GetStatL(self, "IronSightsAngle", self2.SightsAng)

	local targetPosCenter, targetAngCenter

	if CenteredViewModelPosition then
		targetPosCenter = Vector(CenteredViewModelPosition)

		if CenteredViewModelAngle then
			targetAngCenter = Vector(CenteredViewModelAngle)
		end
	elseif IronSightsPosition then
		targetPosCenter = Vector((self2.IronSightsPositionCurrent or IronSightsPosition).x, target_pos.y, target_pos.z - 3)

		if IronSightsAngle then
			targetAngCenter = Vector(0, (self2.IronSightsAngleCurrent or IronSightsAngle).y, 0)
		end
	else
		targetPosCenter, targetAngCenter = target_pos, target_ang
	end

	local stat = self:GetStatus()

	local holsterStatus = TFA.Enum.HolsterStatus[stat] and self2.GetStatL(self, "ProceduralHolsterEnabled")
	local proceduralReloadStatus = TFA.Enum.ReloadStatus[stat] and self2.GetStatL(self, "IsProceduralReloadBased")
	local holsterProgress = 0
	local statusProgress = self:GetStatusProgress()

	if proceduralReloadStatus then
		holsterProgress = TFA.Quintic(Clamp((statusProgress >= 0.5 and (2 - statusProgress * 2) or (statusProgress * 2)), 0, 1))
	elseif self2.GetStatL(self, "ProceduralHolsterEnabled") then
		if TFA.Enum.HolsterStatusFinal[stat] then
			holsterProgress = 1
		elseif TFA.Enum.HolsterStatus[stat] then
			holsterProgress = TFA.Quintic(Clamp(statusProgress * 1.1, 0, 1))
		end
	end

	local sprintAnimAllowed = self2.GetStatL(self, "Sprint_Mode") ~= TFA.Enum.LOCOMOTION_ANI

	local ironSights = self:GetIronSights()
	local sprintProgress = sprintAnimAllowed and TFA.Cubic(self2.SprintProgressUnpredicted2 or self2.SprintProgressUnpredicted or self:GetSprintProgress()) or 0
	local safetyProgress = Lerp(sprintProgress, TFA.Cubic(self2.SafetyProgressUnpredicted or 0), 0)

	local ironSightsProgress = Clamp(
		Lerp(
			math_max(holsterProgress, sprintProgress, safetyProgress),
			TFA.Cubic(self2.IronSightsProgressUnpredicted2 or self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress()),
			0)
		, 0, 1)

	--local ironSightsProgress = TFA.tbezier(self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress(), IRON_SIGHTS_BEZIER)

	local crouchRatio = Lerp(math_max(ironSightsProgress, holsterProgress, Clamp(sprintProgress * 2, 0, 1), safetyProgress), TFA.Quintic(self2.CrouchingRatioUnpredicted or self:GetCrouchingRatio()), 0)

	if crouchRatio > 0.01 then
		target_pos = LerpVector(crouchRatio, target_pos, self2.GetStatL(self, "CrouchViewModelPosition"))
		target_ang = LerpVector(crouchRatio, target_ang, self2.GetStatL(self, "CrouchViewModelAngle"))
	end

	local isCentered = cl_tfa_viewmodel_centered:GetBool()
	if isCentered then
		target_pos:Set(targetPosCenter)
		target_ang:Set(targetAngCenter)
	end

	if holsterStatus or proceduralReloadStatus then
		local targetHolsterPos = Vector(self2.GetStatL(self, "ProceduralHolsterPosition"))
		local targetHolsterAng = Vector(self2.GetStatL(self, "ProceduralHolsterAngle"))

		if self2.ViewModelFlip then
			targetHolsterPos.x = -targetHolsterPos.x

			targetHolsterAng.y = -targetHolsterAng.y
			targetHolsterAng.z = -targetHolsterAng.z
		end

		target_pos = LerpVector(holsterProgress, target_pos, targetHolsterPos)
		target_ang = LerpVector(holsterProgress, target_ang, targetHolsterAng)
	end

	if
		(sprintProgress > 0.01 or safetyProgress > 0.01) and
		(sprintAnimAllowed and sprintProgress > 0.01 or safetyProgress > 0.01)
		and not TFA.Enum.BashStatus[stat]
	then
		local add_pos = isCentered and centered_sprintpos or vector_origin
		local add_ang = isCentered and centered_sprintang or vector_origin

		local sprint_pos = self2.GetStatL(self, "SprintViewModelPosition")
		local sprint_ang = self2.GetStatL(self, "SprintViewModelAngle")

		target_pos = LerpVector(safetyProgress, target_pos, self2.GetStatL(self, "SafetyPos", sprint_pos) + add_pos)
		target_ang = LerpVector(safetyProgress, target_ang, self2.GetStatL(self, "SafetyAng", sprint_ang) + add_ang)

		if sprintAnimAllowed then
			target_pos = LerpVector(sprintProgress, target_pos, sprint_pos + add_pos)
			target_ang = LerpVector(sprintProgress, target_ang, sprint_ang + add_ang)
		end
	end

	if ironSightsProgress > 0.02 and self2.GetStatL(self, "Sights_Mode") ~= TFA.Enum.LOCOMOTION_ANI then
		local score = self2.VM_IronPositionScore or 1
		local getSightsPos = self2.IronSightsPositionCurrent or IronSightsPosition or self2.GetStatL(self, "SightsPos", vector_origin)

		if targetPosCenter and score > 0.04 then
			target_pos = bezierVector(ironSightsProgress, target_pos, LerpVector(score, getSightsPos, targetPosCenter), getSightsPos)
		else
			target_pos = LerpVector(ironSightsProgress, target_pos, getSightsPos)
		end

		if targetAngCenter and score > 0.04 then
			local deviate = 30 * score

			if self2.VM_IsScopedIn then
				deviate = -deviate
			end

			if self2.ViewModelFlip then
				deviate = -deviate
			end

			local targetAngCenter2 = Vector(targetAngCenter.x * score, targetAngCenter.y * score, targetAngCenter.z * score + deviate)
			target_ang = bezierVector(ironSightsProgress, target_ang, targetAngCenter2, self2.IronSightsAngleCurrent or IronSightsAngle or self2.GetStatL(self, "SightsAng", vector_origin))
		else
			target_ang = LerpVector(ironSightsProgress, target_ang, self2.IronSightsAngleCurrent or IronSightsAngle or self2.GetStatL(self, "SightsAng", vector_origin))
		end
	end

	target_pos.x = target_pos.x + GetClampedCVarFloat(cl_tfa_viewmodel_offset_x) * (1 - ironSightsProgress)
	target_pos.y = target_pos.y + GetClampedCVarFloat(cl_tfa_viewmodel_offset_y) * (1 - ironSightsProgress)
	target_pos.z = target_pos.z + GetClampedCVarFloat(cl_tfa_viewmodel_offset_z) * (1 - ironSightsProgress)

	local customizationProgress = TFA.Quintic(self2.CustomizingProgressUnpredicted or self:GetInspectingProgress())

	if customizationProgress > 0.01 and self2.GetStatL(self, "Customize_Mode") ~= TFA.Enum.LOCOMOTION_ANI then
		if not self2.InspectPos then
			self2.InspectPos = Vector(self2.InspectPosDef)

			if self2.ViewModelFlip then
				self2.InspectPos.x = self2.InspectPos.x * -1
			end
		end

		if not self2.InspectAng then
			self2.InspectAng = Vector(self2.InspectAngDef)

			if self2.ViewModelFlip then
				self2.InspectAng.y = self2.InspectAngDef.y * -1
				self2.InspectAng.z = self2.InspectAngDef.z * -1
			end
		end

		target_pos = LerpVector(customizationProgress, target_pos, self2.GetStatL(self, "InspectPos"))
		target_ang = LerpVector(customizationProgress, target_ang, self2.GetStatL(self, "InspectAng"))
	end

	target_pos, target_ang = self:CalculateNearWall(target_pos, target_ang)

	if additivePos then
		target_pos:Add(self2.GetStatL(self, "ViewModelPosition"))
		target_ang:Add(self2.GetStatL(self, "ViewModelAngle"))
	end

	target_ang.z = target_ang.z + -7.5 * (1 - math.abs(0.5 - ironSightsProgress) * 2) * (self:GetIronSights() and 1 or 0.5) * (self2.ViewModelFlip and 1 or -1) * (self2.VM_IronPositionScore or 1)

	if self:GetHidden() then
		target_pos.z = target_pos.z - 5
	end

	if self2.GetStatL(self, "BlowbackEnabled") and self2.BlowbackCurrentRoot > 0.01 then
		local bbvec = self2.GetStatL(self, "BlowbackVector")
		target_pos = target_pos + bbvec * self2.BlowbackCurrentRoot
		local bbang = self2.GetStatL(self, "BlowbackAngle") or angle_zero
		bbvec = bbvec * 1
		bbvec.x = bbang.p
		bbvec.y = bbang.y
		bbvec.z = bbang.r
		target_ang = target_ang + bbvec * self2.BlowbackCurrentRoot
		bbang = self2.BlowbackRandomAngle * (1 - math.max(0, ironSightsProgress) * .8)
		bbvec.x = bbang.p
		bbvec.y = bbang.y
		bbvec.z = bbang.r
		target_ang = target_ang + bbvec * self2.BlowbackCurrentRoot
	end

	if not sv_tfa_recoil_legacy:GetBool() and cl_tfa_viewmodel_vp_enabled:GetBool() then
		if self:HasRecoilLUT() then
			if not ironSights then
				local ang = self:GetRecoilLUTAngle()

				target_ang.x = target_ang.x - ang.p / 2 * Lerp(ironSightsProgress, self:GetStatL("ViewModelPunchPitchMultiplier") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_pitch), self:GetStatL("ViewModelPunchPitchMultiplier_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_pitch_is))
				target_ang.y = target_ang.y + ang.y / 2 * Lerp(ironSightsProgress, self:GetStatL("ViewModelPunchYawMultiplier") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_yaw), self:GetStatL("ViewModelPunchYawMultiplier_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_yaw_is))
			end
		else
			target_ang.x = target_ang.x - self:GetViewPunchP() * Lerp(ironSightsProgress, self:GetStatL("ViewModelPunchPitchMultiplier") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_pitch), self:GetStatL("ViewModelPunchPitchMultiplier_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_pitch_is))
			target_ang.y = target_ang.y + self:GetViewPunchY() * Lerp(ironSightsProgress, self:GetStatL("ViewModelPunchYawMultiplier") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_yaw), self:GetStatL("ViewModelPunchYawMultiplier_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_yaw_is))

			local ViewModelPunch_MaxVertialOffset = Lerp(ironSightsProgress, self:GetStatL("ViewModelPunch_MaxVertialOffset") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_max_vertical), self:GetStatL("ViewModelPunch_MaxVertialOffset_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_max_vertical_is))

			target_pos.y = target_pos.y + math.Clamp(
				self:GetViewPunchP() * Lerp(ironSightsProgress, self:GetStatL("ViewModelPunch_VertialMultiplier") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_vertical), self:GetStatL("ViewModelPunch_VertialMultiplier_IronSights") * GetClampedCVarFloat(cl_tfa_viewmodel_vp_vertical_is)),
				-ViewModelPunch_MaxVertialOffset,
				ViewModelPunch_MaxVertialOffset)
		end
	end

	if not cv_customgunbob:GetBool() then
		self2.pos_cached, self2.ang_cached = Vector(target_pos), Angle(target_ang.x, target_ang.y, target_ang.z)

		return
	end

	local intensityWalk = math.min(self:GetOwner():GetVelocity():Length2D() / self:GetOwner():GetWalkSpeed(), 1) * Lerp(ironSightsProgress, self2.WalkBobMult, self2.WalkBobMult_Iron or self2.WalkBobMult)
	local intensityBreath = Lerp(ironSightsProgress, self2.GetStatL(self, "BreathScale", 0.2), self2.GetStatL(self, "IronBobMultWalk", 0.5) * intensityWalk)
	intensityWalk = (1 - ironSightsProgress) * intensityWalk
	local intensityRun = Lerp(self2.SprintProgressUnpredicted3 or self2.SprintProgressUnpredicted or self:GetSprintProgress(), 0, self2.SprintBobMult)
	local velocity = math.max(self:GetOwner():GetVelocity():Length2D() * self:AirWalkScale() - self:GetOwner():GetVelocity().z * 0.5, 0)
	local rate = math.min(math.max(0.15, math.sqrt(velocity / self:GetOwner():GetRunSpeed()) * 1.75), self:GetSprinting() and 5 or 3)

	self2.pos_cached, self2.ang_cached = self:WalkBob(
		target_pos,
		Angle(target_ang.x, target_ang.y, target_ang.z),
		math.max(intensityBreath - intensityWalk - intensityRun, 0),
		math.max(intensityWalk - intensityRun, 0), rate, delta)
end

local rft, eyeAngles, viewPunch, oldEyeAngles, delta, motion, counterMotion, compensation, fac, positionCompensation, swayRate, wiggleFactor, flipFactor

local gunswaycvar = GetConVar("cl_tfa_gunbob_intensity")
local gunswayinvertcvar = GetConVar("cl_tfa_gunbob_invertsway")
local sv_tfa_weapon_weight = GetConVar("sv_tfa_weapon_weight")

function SWEP:Sway(pos, ang, ftv)
	local self2 = self:GetTable()
	--sanity check
	if not self:OwnerIsValid() then return pos, ang end
	--convar
	fac = GetClampedCVarFloat(gunswaycvar) * 3 * ((1 - ((self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress()) or 0)) * 0.85 + 0.15)
	if gunswayinvertcvar:GetBool() then fac = -fac end
	flipFactor = (self2.ViewModelFlip and -1 or 1)
	--init vars
	delta = delta or Angle()
	motion = motion or Angle()
	counterMotion = counterMotion or Angle()
	compensation = compensation or Angle()

	if ftv then
		--grab eye angles
		eyeAngles = self:GetOwner():EyeAngles()
		viewPunch = self:GetOwner():GetViewPunchAngles()
		eyeAngles.p = eyeAngles.p - viewPunch.p
		eyeAngles.y = eyeAngles.y - viewPunch.y
		oldEyeAngles = oldEyeAngles or eyeAngles
		--calculate delta
		wiggleFactor = (1 - (sv_tfa_weapon_weight:GetBool() and self2.GetStatL(self, "RegularMoveSpeedMultiplier") or 1)) / 0.6 + 0.15
		swayRate = math.pow(sv_tfa_weapon_weight:GetBool() and self2.GetStatL(self, "RegularMoveSpeedMultiplier") or 1, 1.5) * 10
		rft = math.Clamp(ftv, 0.001, 1 / 20)
		local clampFac = 1.1 - math.min((math.abs(motion.p) + math.abs(motion.y) + math.abs(motion.r)) / 20, 1)
		delta.p = math.AngleDifference(eyeAngles.p, oldEyeAngles.p) / rft / 120 * clampFac
		delta.y = math.AngleDifference(eyeAngles.y, oldEyeAngles.y) / rft / 120 * clampFac
		delta.r = math.AngleDifference(eyeAngles.r, oldEyeAngles.r) / rft / 120 * clampFac
		oldEyeAngles = eyeAngles
		--calculate motions, based on Juckey's methods
		counterMotion = LerpAngle(rft * (swayRate * (0.75 + math.max(0, 0.5 - wiggleFactor))), counterMotion, -motion)
		compensation.p = math.AngleDifference(motion.p, -counterMotion.p)
		compensation.y = math.AngleDifference(motion.y, -counterMotion.y)
		motion = LerpAngle(rft * swayRate, motion, delta + compensation)
	end

	--modify position/angle
	positionCompensation = 0.2 + 0.2 * ((self2.IronSightsProgressUnpredicted or self:GetIronSightsProgress()) or 0)
	pos:Add(-motion.y * positionCompensation * 0.66 * fac * ang:Right() * flipFactor) --compensate position for yaw
	pos:Add(-motion.p * positionCompensation * fac * ang:Up()) --compensate position for pitch
	ang:RotateAroundAxis(ang:Right(), motion.p * fac)
	ang:RotateAroundAxis(ang:Up(), -motion.y * 0.66 * fac * flipFactor)
	ang:RotateAroundAxis(ang:Forward(), counterMotion.r * 0.5 * fac * flipFactor)

	return pos, ang
end

local mirror = Matrix()

hook.Add("PostRender", "TFA:CacheSightsPos", function()
	local self = LocalPlayer():GetActiveWeapon()
	if not IsValid(self) then return end
	local self2 = self:GetTable()
	if not self2.IsTFAWeapon then return end
	if not self2.ViewModelFlip then return end

	if not self2.VMIV(self) then return end
	local vm = self2.OwnerViewModel

	self2.ViewModelFlip = false

	vm:SetRenderOrigin(vector_origin)
	vm:SetRenderAngles(angle_zero)

	vm:InvalidateBoneCache()
	vm:SetupBones()

	local ViewModelElements = self:GetStatRaw("ViewModelElements", TFA.LatestDataVersion)

	if ViewModelElements and self2.HasInitAttachments then
		if not self2.vRenderOrder then
			self:RebuildModsRenderOrder()
		end

		TFA._IncNextSetupBones()

		for index = 1, #self2.vRenderOrder do
			local name = self2.vRenderOrder[index]
			local element = ViewModelElements[name]

			if not element then
				self:RebuildModsRenderOrder()
				break
			end

			if element.type ~= "Model" then goto CONTINUE end

			if element.hide then goto CONTINUE end
			if not element.bone then goto CONTINUE end

			if self2.GetStatL(self, "ViewModelElements." .. name .. ".active") == false then goto CONTINUE end

			local pos, ang = self:GetBoneOrientation(ViewModelElements, element, vm, nil, true)
			if not pos and not element.bonemerge then goto CONTINUE end

			self:PrecacheElement(element, true)

			local model = element.curmodel
			local sprite = element.spritemat

			if IsValid(model) then
				if not element.bonemerge then
					model:SetPos(pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z)
					ang:RotateAroundAxis(ang:Up(), element.angle.y)
					ang:RotateAroundAxis(ang:Right(), element.angle.p)
					ang:RotateAroundAxis(ang:Forward(), element.angle.r)
					model:SetAngles(ang)
					mirror:Identity()
					mirror:Scale(element.size)
					model:EnableMatrix("RenderMultiply", mirror)
				end

				if not self2.VElementsBodygroupsCache[index] then
					self2.VElementsBodygroupsCache[index] = #model:GetBodyGroups() - 1
				end

				if self2.VElementsBodygroupsCache[index] then
					for _b = 0, self2.VElementsBodygroupsCache[index] do
						local newbg = self2.GetStatL(self, "ViewModelElements." .. name .. ".bodygroup." .. _b, 0) -- names are not supported, use overridetable

						if model:GetBodygroup(_b) ~= newbg then
							model:SetBodygroup(_b, newbg)
						end
					end
				end

				if element.bonemerge then
					if element.rel and ViewModelElements[element.rel] and IsValid(ViewModelElements[element.rel].curmodel) then
						element.parModel = ViewModelElements[element.rel].curmodel
					else
						element.parModel = self2.OwnerViewModel or self
					end

					if model:GetParent() ~= element.parModel then
						model:SetParent(element.parModel)
					end

					if not model:IsEffectActive(EF_BONEMERGE) then
						model:AddEffects(EF_BONEMERGE)
						model:AddEffects(EF_BONEMERGE_FASTCULL)
						model:SetMoveType(MOVETYPE_NONE)
						model:SetLocalPos(vector_origin)
						model:SetLocalAngles(angle_zero)
					end
				elseif model:IsEffectActive(EF_BONEMERGE) then
					model:RemoveEffects(EF_BONEMERGE)
					model:SetParent(NULL)
				end

				model:InvalidateBoneCache()
				model:SetupBones()
				model.tfa_next_setup_bones = TFA._GetNextSetupBones()
			end

			::CONTINUE::
		end
	end

	self:CacheSightsPos(vm, true)

	vm:SetRenderOrigin()
	vm:SetRenderAngles()

	self.ViewModelFlip = true
	vm:InvalidateBoneCache()
end)

function SWEP:CacheSightsPos(vm, flipped)
	self.SightsAttPos, self.SightsAttAng = nil, nil

	if not self:GetStat("ProceduralSight", false) then return end

	local model = vm
	local attname = self:GetStat("ProceduralSight_VElement")

	if attname then
		if not self:GetStat("VElements." .. attname .. ".active", false) then return end

		model = self.VElements[attname].curmodel
	end

	if not IsValid(model) then return end

	local ViewModelElements = self:GetStatRaw("ViewModelElements", TFA.LatestDataVersion)

	TFA._IncNextSetupBones()

	if self:GetStat("ProceduralSight_PositionType", TFA.Enum.SIGHTSPOS_ATTACH) == TFA.Enum.SIGHTSPOS_BONE then
		local boneid = self:GetStat("ProceduralSight_Bone")
		if not boneid then return end

		if type(boneid) == "string" then
			boneid = model:LookupBone(boneid)
		end

		if not boneid or boneid < 0 then return end

		self.SightsAttPos, self.SightsAttAng = model:GetBonePosition(boneid)
	else
		local attid = self:GetStat("ProceduralSight_Attachment")
		if not attid then return end

		if type(attid) == "string" then
			attid = model:LookupAttachment(attid)
		end

		if not attid or attid <= 0 then return end

		local attpos = model:GetAttachment(attid)

		self.SightsAttPos, self.SightsAttAng = attpos.Pos, attpos.Ang
	end

	if self.SightsAttPos and self.SightsAttAng then
		if not flipped then
			local transform = Matrix()
			transform:Translate(vm:GetPos())
			transform:Rotate(vm:GetAngles())
			transform:Invert()

			transform:Translate(self.SightsAttPos)
			transform:Rotate(self.SightsAttAng)

			self.SightsAttPos, self.SightsAttAng = transform:GetTranslation(), transform:GetAngles()
		end

		local OffsetPos = self:GetStatL("ProceduralSight_OffsetPos")

		if OffsetPos then
			if GetConVarNumber("developer") > 0 then -- draw pre-offset pos
				local a, b = LocalToWorld(self.SightsAttPos, self.SightsAttAng, vm:GetPos(), vm:GetAngles())

				render.DrawLine(a, a + b:Forward() * 1, Color(127, 0, 0), false)
				render.DrawLine(a, a - b:Right() * 1, Color(0, 127, 0), false)
				render.DrawLine(a, a + b:Up() * 1, Color(0, 0, 127), false)
			end

			self.SightsAttPos:Add(self.SightsAttAng:Right() * OffsetPos.x)
			self.SightsAttPos:Add(self.SightsAttAng:Forward() * OffsetPos.y)
			self.SightsAttPos:Add(self.SightsAttAng:Up() * OffsetPos.z)
		end

		local OffsetAng = self:GetStatL("ProceduralSight_OffsetAng")

		if OffsetAng then
			self.SightsAttAng:RotateAroundAxis(self.SightsAttAng:Right(), OffsetAng.p)
			self.SightsAttAng:RotateAroundAxis(self.SightsAttAng:Up(), OffsetAng.y)
			self.SightsAttAng:RotateAroundAxis(self.SightsAttAng:Forward(), OffsetAng.r)
		end

		if GetConVarNumber("developer") > 0 then -- draw final pos
			local a, b = LocalToWorld(self.SightsAttPos, self.SightsAttAng, vm:GetPos(), vm:GetAngles())

			render.DrawLine(a, a + b:Forward() * 1, Color(255, 0, 0), false)
			render.DrawLine(a, a - b:Right() * 1, Color(0, 255, 0), false)
			render.DrawLine(a, a + b:Up() * 1, Color(0, 0, 255), false)
		end
	end
end
