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

return ModuleAPI
