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


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+.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(.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
