Is a reliable, works 100% of the time, fall damage script even possible atm?

You could connect the .Touched event for each part parented to the character(.Touched apparently works on terrain, although I think it fires when the player touches water also). You could also check the Humanoid’s FloorMaterial, and if it’s set to “Air” then the character isn’t touching the ground.

Then for damage, you could take damage based on HumanoidRootPart’s Y velocity(only damages the player when he falls), or HumanoidRootPart’s Velocity.Magnitude(which would allow damage from the player moving at high velocities in other directions).

1 Like

You can utilize the Humanoid.StateChanged event to achieve your goal. Every time if your humanoid state changes to freefall, you can get the y position once it changed, then once the state changes to Landed, you can get the Y value of the HumanoidRootPart when the Humanoid lands, and take the difference of the two Y values. Depending on the difference, you can scale your damage accordingly.

What I would do is do a combination of detecting velocity of the y axis of the torso and GetPropertyChangedSignal for FloorMaterial on the Humanoid. If the floor material is changed from air to some material and the velocity of the torso was some value then you could say they fell from a certain height.

2 Likes

You would then have to run something every frame(this wouldn’t be too bad, because there isn’t intense computing power going on):

1A. Check if player is off the ground via raycasting
1B. If player is not off the ground, repeat 1A until they are off the ground

    2. Get the velocity of the player, whilst raycasting downwards equal to the velocity(so if the velocity is "0, 5, 0", you would rayacst "5" studs downwards)

        3A. If it hits a part, wait for the player to hit the ground, then take corresponding damage
        3B. If not, keep doing #2 until 3A is met

I think comparing start and end heights would cause problems. Isn’t that what he’s doing already?

I’ve never needed to set a specific height for fall damage, but if you need to, you could calculate height from velocity with a little math:

local height = (velocity*velocity)/(2*workspace.Gravity)

I made a fall damage script. It worked every time I tested it, and doesn’t damage a player when he falls into the water.

--height at which the player starts to take damage
local minimumDamageHeight = 20
--height at which the player takes maximumDamage damage
local maximumDamageHeight = 100
--if height exceeds maximumDamageHeight,
--player will take over maximumDamage damage
local maximumDamage = 100

local velocity

-- linear interpolation function
local function lerp(a,b,t)
	return (1-t)*a+b*t
end

--inverse of lerp function
local function invLerp(a,b,v)
	return (v-a)/(b-a)
end

--remaps value, used for max and min damage heights.
local function remap(iMin,iMax,oMin,oMax,v)
	return lerp(oMin,oMax,invLerp(iMin,iMax,v))
end

-- fired when FloorMaterial changes
script.Parent.Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
	--calculate fall height from velocity
	local height = (velocity * velocity)/(2*workspace.Gravity)

	--remap damage based on variables
	local damage = remap(minimumDamageHeight,maximumDamageHeight,0,maximumDamage,height)
	--prevent negative damage
	damage = math.max(damage,0)
	--apply damage
	script.Parent.Humanoid:TakeDamage(damage)
end)

--fires before each physics update
game:GetService("RunService").Stepped:Connect(function(time,step)
	--update velocity variable
	velocity = script.Parent.HumanoidRootPart.Velocity.Y
end)

I put this in a LocalScript in StarterCharacterScripts.

1 Like

while this works great, what worries me is that it’s a local script, wouldn’t this make it vulnerable to client side manipulation?

Its not even a localscript, you put it in a CharacterAdded function.

1 Like

you just can’t depend on loops, they will not always be in sync with physics/movement.

your approach needs to be event based. (using custom wrapped events or provided events, doesn’t matter)

what do you mean? i don’t get it, it IS a local script

I tried to transfer it to a severscript inside StarterCharacterScripts, however even tho there are no errors it doesn’t work

It seems pretty easy for players to hack the client or the server(I’ve heard you can download programs to do it for you), so I don’t worry about it. In this case a client side script works better.

Probably the reason it doesn’t work in a server script is the .Stepped function. The server and client aren’t quite synced, so there might be a little delay(and so velocity is low by the time GetPropertyChangedSignal(“FloorMaterial”) gets fired.

The loop I use is called just before physics are calculated, so it is in sync. I tried getting the velocity only after an event was fired(tried multiple events), but it had already responded to collision and was set to 0. For fall damage, you need the velocity on the previous physics frame, which can’t be done with just events.

Here’s an article about roblox and hacking:

yes you might be right, i tried using heartbeat instead but still velocity is around 0 when the player lands

actually from what i have read, serverside scripts can’t be accessed by anything, unlike local scripts they cannot be decompiled by third party software. I will consult others here on devforum regarding this. Thank you either way

I made a script once that measured the highest value of the changing velocity property of a part. seems to work well. (Used it to measure projectile speeds on a physics catapult)

How tho? Would it allow for migration of his script server side? I am really reluctant to use a local script for damage

It assumes there is no slowing down then reaccelerating before impact.

If you had it run (or something similar) each freefall, it could work. Then you just apply damage base on max impact speed.

local rock = script.Parent

local maxVelocity = 0
local currentIsLarger = true

while currentIsLarger do
	if maxVelocity <= rock.Velocity.Magnitude then
		maxVelocity = rock.Velocity.Magnitude
	else
		currentIsLarger = false
	end
	wait()
end
print(maxVelocity)

If you haven’t gotten a solution to this yet, allow me to help;
State changed should work reliably, as I’ve used it before on my own fall damage scripts and I haven’t found any problems yet. You just have to first have a function saying when its changed (by using an if statement to check if the state is Enum.HumanoidStateType.Freefall). Then have another function inside the previous one saying when it’s changed back to Enum.HumanoidStateType.Landed.

Example:

game.Players.PlayerAdded:Connect(function(Player)
	Player.CharacterAdded:Connect(function()
		local StateChangedFunction
		StateChangedFunction = Player.Character.Humanoid.StateChanged:Connect(function(OldState, NewState)
			if NewState ~= Enum.HumanoidStateType.Jumping then
				if NewState == Enum.HumanoidStateType.Freefall then
					local LastHeight = Player.Character.HumanoidRootPart.Position.Y
					local Function
					Function = Player.Character.Humanoid.StateChanged:Connect(function(OldState2, NewState2)
						if OldState2 == Enum.HumanoidStateType.Freefall then
							Player.Character.Humanoid:TakeDamage(math.abs(LastHeight - Player.Character.HumanoidRootPart.Position.Y))
							wait()
							if Function then
								Function:Disconnect()
							end
						else
							if Function then
								Function:Disconnect()
							end
						end
					end)
				end
			end
		end)
		
		Player.Character.Humanoid.Died:Connect(function()
			if StateChangedFunction then
				StateChangedFunction:Disconnect()
			end
		end)
	end)
end)

Please note that this script is purely for an example, and is not recommended for an actual game due to it damaging the player whenever they slightly fall. (edits should be made)
(This is also a server script inside ServerScriptService)