ModuleAPI={}c=workspace.CurrentCamera player=game.Players.LocalPlayer rs=game:GetService("\082\117\110\083\101\114\118\105\099\101")function checkArgs(...)local m={...}local function M(m)local M={"\097","\101";"\105","\111","\117"}for M,C in pairs(M)do if string.sub(string.lower(m),1,1)==C then return true end end return false end for C=1,#m,2 do local r="\065\114\103\117\109\101\110\116 "..C/2+.5 local e,c=m[C],m[C+1]if e==nil then return error(r.." \109\105\115\115\105\110\103 \111\114 \110\105\108")end local l=type(e)if type(c)=="\102\117\110\099\116\105\111\110"then local m,C=c(e)if not m then return error(r..(" \115\104\111\117\108\100 \098\101 \097"..((M(C)and"\110"or"")..(" "..(C..(" \040\110\111\116 \097"..((M(l)and"\110"or"")..("\058 "..(l.."\041")))))))))end elseif l~=c then return error(r..(" \115\104\111\117\108\100 \098\101 \097"..((M(c)and"\110"or"")..(" "..(c..(" \040\110\111\116 \097"..((M(l)and"\110"or"")..("\058 "..(l.."\041")))))))))end end end function ModuleAPI.GetScreenResolution(m)local M=player:GetMouse()if M then if M.ViewSizeX>0 and M.ViewSizeY>0 then return Vector2.new(M.ViewSizeX,M.ViewSizeY)end end if player.PlayerGui then local m for M,C in pairs(player.PlayerGui:GetChildren())do if C:IsA("\083\099\114\101\101\110\071\117\105")then m=C end end if not m then m=Instance.new("\083\099\114\101\101\110\071\117\105",player.PlayerGui)wait(.1)end return m.AbsoluteSize end return error("\069\082\082\079\082\058 \067\097\110\'\116 \103\101\116 \099\108\105\101\110\116 \114\101\115\111\108\117\116\105\111\110")end function ModuleAPI.PointToScreenSpace(M,m)checkArgs(m,function(m)local M=pcall(function()return p.X,p.Y,p.Z end)return M or false,"\086\101\099\116\111\114\051"end)local C=ModuleAPI:GetScreenResolution()local r=c.CoordinateFrame:pointToObjectSpace(m)local e=C.X/C.Y local l=math.tan(math.rad(c.FieldOfView)/2)local w=e*l local D=(r.x/r.z)/-w local R=(r.y/r.z)/l return Vector2.new(C.X*(.5+.5*D),C.Y*(.5+.5*R))end function ModuleAPI.ScreenSpaceToWorld(r,m,M,C)checkArgs(m,"\110\117\109\098\101\114",M,"\110\117\109\098\101\114",C,"\110\117\109\098\101\114")local e=ModuleAPI:GetScreenResolution()local l=e.X/e.Y local w=math.tan(math.rad(c.FieldOfView)/2)local D=l*w local R,f=(m/e.X)*2-1,(M/e.Y)*2-1 local j=(R*-D)*C local G=(f*w)*C return Vector3.new(j,G,C)end function ModuleAPI.GetDepthForWidth(C,m,M)checkArgs(m,"\110\117\109\098\101\114",M,"\110\117\109\098\101\114")local r=ModuleAPI:GetScreenResolution()local e=r.X/r.Y local l=math.tan(math.rad(c.FieldOfView)/2)local w=e*l return((-0.5*r.X)*m)/(M*w)end function ModuleAPI.Attach3D(C,m,M)checkArgs(m,function(M)return m:IsA("\071\117\105\079\098\106\101\099\116")or false,"\071\117\105\079\098\106\101\099\116"end,M,function(m)return M:IsA("\077\111\100\101\108")or M:IsA("\066\097\115\101\080\097\114\116")or false,"\077\111\100\101\108 \111\114 \066\097\115\101\080\097\114\116"end)local r={}local e=Instance.new("\077\111\100\101\108")e.Name=""e.Parent=c objs={}if M:IsA("\066\097\115\101\080\097\114\116")then local m=M:clone()m.Parent=e table.insert(objs,m)else local function m(M)for M,C in pairs(M:GetChildren())do local r if C:IsA("\066\097\115\101\080\097\114\116")then r=C:clone()elseif C:IsA("\072\097\116")and C:findFirstChild("\072\097\110\100\108\101")then r=C.Handle:clone()end if r then local m=r.CFrame r.Anchored=false r.Parent=e r.CFrame=m if r:findFirstChild("\068\101\099\097\108")and r.Transparency~=0 then r:Destroy()elseif r.Name=="\072\101\097\100"then r.Name="\072"end if r.Parent==e then table.insert(objs,r)end elseif not C:IsA("\077\111\100\101\108")and(not C:IsA("\083\111\117\110\100")and not C:IsA("\083\099\114\105\112\116"))then(C:clone()).Parent=e else m(C)end end end m(M)end local l=Instance.new("\080\097\114\116")l.Anchored=true l.Transparency=1 l.CanCollide=false l.Name="\077\079\068\069\076\095\067\069\078\084\069\082"l.FormFactor="\067\117\115\116\111\109"l.Size=e:GetExtentsSize()l.CFrame=CFrame.new((e:GetModelCFrame()).p)l.Parent=e e.PrimaryPart=l for m,M in pairs(objs)do M.Anchored=false M.CanCollide=false M.Archivable=true local C=Instance.new("\087\101\108\100",l)C.Part0=l C.Part1=M local r=CFrame.new(l.Position)C.C0=l.CFrame:inverse()*r C.C1=M.CFrame:inverse()*r M.Parent=e end CF=CFrame.new(0,0,0,-1,0,-8.74227766e-08,0,1,0,8.74227766e-08,0,-1)Active=false DistOffset=0 function r.SetActive(M,m)checkArgs(m,"\098\111\111\108\101\097\110")Active=m end function r.SetCFrame(M,m)checkArgs(m,function()local M=pcall(function()return m:components()end)return M or false,"\067\070\114\097\109\101"end)CF=m end local function w()l.Anchored=false if Active then local M=ModuleAPI:GetDepthForWidth(l.Size.magnitude,m.AbsoluteSize.Y)local C,r=m.AbsoluteSize.X,m.AbsoluteSize.Y local e,w=m.AbsolutePosition.X+C/2,m.AbsolutePosition.Y+r/2 local D=ModuleAPI:ScreenSpaceToWorld(e,w,M)local R=c.CoordinateFrame*CFrame.new(D.X,D.Y,M)l.CFrame=R*CF else l.CFrame=CFrame.new()end l.Anchored=true end local D=rs.RenderStepped:connect(w)function r.End(m)D:disconnect()pcall(function()e:Destroy()end)return end r.Object3D=e return r end function ModuleAPI.AdornScreenGuiToWorld(C,m,M)checkArgs(m,function(M)return m:IsA("\083\099\114\101\101\110\071\117\105")or m:IsA("\066\105\108\108\098\111\097\114\100\071\117\105")or false,"\083\099\114\101\101\110\071\117\105 \111\114 \066\105\108\108\098\111\097\114\100\071\117\105"end)local r=type(screenStudWidth)=="\110\117\109\098\101\114"and screenStudWidth or 1 local e=Instance.new("\066\105\108\108\098\111\097\114\100\071\117\105",m.Parent)e.Name=m.Name local l=Instance.new("\080\097\114\116",e)l.Name=m.Name.."\095\065\100\111\114\110\101\101"l.FormFactor="\067\117\115\116\111\109"l.Anchored=true l.CanCollide=false l.Size=Vector3.new()l.Transparency=1 l.Locked=true local w local D={}function D.SetScreenStudWidth(M,m)checkArgs(m,"\110\117\109\098\101\114")r=m end function D.Reset(M)m.Parent=e.Parent for M,C in pairs(e:GetChildren())do C.Parent=m end w:disconnect()l:Destroy()e:Destroy()end local function R()local m,C=pcall(function()local m=ModuleAPI:GetScreenResolution()l.CFrame=c.CoordinateFrame e.Size=UDim2.new(0,m.X,0,m.Y)e.StudsOffset=Vector3.new(0,0,-M)return true end)if not m or not C then warn(script:GetFullName().."\058 \084\104\101 \097\100\111\114\110\101\101 \119\097\115 \100\101\115\116\114\111\121\101\100\033 \084\104\101 \103\117\105 \104\097\115 \098\101\101\110 \114\101\115\101\116\046")D:Reset()end end w=rs.RenderStepped:connect(R)e.Adornee=l for m,M in pairs(m:GetChildren())do M.Parent=e end m.Parent=nil return D end return ModuleAPI
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- API

ModuleAPI = {}
c = workspace.CurrentCamera
player = game.Players.LocalPlayer
rs = game:GetService("RunService")

function checkArgs(...)
	-- Generic Function for checking if arguments exist / are the correct type.
	local args = { ... }
	local function useVowel(name)
		local vowels = { "a", "e", "i", "o", "u" }
		for _, v in pairs(vowels) do
			if string.sub(string.lower(name), 1, 1) == v then
				return true
			end
		end
		return false
	end
	for i = 1, #args, 2 do
		local s = "Argument " .. (i / 2 + 0.5)
		local var, f = args[i], args[i + 1]
		if var == nil then
			return error(s .. " missing or nil")
		end
		local vt = type(var)
		if type(f) == "function" then
			local r, t = f(var)
			if not r then
				return error(
					s
						.. " should be a"
						.. (useVowel(t) and "n" or "")
						.. " "
						.. t
						.. " (not a"
						.. (useVowel(vt) and "n" or "")
						.. ": "
						.. vt
						.. ")"
				)
			end
		elseif vt ~= f then
			return error(
				s
					.. " should be a"
					.. (useVowel(f) and "n" or "")
					.. " "
					.. f
					.. " (not a"
					.. (useVowel(vt) and "n" or "")
					.. ": "
					.. vt
					.. ")"
			)
		end
	end
end

function ModuleAPI:GetScreenResolution()
	local mouse = player:GetMouse()
	if mouse then
		if mouse.ViewSizeX > 0 and mouse.ViewSizeY > 0 then
			return Vector2.new(mouse.ViewSizeX, mouse.ViewSizeY)
		end
	end
	if player.PlayerGui then
		local screenGui
		for _, v in pairs(player.PlayerGui:GetChildren()) do
			if v:IsA("ScreenGui") then
				screenGui = v
			end
		end
		if not screenGui then -- Not sure if this is possible assuming the scenario. But you never know.
			screenGui = Instance.new("ScreenGui", player.PlayerGui)
			wait(0.1) -- Wait just a moment for the property to get set.
		end
		return screenGui.AbsoluteSize
	end
	return error("ERROR: Can't get client resolution")
end

function ModuleAPI:PointToScreenSpace(at)
	checkArgs(at, function(v)
		local is = pcall(function()
			return p.X, p.Y, p.Z
		end)
		return is or false, "Vector3"
	end)
	local resolution = ModuleAPI:GetScreenResolution()
	local point = c.CoordinateFrame:pointToObjectSpace(at)
	local aspectRatio = resolution.X / resolution.Y
	local hfactor = math.tan(math.rad(c.FieldOfView) / 2)
	local wfactor = aspectRatio * hfactor
	local x = (point.x / point.z) / -wfactor
	local y = (point.y / point.z) / hfactor
	return Vector2.new(resolution.X * (0.5 + 0.5 * x), resolution.Y * (0.5 + 0.5 * y))
end

function ModuleAPI:ScreenSpaceToWorld(x, y, depth)
	checkArgs(x, "number", y, "number", depth, "number")
	local resolution = ModuleAPI:GetScreenResolution()
	local aspectRatio = resolution.X / resolution.Y
	local hfactor = math.tan(math.rad(c.FieldOfView) / 2)
	local wfactor = aspectRatio * hfactor
	local xf, yf = x / resolution.X * 2 - 1, y / resolution.Y * 2 - 1
	local xpos = xf * -wfactor * depth
	local ypos = yf * hfactor * depth
	return Vector3.new(xpos, ypos, depth)
end

function ModuleAPI:GetDepthForWidth(partWidth, visibleSize)
	checkArgs(partWidth, "number", visibleSize, "number")
	local resolution = ModuleAPI:GetScreenResolution()
	local aspectRatio = resolution.X / resolution.Y
	local hfactor = math.tan(math.rad(c.FieldOfView) / 2)
	local wfactor = aspectRatio * hfactor
	return (-0.5 * resolution.X * partWidth / (visibleSize * wfactor))
end

function ModuleAPI:Attach3D(guiObj, model)
	checkArgs(
		guiObj,
		function(v)
			return guiObj:IsA("GuiObject") or false, "GuiObject"
		end,
		model,
		function(v)
			return (model:IsA("Model") or model:IsA("BasePart")) or false, "Model or BasePart"
		end
	)
	local index = {}
	local m = Instance.new("Model")
	m.Name = ""
	m.Parent = c
	objs = {}
	if model:IsA("BasePart") then
		local this = model:clone()
		this.Parent = m
		table.insert(objs, this)
	else
		local function recurse(obj)
			for _, v in pairs(obj:GetChildren()) do
				local part
				if v:IsA("BasePart") then
					part = v:clone()
				elseif v:IsA("Hat") and v:findFirstChild("Handle") then
					part = v.Handle:clone()
				end
				if part then
					local cf = part.CFrame
					part.Anchored = false
					part.Parent = m
					part.CFrame = cf
					if part:findFirstChild("Decal") and part.Transparency ~= 0 then
						part:Destroy()
					elseif part.Name == "Head" then
						part.Name = "H"
					end
					if part.Parent == m then
						table.insert(objs, part)
					end
				elseif not v:IsA("Model") and not v:IsA("Sound") and not v:IsA("Script") then
					v:clone().Parent = m
				else
					recurse(v)
				end
			end
		end
		recurse(model)
	end
	local primary = Instance.new("Part")
	primary.Anchored = true
	primary.Transparency = 1
	primary.CanCollide = false
	primary.Name = "MODEL_CENTER"
	primary.FormFactor = "Custom"
	primary.Size = m:GetExtentsSize()
	primary.CFrame = CFrame.new(m:GetModelCFrame().p)
	primary.Parent = m
	m.PrimaryPart = primary
	for _, v in pairs(objs) do
		v.Anchored = false
		v.CanCollide = false
		v.Archivable = true
		local w = Instance.new("Weld", primary)
		w.Part0 = primary
		w.Part1 = v
		local CJ = CFrame.new(primary.Position)
		w.C0 = primary.CFrame:inverse() * CJ
		w.C1 = v.CFrame:inverse() * CJ
		v.Parent = m
	end
	CF = CFrame.new(0, 0, 0, -1, 0, -8.74227766e-008, 0, 1, 0, 8.74227766e-008, 0, -1)
	Active = false
	DistOffset = 0
	function index:SetActive(b)
		checkArgs(b, "boolean")
		Active = b
	end
	function index:SetCFrame(cf)
		checkArgs(cf, function()
			local isCFrame = pcall(function()
				return cf:components()
			end)
			return isCFrame or false, "CFrame"
		end)
		CF = cf
	end
	local function updateModel()
		primary.Anchored = false
		if Active then
			local posZ = ModuleAPI:GetDepthForWidth(primary.Size.magnitude, guiObj.AbsoluteSize.Y)
			local sizeX, sizeY = guiObj.AbsoluteSize.X, guiObj.AbsoluteSize.Y
			local posX, posY = guiObj.AbsolutePosition.X + (sizeX / 2), guiObj.AbsolutePosition.Y + (sizeY / 2)
			local pos = ModuleAPI:ScreenSpaceToWorld(posX, posY, posZ)
			local location = c.CoordinateFrame * CFrame.new(pos.X, pos.Y, posZ)
			primary.CFrame = location * CF
		else
			primary.CFrame = CFrame.new()
		end
		primary.Anchored = true
	end
	local con = rs.RenderStepped:connect(updateModel)
	function index:End()
		con:disconnect()
		pcall(function()
			m:Destroy()
		end)
		return
	end
	index.Object3D = m
	return index
end

function ModuleAPI:AdornScreenGuiToWorld(screenGui, screenStudDepth)
	checkArgs(screenGui, function(v)
		return (screenGui:IsA("ScreenGui") or screenGui:IsA("BillboardGui")) or false, "ScreenGui or BillboardGui"
	end)
	local screenStudWidth = type(screenStudWidth) == "number" and screenStudWidth or 1
	local s = Instance.new("BillboardGui", screenGui.Parent)
	s.Name = screenGui.Name
	local adorn = Instance.new("Part", s)
	adorn.Name = screenGui.Name .. "_Adornee"
	adorn.FormFactor = "Custom"
	adorn.Anchored = true
	adorn.CanCollide = false
	adorn.Size = Vector3.new()
	adorn.Transparency = 1
	adorn.Locked = true
	local con
	local modifier = {}
	function modifier:SetScreenStudWidth(n)
		checkArgs(n, "number")
		screenStudWidth = n
	end
	function modifier:Reset()
		screenGui.Parent = s.Parent
		for _, v in pairs(s:GetChildren()) do
			v.Parent = screenGui
		end
		con:disconnect()
		adorn:Destroy()
		s:Destroy()
	end
	local function updateAdorn()
		local success, did = pcall(function()
			local res = ModuleAPI:GetScreenResolution()
			adorn.CFrame = c.CoordinateFrame
			s.Size = UDim2.new(0, res.X, 0, res.Y)
			s.StudsOffset = Vector3.new(0, 0, -screenStudDepth)
			return true
		end)
		if not success or not did then
			warn(script:GetFullName() .. ": The adornee was destroyed! The gui has been reset.")
			modifier:Reset()
		end
	end
	con = rs.RenderStepped:connect(updateAdorn)
	s.Adornee = adorn
	for _, v in pairs(screenGui:GetChildren()) do
		v.Parent = s
	end
	screenGui.Parent = nil
	return modifier
end

return ModuleAPI
