function Util.debugPrint(d,C)local G=""C=C or"  "local W local q for d,Y in pairs(d)do local D=type(Y)local E,Q=pcall(function()return tostring(d)end)if E then q=Q else q="\091"..(type(d).."\093")end if D=="\116\097\098\108\101"then W=string.format("\n\037\115\037\115","",Util.debugPrint(Y,C.."  "))else local d,C=pcall(function()return tostring(Y)end)if d then W=C else W="\091"..(type(Y).."\093")end end G=string.format("\037\115\037\115\037\115\058 \037\115\n",G,C,q,W)end return G end function Util.tableToString(d,C)local G=""C=C or"  "local W for d,q in pairs(d)do local Y=type(q)if Y=="\116\097\098\108\101"then W=string.format("\n\037\115\037\115","",Util.tableToString(q,C.."  "))elseif Y=="\110\105\108"then W="\091\110\105\108\093"elseif Y=="\115\116\114\105\110\103"then W=string.format("\"\037\115\"",q)elseif Y=="\098\111\111\108\101\097\110"then W=Util.boolToString(q)elseif Y=="\117\115\101\114\100\097\116\097"then W="\091\085\083\069\082\068\065\084\065\093"elseif Y=="\102\117\110\099\116\105\111\110"then W="\091\070\085\078\067\084\073\079\078\093"else W=q end d=tostring(d)G=string.format("\037\115\037\115\037\115\058 \037\115\n",G,C,d,W)end return G end function Util.newRemoteEvent(d,C)local G=C:FindFirstChild(d)if G then Log.Warn.Util("\082\101\109\111\116\101\069\118\101\110\116 \037\115 \117\110\100\101\114 \037\115 \097\108\114\101\097\100\121 \101\120\105\115\116\115",d,C.Name)return G end local W=Instance.new("\082\101\109\111\116\101\069\118\101\110\116",C)W.Name=d return W end function Util.isPlayerInGroup(d,C)for C,G in ipairs(C)do if d:IsInGroup(G)then return true end end end function Util.isPlayerIDInTable(d,C)for C,G in ipairs(C)do if d.UserId==G then return true end end end function Util.lerp(d,C,G)return d*(1-G)+C*G end function Util.getClientFocus()return(CollectionService:GetTagged(Util._clientFocusTag))[1]end function Util.setClientFocus(d)if RunService:IsClient()then for d,C in ipairs(CollectionService:GetTagged(Util._clientFocusTag))do CollectionService:RemoveTag(C,Util._clientFocusTag)end CollectionService:AddTag(d,Util._clientFocusTag)end end Util.highestBotNum=150 function Util.playerIsBot(d)local C=tonumber(string.sub(d.Name,11))if C~=nil and(string.len(d.Name)>10 and(string.sub(d.Name,1,10)=="\114\098\120\114\101\102\116\101\115\116"and(C<=Util.highestBotNum and C>=1)))then return true end return false end function Util.playerIsAlive(d)if d and d.Character then local C=d.Character:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if C and C:GetState()~=Enum.HumanoidStateType.Dead then return true end end return false end function Util.ancestorHasTag(d,C)local G=d while G do if CollectionService:HasTag(G,C)then return true elseif not G.Parent then return false else G=G.Parent end end return false end function Util.getTableKeys(d)local C={}for d in pairs(d)do table.insert(C,d)end return C end function Util.loadCharacter(d)if Conf.force_rthro then local C=Conf.avatarOutfits local G=d.UserId local W=C[G%(#C-1)+1]local q=Players:GetHumanoidDescriptionFromUserId(d.UserId)local Y=Players:GetHumanoidDescriptionFromOutfitId(W)for d,C in pairs(Conf.avatarAnimations)do Y[d]=C end local D=q:GetEquippedEmotes()Y:SetEquippedEmotes(D)d:LoadCharacterWithHumanoidDescription(Y)else d:LoadCharacter()end end
-- Very inefficient, but provides all available info
function Util.debugPrint(tbl, indent)
	local out = ""

	indent = indent or "  "
	local vStr
	local kStr

	for k, v in pairs(tbl) do
		local t = type(v)

		local kConversionSuccess, kConversionResult = pcall(function()
			return tostring(k)
		end)
		if kConversionSuccess then
			kStr = kConversionResult
		else
			kStr = "[" .. type(k) .. "]"
		end

		if t == "table" then
			vStr = string.format("\n%s%s", "", Util.debugPrint(v, indent .. "  "))
		else
			local vConversionSuccess, vConversionResult = pcall(function()
				return tostring(v)
			end)
			if vConversionSuccess then
				vStr = vConversionResult
			else
				vStr = "[" .. type(v) .. "]"
			end
		end

		out = string.format("%s%s%s: %s\n", out, indent, kStr, vStr)
	end

	return out
end

function Util.tableToString(tbl, indent)
	local out = ""

	indent = indent or "  "
	local vStr

	for k, v in pairs(tbl) do
		local t = type(v)

		if t == "table" then
			vStr = string.format("\n%s%s", "", Util.tableToString(v, indent .. "  "))
		elseif t == "nil" then
			vStr = "[nil]"
		elseif t == "string" then
			vStr = string.format('"%s"', v)
		elseif t == "boolean" then
			vStr = Util.boolToString(v)
		elseif t == "userdata" then
			vStr = "[USERDATA]"
		elseif t == "function" then
			vStr = "[FUNCTION]"
		else
			vStr = v
		end

		k = tostring(k)

		out = string.format("%s%s%s: %s\n", out, indent, k, vStr)
	end

	return out
end

function Util.newRemoteEvent(name, parent)
	local existing = parent:FindFirstChild(name)

	if existing then
		Log.Warn.Util("RemoteEvent %s under %s already exists", name, parent.Name)

		return existing
	end

	local event = Instance.new("RemoteEvent", parent)
	event.Name = name

	return event
end

function Util.isPlayerInGroup(player, groups)
	for _, groupId in ipairs(groups) do
		if player:IsInGroup(groupId) then
			return true
		end
	end
end

function Util.isPlayerIDInTable(player, ids)
	for _, id in ipairs(ids) do
		if player.UserId == id then
			return true
		end
	end
end

function Util.lerp(start, stop, t)
	return start * (1 - t) + stop * t
end

function Util.getClientFocus()
	return CollectionService:GetTagged(Util._clientFocusTag)[1]
end

function Util.setClientFocus(instance)
	if RunService:IsClient() then
		-- Remove any tags
		for _, obj in ipairs(CollectionService:GetTagged(Util._clientFocusTag)) do
			CollectionService:RemoveTag(obj, Util._clientFocusTag)
		end

		-- Tag the specified object
		CollectionService:AddTag(instance, Util._clientFocusTag)
	end
end

Util.highestBotNum = 150
function Util.playerIsBot(player)
	local numOnEnd = tonumber(string.sub(player.Name, 11))
	if
		numOnEnd ~= nil
		and string.len(player.Name) > 10
		and string.sub(player.Name, 1, 10) == "rbxreftest"
		and numOnEnd <= Util.highestBotNum
		and numOnEnd >= 1
	then
		return true
	end
	return false
end

function Util.playerIsAlive(player)
	if player and player.Character then
		local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
		if humanoid and humanoid:GetState() ~= Enum.HumanoidStateType.Dead then
			return true
		end
	end
	return false
end

function Util.ancestorHasTag(instance, tag)
	local currentInstance = instance
	while currentInstance do
		if CollectionService:HasTag(currentInstance, tag) then
			return true
		elseif not currentInstance.Parent then
			return false
		else
			currentInstance = currentInstance.Parent
		end
	end

	return false
end

function Util.getTableKeys(tbl)
	local keys = {}

	for k in pairs(tbl) do
		table.insert(keys, k)
	end

	return keys
end

function Util.loadCharacter(player)
	if Conf.force_rthro then
		local avatars = Conf.avatarOutfits
		local effectiveId = player.UserId
		local outfitId = avatars[(effectiveId % (#avatars - 1)) + 1]

		local playerDesc = Players:GetHumanoidDescriptionFromUserId(player.UserId)
		local desc = Players:GetHumanoidDescriptionFromOutfitId(outfitId)

		-- Load animations from configuration
		for property, value in pairs(Conf.avatarAnimations) do
			desc[property] = value
		end

		local emotes = playerDesc:GetEquippedEmotes()
		desc:SetEquippedEmotes(emotes)

		player:LoadCharacterWithHumanoidDescription(desc)
	else
		player:LoadCharacter()
	end
end
