this can be done probably if im right (and im always right because im never wrong ever) using raycasts and some vector magic(that i could explain if you want me to but)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {character}
--prob put this in a loop yk
if not ragdolled and onground then
local results = workspace:Raycast(humanoidRootPart.Position,-Vector3.yAxis*5--[[ray length below the player]],params)
if results then
local angle = math.deg(math.acos(Vector3.yAxis:Dot(results.Normal)))--in degrees
end
end
the only things that i think could be improved is the distance to cast below the player(but it doesnt matter if you check if they are on the ground anyways)