--[[Thanks to Clavus.  Like seriously, SCK was brilliant. Even though you didn't include a license anywhere I could find, it's only fit to credit you.]]
--

local vector_origin = Vector()

--[[
Function Name:  InitMods
Syntax: self:InitMods().  Should be called only once for best performance.
Returns:  Nothing.
Notes:  Creates the VElements and WElements table, and sets up mods.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:InitMods()
	--Create a new table for every weapon instance.
	self.SWEPConstructionKit = true

	self.ViewModelElements = self:CPTbl(self.ViewModelElements)
	self.WorldModelElements = self:CPTbl(self.WorldModelElements)
	self.ViewModelBoneMods = self:CPTbl(self.ViewModelBoneMods)

	-- i have no idea how this gonna behave without that with SWEP Construction kit
	-- so we gonna leave this thing alone and precache everything
	self:CreateModels(self.ViewModelElements, true) -- create viewmodels
	self:CreateModels(self.WorldModelElements) -- create worldmodels

	--Build the bones and such.
	if self:OwnerIsValid() then
		local vm = self.OwnerViewModel

		if IsValid(vm) then
			--self:ResetBonePositions(vm)
			if (self.ShowViewModel == nil or self.ShowViewModel) then
				vm:SetColor(Color(255, 255, 255, 255))
				--This hides the viewmodel, FYI, lol.
			else
				vm:SetMaterial("Debug/hsv")
			end
		end
	end
end

--[[
Function Name:  UpdateProjectedTextures
Syntax: self:UpdateProjectedTextures().  Automatically called already.
Returns:  Nothing.
Notes:  This takes care of our flashlight and laser.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--

function SWEP:UpdateProjectedTextures(view)
	self:DrawLaser(view)
	self:DrawFlashlight(view)
end

--[[
Function Name:  ViewModelDrawn
Syntax: self:ViewModelDrawn().  Automatically called already.
Returns:  Nothing.
Notes:  This draws the mods.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:PreDrawViewModel(vm, wep, ply)
	self:ProcessBodygroups()

	--vm:SetupBones()

	if self:GetHidden() then
		render.SetBlend(0)
	end
end

SWEP.CameraAttachmentOffsets = {{"p", 0}, {"y", 0}, {"r", 0}}
SWEP.CameraAttachment = nil
SWEP.CameraAttachments = {"camera", "attach_camera", "view", "cam", "look"}
SWEP.CameraAngCache = nil
local tmpvec = Vector(0, 0, -2000)

do
	local reference_table

	local function rendersorter(a, b)
		local ar, br = reference_table[a], reference_table[b]

		if ar == br then
			return a > b
		end

		return ar > br
	end

	local function inc_references(lookup, name, entry, output, level)
		output[name] = (output[name] or 0) + level
		local elemother = lookup[entry.rel]

		if elemother then
			inc_references(lookup, entry.rel, elemother, output, level + 1)
		end
	end

	function SWEP:RebuildModsRenderOrder()
		self.vRenderOrder = {}
		self.wRenderOrder = {}
		self.VElementsBodygroupsCache = {}
		self.WElementsBodygroupsCache = {}

		local ViewModelElements = self:GetStatRaw("ViewModelElements", TFA.LatestDataVersion) or {}
		local WorldModelElements = self:GetStatRaw("WorldModelElements", TFA.LatestDataVersion) or {}

		if istable(ViewModelElements) then
			local target = self.vRenderOrder
			reference_table = {}

			for k, v in pairs(ViewModelElements) do
				if v.type == "Model" then
					table.insert(target, k)
					inc_references(ViewModelElements, k, v, reference_table, 10000)
				elseif v.type == "Sprite" or v.type == "Quad" or v.type == "Bodygroup" then
					table.insert(target, k)
					inc_references(ViewModelElements, k, v, reference_table, 1)
				end
			end

			table.sort(target, rendersorter)
		end

		if istable(WorldModelElements) then
			local target2 = self.wRenderOrder
			reference_table = {}

			for k, v in pairs(WorldModelElements) do
				if v.type == "Model" then
					table.insert(target2, 1, k)
					inc_references(WorldModelElements, k, v, reference_table, 10000)
				elseif v.type == "Sprite" or v.type == "Quad" or v.type == "Bodygroup" then
					table.insert(target2, k)
					inc_references(WorldModelElements, k, v, reference_table, 1)
				end
			end

			table.sort(target2, rendersorter)
		end

		return self.vRenderOrder, self.wRenderOrder
	end
end

function SWEP:RemoveModsRenderOrder()
	self.vRenderOrder = nil
end

local drawfn, drawself, fndrawpos, fndrawang, fndrawsize

local function dodrawfn()
	drawfn(drawself, fndrawpos, fndrawang, fndrawsize)
end

local next_setup_bones = 0

function TFA._IncNextSetupBones()
	next_setup_bones = next_setup_bones + 1
end

function TFA._GetNextSetupBones()
	return next_setup_bones
end

local mirror_scale = Vector(1, -1, 1)
local normal_scale = Vector(1, 1, 1)

local mirror = Matrix()

local DRAW_AND_SETUP = 0
local ONLY_DRAW = 1
local ONLY_SETUP = 2

local function draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, nodraw)
	if self2.GetStatL(self, "ViewModelElements." .. name .. ".active") == false then return end
	if self2.TFA_IsDrawingStencilSights and self2.GetStatL(self, "StencilSight_VElement") == name then return end

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

	self:PrecacheElement(element, true)

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

	local dodraw = nodraw == DRAW_AND_SETUP or nodraw == ONLY_DRAW
	local dosetup = nodraw == DRAW_AND_SETUP or nodraw == ONLY_SETUP

	if element.type == "Model" and IsValid(model) then
		if not element.bonemerge and dosetup then
			mirror:Identity()

			if self2.ViewModelFlip 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)

				mirror:Scale(mirror_scale)
				mirror:Scale(element.size)
			else
				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)

				mirror:Scale(normal_scale)
				mirror:Scale(element.size)
			end

			model:SetAngles(ang)
			model:EnableMatrix("RenderMultiply", mirror)
		end

		if dodraw then
			if element.surpresslightning then
				render.SuppressEngineLighting(true)
			end

			local material = self:GetStatL("ViewModelElements." .. name .. ".material")

			if not material or material == "" then
				model:SetMaterial("")
			elseif model:GetMaterial() ~= material then
				model:SetMaterial(material)
			end

			local skin = self:GetStatL("ViewModelElements." .. name .. ".skin")

			if skin and skin ~= model:GetSkin() then
				model:SetSkin(skin)
			end

			if not self2.SCKMaterialCached_V[name] then
				self2.SCKMaterialCached_V[name] = true

				local materialtable = self:GetStatL("ViewModelElements." .. name .. ".materials", {})
				local entmats = table.GetKeys(model:GetMaterials())

				for _, k in ipairs(entmats) do
					model:SetSubMaterial(k - 1, materialtable[k] or "")
				end
			end
		end

		if dosetup then
			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
				model:SetPos(pos)
				model:SetAngles(ang)

				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
		end

		if dodraw then
			render.SetColorModulation(element.color.r / 255, element.color.g / 255, element.color.b / 255)
			render.SetBlend(element.color.a / 255)
		end

		if dosetup and model.tfa_next_setup_bones ~= next_setup_bones then
			model:InvalidateBoneCache()
			model:SetupBones()
			model.tfa_next_setup_bones = next_setup_bones
		end

		if dodraw then
			if self2.ViewModelFlip then
				render.CullMode(MATERIAL_CULLMODE_CW)
			end

			model:DrawModel()

			render.SetBlend(1)
			render.SetColorModulation(1, 1, 1)

			if self2.ViewModelFlip then
				render.CullMode(MATERIAL_CULLMODE_CCW)
			end

			if element.surpresslightning then
				render.SuppressEngineLighting(false)
			end
		end
	elseif dodraw and element.type == "Sprite" and sprite then
		local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
		render.SetMaterial(sprite)
		render.DrawSprite(drawpos, element.size.x, element.size.y, element.color)
	elseif dodraw and element.type == "Quad" and element.draw_func then
		local drawpos = 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)

		cam.Start3D2D(drawpos, ang, element.size)
		render.PushFilterMin(TEXFILTER.ANISOTROPIC)
		render.PushFilterMag(TEXFILTER.ANISOTROPIC)

		drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func, self, nil, nil, nil
		ProtectedCall(dodrawfn)

		render.PopFilterMin()
		render.PopFilterMag()
		cam.End3D2D()
	end
end

function SWEP:ViewModelDrawn()
	local self2 = self:GetTable()
	render.SetBlend(1)

	if self2.DrawHands then
		self2.DrawHands(self)
	end

	local vm = self.OwnerViewModel
	if not IsValid(vm) then return end
	if not self:GetOwner().GetHands then return end

	if self2.UseHands then
		local hands = self:GetOwner():GetHands()

		if IsValid(hands) then
			if not self2.GetHidden(self) then
				hands:SetParent(vm)
			else
				hands:SetParent(nil)
				hands:SetPos(tmpvec)
			end
		end
	end

	self2.UpdateBonePositions(self, vm)

	if not self2.CameraAttachment then
		self2.CameraAttachment = -1

		for _, v in ipairs(self2.CameraAttachments) do
			local attid = vm:LookupAttachment(v)

			if attid and attid > 0 then
				self2.CameraAttachment = attid
				break
			end
		end
	end

	if self2.CameraAttachment and self2.CameraAttachment > 0 then
		local angpos = vm:GetAttachment(self2.CameraAttachment)

		if angpos and angpos.Ang then
			local ftv = FrameTime()
			local angv = angpos.Ang
			local off = vm:WorldToLocalAngles(angv)
			local spd = 15
			local cycl = vm:GetCycle()
			self2.CameraAngCache = self2.CameraAngCache or off

			for _, v in pairs(self2.CameraAttachmentOffsets) do
				local offtype = v[1]
				local offang = v[2]

				if offtype == "p" then
					off:RotateAroundAxis(off:Right(), offang)
				elseif offtype == "y" then
					off:RotateAroundAxis(off:Up(), offang)
				elseif offtype == "r" then
					off:RotateAroundAxis(off:Forward(), offang)
				end
			end

			if self2.ViewModelFlip then
				off = Angle()
			end

			local stat = self:GetStatus()

			if (stat == TFA.Enum.STATUS_DRAW) and cycl < 0.05 then
				local mul = cycl / 0.05

				self2.CameraAngCache.p = self2.CameraAngCache.p * mul
				self2.CameraAngCache.y = self2.CameraAngCache.y * mul
				self2.CameraAngCache.r = self2.CameraAngCache.r * mul
			elseif TFA.Enum.HolsterStatus[stat] and cycl > 0.95 and not self2.GetStatL(self, "ProceduralHolsterEnabled") then
				local mul = 1 - (cycl - 0.95) / 0.05

				self2.CameraAngCache.p = self2.CameraAngCache.p * mul
				self2.CameraAngCache.y = self2.CameraAngCache.y * mul
				self2.CameraAngCache.r = self2.CameraAngCache.r * mul
			end

			self2.CameraAngCache.p = math.Approach(self2.CameraAngCache.p, off.p, (self2.CameraAngCache.p - off.p) * ftv * spd)
			self2.CameraAngCache.y = math.Approach(self2.CameraAngCache.y, off.y, (self2.CameraAngCache.y - off.y) * ftv * spd)
			self2.CameraAngCache.r = math.Approach(self2.CameraAngCache.r, off.r, (self2.CameraAngCache.r - off.r) * ftv * spd)
		else
			self2.CameraAngCache.p = 0
			self2.CameraAngCache.y = 0
			self2.CameraAngCache.r = 0
		end
	end

	local ViewModelElements = self:GetStatRawL("ViewModelElements") or {}
	local ViewModelBodygroups = self:GetStatRawL("ViewModelBodygroups") or {}

	if ViewModelElements and self2.HasInitAttachments then
		-- ViewModelElements = self:GetStatL("ViewModelElements")
		-- self:CreateModels(ViewModelElements, true)

		self2.SCKMaterialCached_V = self2.SCKMaterialCached_V or {}

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

		vm:InvalidateBoneCache()
		vm:SetupBones()
		next_setup_bones = next_setup_bones + 1

		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 == "Bodygroup" then
				if element.index and element.value_active then
					ViewModelBodygroups[element.index] = self2.GetStatL(self, "ViewModelElements." .. name .. ".active") and element.value_active or (element.value_inactive or 0)
				end

				goto CONTINUE
			end

			if element.hide then goto CONTINUE end

			if element.type == "Quad" and element.draw_func_outer then goto CONTINUE end
			if not element.bone and not element.attachment then goto CONTINUE end

			draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, element.translucent == true and ONLY_SETUP or DRAW_AND_SETUP)

			::CONTINUE::
		end
	end

	if not self2.UseHands and self2.ViewModelDrawnPost then
		self:ViewModelDrawnPost()
		self:ViewModelDrawnPostFinal()
	end

	if self2.ShellEjectionQueue ~= 0 then
		for i = 1, self2.ShellEjectionQueue do
			self:MakeShell(true)
		end

		self2.ShellEjectionQueue = 0
	end
end

function SWEP:ViewModelDrawnPostFinal()
	local self2 = self:GetTable()
	local vm = self.OwnerViewModel
	if not IsValid(vm) then return end

	local ViewModelElements = self:GetStatRawL("ViewModelElements")
	if not ViewModelElements then return end

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

		if element.hide or not element.translucent then goto CONTINUE end

		if element.type == "Quad" and element.draw_func_outer then goto CONTINUE end
		if not element.bone and not element.attachment then goto CONTINUE end

		draw_element_closure(self, self2, name, index, vm, ViewModelElements, element, ONLY_DRAW)

		::CONTINUE::
	end
end

function SWEP:ViewModelDrawnPost()
	local self2 = self:GetTable()
	if not self2.VMIV(self) then return end

	if not self.ViewModelFlip then
		self2.CacheSightsPos(self, self.OwnerViewModel, false)
	end

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

	if not ViewModelElements or not self2.vRenderOrder then return end

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

		if element.type == "Quad" and element.draw_func_outer and not element.hide and (element.bone or element.attachment and element.attachment ~= "") and self:GetStatL("ViewModelElements." .. name .. ".active") ~= false then
			local pos, ang = self:GetBoneOrientation(ViewModelElements, element, self2.OwnerViewModel)

			if pos then
				local drawpos = 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)

				drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func_outer, self, drawpos, ang, element.size
				ProtectedCall(dodrawfn)
			end
		end
	end
end

--[[
Function Name:  DrawWorldModel
Syntax: self:DrawWorldModel().  Automatically called already.
Returns:  Nothing.
Notes:  This draws the world model, plus its attachments.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:DrawWorldModel()
	local self2 = self:GetTable()

	local skinStat = self2.GetStatL(self, "Skin")
	if isnumber(skinStat) then
		if self:GetSkin() ~= skinStat then
			self:SetSkin(skinStat)
		end
	end

	if not self2.MaterialCached_W and self2.GetStatL(self, "MaterialTable_W") then
		self2.MaterialCached_W = {}
		self:SetSubMaterial()

		local collectedKeys = table.GetKeys(self2.GetStatL(self, "MaterialTable_W"))
		table.Merge(collectedKeys, table.GetKeys(self2.GetStatL(self, "MaterialTable")))

		for _, k in ipairs(collectedKeys) do
			if (k == "BaseClass") then goto CONTINUE end

			local v = self2.GetStatL(self, "MaterialTable_W")[k]

			if not self2.MaterialCached_W[k] then
				self:SetSubMaterial(k - 1, v)
				self2.MaterialCached_W[k] = true
			end

			::CONTINUE::
		end
	end

	local ply = self:GetOwner()
	local validowner = IsValid(ply)

	if validowner then
		-- why? this tanks FPS because source doesn't have a chance to setup bones when it needs to
		-- instead we ask it to do it `right now`
		-- k then
		ply:SetupBones()
		ply:InvalidateBoneCache()
		self:InvalidateBoneCache()
	end

	if self2.ShowWorldModel == nil or self2.ShowWorldModel or not validowner then
		self2.WorldModelOffsetUpdate(self, ply)
		self2.ProcessBodygroups(self)

		self:DrawModel()
	end

	self:SetupBones()
	self2.UpdateWMBonePositions(self)

	self:DrawWElements()

	if IsValid(self) and self.IsTFAWeapon and (self:GetOwner() ~= LocalPlayer() or not self:IsFirstPerson()) then
		self2.UpdateProjectedTextures(self, false)
	end
end

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

	local WorldModelElements = self2.GetStatRaw(self, "WorldModelElements", TFA.LatestDataVersion)

	if not WorldModelElements then return end

	if not self2.SCKMaterialCached_W then
		self2.SCKMaterialCached_W = {}
	end

	if not self2.wRenderOrder then
		self2.RebuildModsRenderOrder(self)
	end

	local ply = self:GetOwner()
	local validowner = IsValid(ply)

	for index = 1, #self2.wRenderOrder do
		local name = self2.wRenderOrder[index]
		local element = WorldModelElements[name]

		if not element then
			self2.RebuildModsRenderOrder(self)
			break
		end

		if element.type == "Bodygroup" then
			if element.index and element.value_active then
				self2.WorldModelBodygroups[element.index] = self2.GetStatL(self, "WorldModelElements." .. name .. ".active") and element.value_active or (element.value_inactive or 0)
			end

			goto CONTINUE
		end

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

		local bone_ent = (validowner and ply:LookupBone(element.bone or "ValveBiped.Bip01_R_Hand")) and ply or self
		local pos, ang

		if element.bone then
			pos, ang = self2.GetBoneOrientation(self, WorldModelElements, element, bone_ent)
		else
			pos, ang = self2.GetBoneOrientation(self, WorldModelElements, element, bone_ent, "ValveBiped.Bip01_R_Hand")
		end

		if not pos and not element.bonemerge then goto CONTINUE end

		self2.PrecacheElement(self, element, true)

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

		if element.type == "Model" and IsValid(model) then
			if element.bonemerge then
				model:SetPos(pos)
				model:SetAngles(ang)
			else
				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)
			end

			local material = self2.GetStatL(self, "WorldModelElements." .. name .. ".material")

			if not material or material == "" then
				model:SetMaterial("")
			elseif model:GetMaterial() ~= material then
				model:SetMaterial(material)
			end

			local skin = self2.GetStatL(self, "WorldModelElements." .. name .. ".skin")

			if skin and skin ~= model:GetSkin() then
				model:SetSkin(skin)
			end

			if not self2.SCKMaterialCached_W[name] then
				self2.SCKMaterialCached_W[name] = true

				local materialtable = self2.GetStatL(self, "WorldModelElements." .. name .. ".materials", {})
				local entmats = table.GetKeys(model:GetMaterials())

				for _, k in ipairs(entmats) do
					model:SetSubMaterial(k - 1, materialtable[k] or "")
				end
			end

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

			if self2.WElementsBodygroupsCache[index] then
				for _b = 0, self2.WElementsBodygroupsCache[index] do
					local newbg = self2.GetStatL(self, "WorldModelElements." .. 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.surpresslightning then
				render.SuppressEngineLighting(true)
			end

			if element.bonemerge then
				if element.rel and WorldModelElements[element.rel] and IsValid(WorldModelElements[element.rel].curmodel) and WorldModelElements[element.rel].bone ~= "oof" then
					element.parModel = WorldModelElements[element.rel].curmodel
				else
					element.parModel = 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:SetLocalPos(vector_origin)
					model:SetLocalAngles(angle_zero)
				end
			elseif model:IsEffectActive(EF_BONEMERGE) then
				model:RemoveEffects(EF_BONEMERGE)
				model:SetParent(nil)
			end

			render.SetColorModulation(element.color.r / 255, element.color.g / 255, element.color.b / 255)
			render.SetBlend(element.color.a / 255)

			model:DrawModel()

			render.SetBlend(1)
			render.SetColorModulation(1, 1, 1)

			if element.surpresslightning then
				render.SuppressEngineLighting(false)
			end
		elseif element.type == "Sprite" and sprite then
			local drawpos = pos + ang:Forward() * element.pos.x + ang:Right() * element.pos.y + ang:Up() * element.pos.z
			render.SetMaterial(sprite)
			render.DrawSprite(drawpos, element.size.x, element.size.y, element.color)
		elseif element.type == "Quad" and element.draw_func then
			local drawpos = 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)
			cam.Start3D2D(drawpos, ang, element.size)

			drawfn, drawself, fndrawpos, fndrawang, fndrawsize = element.draw_func, self, nil, nil, nil
			ProtectedCall(dodrawfn)

			cam.End3D2D()
		end

		::CONTINUE::
	end
end

function SWEP:WorldModelOffsetUpdate(ply)
	if not IsValid(ply) then
		self:SetRenderOrigin(nil)
		self:SetRenderAngles(nil)

		local WorldModelOffset = self:GetStatRawL("WorldModelOffset")

		if WorldModelOffset and WorldModelOffset.Scale then
			self:SetModelScale(WorldModelOffset.Scale, 0)
		end

		return
	end


	local WorldModelOffset = self:GetStatRawL("WorldModelOffset")

		-- THIS IS DANGEROUS
	if WorldModelOffset and WorldModelOffset.Pos and WorldModelOffset.Ang then
		-- TO DO ONLY CLIENTSIDE
		-- since this will break hitboxes!
		local handBone = ply:LookupBone("ValveBiped.Bip01_R_Hand")

		if handBone then
			--local pos, ang = ply:GetBonePosition(handBone)
			local pos, ang
			local mat = ply:GetBoneMatrix(handBone)

			if mat then
				pos, ang = mat:GetTranslation(), mat:GetAngles()
			else
				pos, ang = ply:GetBonePosition(handBone)
			end

			local opos, oang, oscale = WorldModelOffset.Pos, WorldModelOffset.Ang, WorldModelOffset.Scale

			pos = pos + ang:Forward() * opos.Forward + ang:Right() * opos.Right + ang:Up() * opos.Up
			ang:RotateAroundAxis(ang:Up(), oang.Up)
			ang:RotateAroundAxis(ang:Right(), oang.Right)
			ang:RotateAroundAxis(ang:Forward(), oang.Forward)
			self:SetRenderOrigin(pos)
			self:SetRenderAngles(ang)
			--if WorldModelOffset.Scale and ( !self2.MyModelScale or ( WorldModelOffset and self2.MyModelScale!=WorldModelOffset.Scale ) ) then
			self:SetModelScale(oscale or 1, 0)
			--end
		end
	end
end

--[[
Function Name:  GetBoneOrientation
Syntax: self:GetBoneOrientation( base bone mod table, bone mod table, entity, bone override ).
Returns:  Position, Angle.
Notes:  This is a very specific function for a specific purpose, and shouldn't be used generally to get a bone's orientation.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:GetBoneOrientation(basetabl, tabl, ent, bone_override, isVM, isAttachment, isNonRoot)
	local bone, pos, ang

	if not IsValid(ent) then return Vector(), Angle() end

	if not isNonRoot and tabl.rel and tabl.rel ~= "" and not tabl.bonemerge then
		local v = basetabl[tabl.rel]
		if not v then return Vector(), Angle() end

		local boneName = tabl.bone

		if tabl.attachment and tabl.attachment ~= "" and v.curmodel:LookupAttachment(tabl.attachment) ~= 0 then
			pos, ang = self:GetBoneOrientation(basetabl, v, v.curmodel, tabl.attachment, isVM, true, true)

			if pos and ang then return pos, ang end
		elseif v.curmodel and ent ~= v.curmodel and (v.bonemerge or (boneName and boneName ~= "" and v.curmodel:LookupBone(boneName))) then
			pos, ang = self:GetBoneOrientation(basetabl, v, v.curmodel, boneName, isVM, false, true)

			if pos and ang then return pos, ang end
		else
			--As clavus states in his original code, don't make your elements named the same as a bone, because recursion.
			pos, ang = self:GetBoneOrientation(basetabl, v, ent, nil, isVM, false, true)

			if pos and ang then
				pos = pos + ang:Forward() * v.pos.x + ang:Right() * v.pos.y + ang:Up() * v.pos.z

				ang:RotateAroundAxis(ang:Up(), v.angle.y)
				ang:RotateAroundAxis(ang:Right(), v.angle.p)
				ang:RotateAroundAxis(ang:Forward(), v.angle.r)

				-- For mirrored viewmodels.  You might think to scale negatively on X, but this isn't the case.

				return pos, ang
			end
		end
	end

	if isAttachment == nil then isAttachment = tabl.attachment ~= nil end

	if isnumber(bone_override) then
		bone = bone_override
	elseif isAttachment then
		bone = ent:LookupAttachment(bone_override or tabl.attachment)
	else
		bone = ent:LookupBone(bone_override or tabl.bone) or 0
	end

	if not bone or bone == -1 then return end
	pos, ang = Vector(0, 0, 0), Angle(0, 0, 0)

	if ent.tfa_next_setup_bones ~= next_setup_bones then
		ent:InvalidateBoneCache()
		ent:SetupBones()
		ent.tfa_next_setup_bones = next_setup_bones
	end

	if isAttachment then
		-- mmmm yes tasty LuaVM memory
		-- GC screams in agony
		local get = ent:GetAttachment(bone)

		if get then
			pos, ang = get.Pos, get.Ang
		end
	else

		local m = ent:GetBoneMatrix(bone)

		if m then
			pos, ang = m:GetTranslation(), m:GetAngles()
		end
	end

	local owner = self:GetOwner()

	if isVM and self.ViewModelFlip then
		ang.r = -ang.r
	end

	return pos, ang
end
--[[
Function Name:  CleanModels
Syntax: self:CleanModels( elements table ).
Returns:   Nothing.
Notes:  Removes all existing models.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:CleanModels(input)
	if not istable(input) then return end

	for _, v in pairs(input) do
		if (v.type == "Model" and v.curmodel) then
			if IsValid(v.curmodel) then
				v.curmodel:Remove()
			end

			v.curmodel = nil
		elseif (v.type == "Sprite" and v.sprite and v.sprite ~= "" and (not v.spritemat or v.cursprite ~= v.sprite)) then
			v.cursprite = nil
			v.spritemat = nil
		end
	end
end

function SWEP:PrecacheElementModel(element, is_vm)
	element.curmodel = ClientsideModel(element.model, RENDERGROUP_OTHER)
	element.curmodel.tfa_gun_parent = self
	element.curmodel.tfa_gun_clmodel = true

	if self.SWEPConstructionKit then
		TFA.RegisterClientsideModel(element.curmodel, self)
	end

	if not IsValid(element.curmodel) then
		element.curmodel = nil
		return
	end

	element.curmodel:SetPos(self:GetPos())
	element.curmodel:SetAngles(self:GetAngles())
	element.curmodel:SetParent(self)
	element.curmodel:SetOwner(self)
	element.curmodel:SetNoDraw(true)

	if element.material then
		element.curmodel:SetMaterial(element.material or "")
	end

	if element.skin then
		element.curmodel:SetSkin(element.skin)
	end

	local matrix = Matrix()
	matrix:Scale(element.size)

	element.curmodel:EnableMatrix("RenderMultiply", matrix)
	element.curmodelname = element.model
	element.view = is_vm == true

	-- // make sure we create a unique name based on the selected options
end

do
	local tocheck = {"nocull", "additive", "vertexalpha", "vertexcolor", "ignorez"}

	function SWEP:PrecacheElementSprite(element, is_vm)
		if element.vmt then
			element.spritemat = Material(element.sprite)
			element.cursprite = element.sprite
			return
		end

		local name = "tfa-" .. element.sprite .. "-"

		local params = {
			["$basetexture"] = element.sprite
		}

		for _, element_property in ipairs(tocheck) do
			if (element[element_property]) then
				params["$" .. element_property] = 1
				name = name .. "1"
			else
				name = name .. "0"
			end
		end

		element.cursprite = element.sprite
		element.spritemat = CreateMaterial(name, "UnlitGeneric", params)
	end
end

function SWEP:PrecacheElement(element, is_vm)
	if element.type == "Model" and element.model and (not IsValid(element.curmodel) or element.curmodelname ~= element.model) and element.model ~= "" then
		if IsValid(element.curmodel) then
			element.curmodel:Remove()
		end

		self:PrecacheElementModel(element, is_vm)
	elseif (element.type == "Sprite" and element.sprite and element.sprite ~= "" and (not element.spritemat or element.cursprite ~= element.sprite)) then
		self:PrecacheElementSprite(element, is_vm)
	end
end

--[[
Function Name:  CreateModels
Syntax: self:CreateModels( elements table ).
Returns:   Nothing.
Notes:  Creates the elements for whatever you give it.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:CreateModels(input, is_vm)
	if not istable(input) then return end

	for _, element in pairs(input) do
		self:PrecacheElement(element, is_vm)
	end
end

--[[
Function Name:  UpdateBonePositions
Syntax: self:UpdateBonePositions( viewmodel ).
Returns:   Nothing.
Notes:   Updates the bones for a viewmodel.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
local bpos, bang
local onevec = Vector(1, 1, 1)
local getKeys = table.GetKeys

local function appendTable(t, t2)
	for i = 1, #t2 do
		t[#t + 1] = t2[i]
	end
end

SWEP.ChildrenScaled = {}
SWEP.ViewModelBoneMods_Children = {}

function SWEP:ScaleChildBoneMods(ent,bone,cumulativeScale)
	if self.ChildrenScaled[bone] then
		return
	end
	self.ChildrenScaled[bone] = true
	local boneid = ent:LookupBone(bone)
	if not boneid then return end
	local curScale = (cumulativeScale or Vector(1,1,1)) * 1
	if self.ViewModelBoneMods[bone] then
		curScale = curScale * self.ViewModelBoneMods[bone].scale
	end
	local ch = ent:GetChildBones(boneid)
	if ch and #ch > 0 then
		for _, boneChild in ipairs(ch) do
			self:ScaleChildBoneMods(ent,ent:GetBoneName(boneChild),curScale)
		end
	end
	if self.ViewModelBoneMods[bone] then
		self.ViewModelBoneMods[bone].scale = curScale
	else
		self.ViewModelBoneMods_Children[bone] = {
			["pos"] = vector_origin,
			["angle"] = angle_zero,
			["scale"] = curScale * 1
		}
	end
end

local vmbm_old_count = 0

function SWEP:UpdateBonePositions(vm)
	local self2 = self:GetTable()
	local vmbm = self2.GetStatL(self, "ViewModelBoneMods")

	local vmbm_count = 0

	if vmbm then
		vmbm_count = table.Count(vmbm)
	end

	if vmbm_old_count ~= vmbm_count then
		self:ResetBonePositions()
	end

	vmbm_old_count = vmbm_count

	if vmbm then
		local stat = self:GetStatus()

		if not self2.BlowbackBoneMods then
			self2.BlowbackBoneMods = {}
			self2.BlowbackCurrent = 0
		end

		if not self2.HasSetMetaVMBM then
			for k,v in pairs(self2.ViewModelBoneMods) do
				if (k == "BaseClass") then goto CONTINUE end -- do not name your bones like this pls

				local scale = v.scale

				if scale and scale.x ~= 1 or scale.y ~= 1 or scale.z ~= 1 then
					self:ScaleChildBoneMods(vm, k)
				end

				::CONTINUE::
			end

			for _,v in pairs(self2.BlowbackBoneMods) do
				v.pos_og = v.pos
				v.angle_og = v.angle
				v.scale_og = v.scale or onevec
			end

			self2.HasSetMetaVMBM = true
			self2.ViewModelBoneMods["wepEnt"] = self

			setmetatable(self2.ViewModelBoneMods, {__index = function(t,k)
				if not IsValid(self) then return end
				if self2.ViewModelBoneMods_Children[k] then return self2.ViewModelBoneMods_Children[k] end
				if not self2.BlowbackBoneMods[k] then return end
				if not ( self2.SequenceEnabled[ACT_VM_RELOAD_EMPTY] and TFA.Enum.ReloadStatus[stat] and self2.Blowback_PistolMode ) then
					self2.BlowbackBoneMods[k].pos = self2.BlowbackBoneMods[k].pos_og * self2.BlowbackCurrent
					self2.BlowbackBoneMods[k].angle = self2.BlowbackBoneMods[k].angle_og * self2.BlowbackCurrent
					self2.BlowbackBoneMods[k].scale = Lerp(self2.BlowbackCurrent, onevec, self2.BlowbackBoneMods[k].scale_og)
					return self2.BlowbackBoneMods[k]
				end
			end})
		end

		if not ( self2.SequenceEnabled[ACT_VM_RELOAD_EMPTY] and TFA.Enum.ReloadStatus[stat] and self2.Blowback_PistolMode ) then
			self2.BlowbackCurrent = math.Approach(self2.BlowbackCurrent, 0, self2.BlowbackCurrent * FrameTime() * 30)
		end

		local keys = getKeys(vmbm)
		appendTable(keys, getKeys(self2.GetStatL(self, "BlowbackBoneMods") or self2.BlowbackBoneMods))
		appendTable(keys, getKeys(self2.ViewModelBoneMods_Children))

		for _,k in pairs(keys) do
			if k == "wepEnt" then goto CONTINUE end

			local v = vmbm[k] or self2.GetStatL(self, "ViewModelBoneMods." .. k)
			if not v then goto CONTINUE end

			local vscale, vangle, vpos = v.scale, v.angle, v.pos

			local bone = vm:LookupBone(k)
			if not bone then goto CONTINUE end

			local b = self2.GetStatL(self, "BlowbackBoneMods." .. k)

			if b then
				vscale = Lerp(self2.BlowbackCurrent, vscale, vscale * b.scale)
				vangle = vangle + b.angle * self2.BlowbackCurrent
				vpos = vpos + b.pos * self2.BlowbackCurrent
			end

			if vm:GetManipulateBoneScale(bone) ~= vscale then
				vm:ManipulateBoneScale(bone, vscale)
			end

			if vm:GetManipulateBoneAngles(bone) ~= vangle then
				vm:ManipulateBoneAngles(bone, vangle)
			end

			if vm:GetManipulateBonePosition(bone) ~= vpos then
				vm:ManipulateBonePosition(bone, vpos)
			end

			::CONTINUE::
		end
	elseif self2.BlowbackBoneMods then
		for bonename, tbl in pairs(self2.BlowbackBoneMods) do
			local bone = vm:LookupBone(bonename)

			if bone and bone >= 0 then
				bpos = tbl.pos * self2.BlowbackCurrent
				bang = tbl.angle * self2.BlowbackCurrent
				vm:ManipulateBonePosition(bone, bpos)
				vm:ManipulateBoneAngles(bone, bang)
			end
		end
	end
end

--[[
Function Name:  ResetBonePositions
Syntax: self:ResetBonePositions( viewmodel ).
Returns:   Nothing.
Notes:   Resets the bones for a viewmodel.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:ResetBonePositions(val)
	if SERVER then
		self:CallOnClient("ResetBonePositions", "")

		return
	end

	local vm = self.OwnerViewModel
	if not IsValid(vm) then return end
	if (not vm:GetBoneCount()) then return end

	for i = 0, vm:GetBoneCount() do
		vm:ManipulateBoneScale(i, Vector(1, 1, 1))
		vm:ManipulateBoneAngles(i, Angle(0, 0, 0))
		vm:ManipulateBonePosition(i, vector_origin)
	end
end

--[[
Function Name:  UpdateWMBonePositions
Syntax: self:UpdateWMBonePositions( worldmodel ).
Returns:   Nothing.
Notes:   Updates the bones for a worldmodel.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:UpdateWMBonePositions()
	if not self.WorldModelBoneMods then
		self.WorldModelBoneMods = {}
	end

	local WM_BoneMods = self:GetStatL("WorldModelBoneMods", self.WorldModelBoneMods)

	if next(WM_BoneMods) then
		for bone = 0, self:GetBoneCount() - 1 do
			local bonemod = WM_BoneMods[self:GetBoneName(bone)]
			if not bonemod then goto CONTINUE end

			local childscale
			local cur = self:GetBoneParent(bone)

			while (cur ~= -1) do
				local par = WM_BoneMods[self:GetBoneName(cur)]

				if par then
					childscale = (childscale or onevec) * (par.scale or onevec)
				end

				cur = self:GetBoneParent(cur)
			end

			local s = (bonemod.scale or onevec)
			if childscale then
				s = s * childscale
			end

			if self:GetManipulateBoneScale(bone) ~= s then
				self:ManipulateBoneScale(bone, s)
			end

			local a = bonemod.angle or angle_zero

			if self:GetManipulateBoneAngles(bone) ~= a then
				self:ManipulateBoneAngles(bone, a)
			end

			local p = bonemod.pos or vector_origin

			if self:GetManipulateBonePosition(bone) ~= p then
				self:ManipulateBonePosition(bone, p)
			end

			::CONTINUE::
		end
	end
end

--[[
Function Name:  ResetWMBonePositions
Syntax: self:ResetWMBonePositions( worldmodel ).
Returns:   Nothing.
Notes:   Resets the bones for a worldmodel.
Purpose:  SWEP Construction Kit Compatibility / Basic Attachments.
]]
--
function SWEP:ResetWMBonePositions(wm)
	if SERVER then
		self:CallOnClient("ResetWMBonePositions", "")

		return
	end

	if not wm then
		wm = self
	end

	if not IsValid(wm) then return end

	for i = 0, wm:GetBoneCount() - 1 do
		wm:ManipulateBoneScale(i, Vector(1, 1, 1))
		wm:ManipulateBoneAngles(i, Angle(0, 0, 0))
		wm:ManipulateBonePosition(i, vector_origin)
	end
end

function SWEP:PreDrawStencilSight(vm, ply, SightElementTable)
end

function SWEP:PostDrawStencilSight(vm, ply, SightElementTable)
end
