Edit: Ok, NOW the saturation decrease works. But the fall damage script decides not to work sometimes. Can anyone help me there?
Fall damage script:
local module = require(game.ReplicatedStorage.GlobalFunctions)
game.Players.PlayerAdded:Connect(function(p)
p.CharacterAdded:Connect(function(c)
local startPos
c.Humanoid.StateChanged:Connect(function(old, new)
if new == Enum.HumanoidStateType.Freefall then
startPos = c.HumanoidRootPart.Position.Y
elseif new == Enum.HumanoidStateType.Landed then
if not startPos then return end
local landPos = c.HumanoidRootPart.Position.Y
if startPos < landPos then return end
local fallDamage = module.RoundNumber((startPos - landPos) / 2)
c.Humanoid:TakeDamage(fallDamage)
print(fallDamage)
end
end)
end)
end)
Do know have a general idea of where the problem is? I can’t really assume anything from the code above and I don’t really understand what you mean when you say it “doesn’t work sometimes”.
I mean, sometimes, the code works, and I take damage, and sometimes, I don’t take damage.
I’m pretty sure it happens at one of the if statements checking the HumanoidStateType, and it doesn’t run through.
Did you print in each if statement to see which one is causing problems? You should probably start printing and figuring out where the problem is coming from so you can get a good idea on how to fix the issue.
Also I don’t know if this would fix it but have you tried switching your vector subtraction to (landPos - startPos). Just a suggestion or something to try.
Hmm… Tried putting prints and I got a really weird result.
Here’s that code so you know.
local module = require(game.ReplicatedStorage.GlobalFunctions)
game.Players.PlayerAdded:Connect(function(p)
p.CharacterAdded:Connect(function(c)
local startPos
c.Humanoid.StateChanged:Connect(function(old, new)
if new == Enum.HumanoidStateType.Freefall then
startPos = c.HumanoidRootPart.Position.Y
print("'startpos' set")
elseif new == Enum.HumanoidStateType.Landed then
print("landed")
if not startPos then return end
local landPos = c.HumanoidRootPart.Position.Y
print("ok")
if startPos < landPos then return end
print("cool")
local fallDamage = module.RoundNumber((startPos - landPos) / 2)
print("fall damage set")
c.Humanoid:TakeDamage(fallDamage)
print(fallDamage)
end
end)
end)
end)
When I fall a long distance, it only prints 'startpos' set, and when I jump, it only gets to ok. Does this mean it doesn’t detect the humanoid state being changed when it lands?
As for this:
I didn’t do that because, remember, when a player falls, its starting position is usually always higher than the landing position, so subtracting the land position from the start position will always return a negative number.
We’ll come back to this tomorrow.
I’ll play around with this in studio but I don’t think landed is set once the player falls from a high distance. Personally I would use ray casting to detect these things, but I have no clue how intensive it would be if done on the server-side. Also thanks for the correction I very much misunderstood that lol.
By the way, I got this code from a “HowToRoblox” tutorial video, because I was too lazy to figure it out it myself.
Lol I know that feeling. So I was playing around in studio and I found out that “Landed” isn’t always set on when landing from a jump or falling from a long distance. But when you jump for the first time, as soon as you jump the state is set to “FreeFalling” which means on a jump you’d have a start pos close to the ground and if your if statement condition is met I believe you return. (if startPos < landPos then return end)
If “Landed” isn’t always set on land, could I just listen for c.RightFoot.Touched?
If I were you I would look into RayCasting and come up with a system using that as it, in my opinion, will be more consistent that just depending on the humanoid state changing. Or you could do that as well. You’d still have to cast a ray downward to validate that the player has landed on something.
As a reference this is some functions I use in my basketball game to detect if the player jumps and lands.
function Events:OnGround(OBJ, BALL)
local court = workspace:FindFirstChild(BALL.Name)
local chars = GetCharacters()
local leftleg = OBJ["Left Leg"]
local ignore = {court.Court.Places, unpack(chars)}
--[ CAST ]--
local ray = Ray.new(leftleg.Position, -leftleg.CFrame.UpVector * MAX_DISTANCE)
local part, pos = workspace:FindPartOnRayWithIgnoreList(ray, ignore)
--[ Check ]--
if ((leftleg.Position - pos).magnitude > 1.3) then
return false
else
return true
end
end
What does OBJ mean in your method MathModule:OnGround?
Oh sorry that was a completely different function for something else. I edited it but if you don’t see it :
function Events:OnGround(OBJ, BALL)
local court = workspace:FindFirstChild(BALL.Name)
local chars = GetCharacters()
local leftleg = OBJ["Left Leg"]
local ignore = {court.Court.Places, unpack(chars)}
--[ CAST ]--
local ray = Ray.new(leftleg.Position, -leftleg.CFrame.UpVector * MAX_DISTANCE)
local part, pos = workspace:FindPartOnRayWithIgnoreList(ray, ignore)
--[ Check ]--
if ((leftleg.Position - pos).magnitude > 1.3) then
return false
else
return true
end
end
It casts a ray down from the players left leg, with an ignore list of course. But your situation may be different.
But then how do I get when the player landed?
--[ Check ]--
if ((leftleg.Position - pos).magnitude > 1.3) then
return false
else
return true
end
Right here if it returns true, which means the player is closest to something they can land on, then they “Landed”.
I’m pretty sure I’m not doing it right.
local module = require(game.ReplicatedStorage.GlobalFunctions)
game.Players.PlayerAdded:Connect(function(p)
p.CharacterAdded:Connect(function(c)
local startPos
c.Humanoid.StateChanged:Connect(function(old, new)
if new == Enum.HumanoidStateType.Freefall then
startPos = c.HumanoidRootPart.Position.Y
print("'startpos' set")
local ray = Ray.new(c.LeftFoot.Position, -c.LeftFoot.CFrame.UpVector * 5000)
local part, pos = workspace:FindPartOnRay(ray, c)
repeat wait() until (c.LeftFoot.Position - pos).magnitude <= 1
local landPos = c.HumanoidRootPart.Position.Y
if startPos < landPos then return end
local fallDamage = module.RoundNumber((startPos - landPos) / 2)
c.Humanoid:TakeDamage(fallDamage)
print(fallDamage)
end
end)
end)
end)
It doesn’t do anything? If not check to see if the magnitude for “Landing” is actually 1 sometimes it’s a little bit more than that.
No. Doesn’t work by doing that.
OH, so I figured out what was wrong and I was overthinking the whole thing. Sometimes those connections like “PlayerAdded” run after the first couple of players join the server, so you’d have to use a for loop to account for them as well. For Example :
--// Pos Cache
local startPosCache = {} --> Since we're tracking mulitple players in one script we need to handle them accordingly.
--// Detections Function
local function StateChanged(new, c)
if new == Enum.HumanoidStateType.Freefall then
--// Cache start position
startPosCache[c.Name] = c.HumanoidRootPart.Position.Y
--// Variables
local startPos = startPosCache[c.Name]
local ray = Ray.new(c.LeftFoot.Position, -c.LeftFoot.CFrame.UpVector * 5000)
local part, pos = workspace:FindPartOnRay(ray, c)
--// Wait for player to land
repeat
wait()
until (c.LeftFoot.Position - pos).magnitude <= 1
--// Establish final Position
local landPos = c.HumanoidRootPart.Position.Y
--// Check positions
if startPos < landPos then
startPosCache[c.Name] = nil
return
end
--// Apply fall damage
local fallDamage = (startPos - landPos)
c.Humanoid:TakeDamage(fallDamage)
startPosCache[c.Name] = nil
print(fallDamage)
end
end
--// Player Added
game.Players.PlayerAdded:Connect(function(p)
p.CharacterAdded:Connect(function(c)
c.Humanoid.StateChanged:Connect(function(old, new)
StateChanged(new, c)
end)
end)
end)
--// Apply for players already in game
for _, player in pairs(game.Players:GetPlayers()) do
local c = player.Character
--// Apply
c.Humanoid.StateChanged:Connect(function(old, new)
StateChanged(new, c)
end)
end