Dont Fall 4.0 - The most interesting way to stop players falling!

Version 4.0 is now available

03.23.25

(Yes I STILL ocassionally work on this - no clue why)

New update includes:

  • Now server sided
  • Now more efficient
  • No longer stores unnecessary player positions
  • Tween mode can be enabled and disabled easily
  • Tweens now display for other players
  • Now includes an offset
  • Now includes better fall threshold
  • Better variable naming
  • Prevents exploiters disabling the no fall script (as it’s now server sided)

Here’s the script: (just copy and paste it into a script inside ‘ServerScriptService’ )

--[[ 
    Simple Dont Fall
--]]

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

-- CONFIG
local FALL_THRESHOLD_SECONDS = 2 --- How long a player must be in freefall before correcting
local PUSH_BACK_OFFSET = 3 --- Studs to push back from the last stable positions
local USE_TWEEN = true --- Set to true to tween the player back smoothly
local TWEEN_TIME = 1.2 --- Duration of the tween (in seconds) if tweening is on
-- EXPERIMENTAL
local POSITION_HISTORY_MAX = 1 --- If you want to modify the script to log the pos history before a fall event occured

local playerData = {}

local function addPosition(userId, position)
	if not playerData[userId] then
		playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
	end
	local history = playerData[userId].history
	table.insert(history, position)
	if #history > POSITION_HISTORY_MAX then
		table.remove(history, 1)
	end
end

local function getLastStablePosition(userId)
	if playerData[userId] and #playerData[userId].history > 0 then
		return playerData[userId].history[#playerData[userId].history]
	end
	return nil
end

local function isOnSolidGround(character)
	local rootPart = character:FindFirstChild("HumanoidRootPart")
	if not rootPart then return false end

	local rayOrigin = rootPart.Position
	local rayDirection = Vector3.new(0, -5, 0) 
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {character} 
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	local result = workspace:Raycast(rayOrigin, rayDirection, rayParams)
	return (result ~= nil)
end

local function teleportPlayerToStablePosition(player, character)
	local stablePosition = getLastStablePosition(player.UserId)
	if stablePosition and character and character:FindFirstChild("HumanoidRootPart") then
		local rootPart = character.HumanoidRootPart
		local currentPosition = rootPart.Position

		local direction = (stablePosition - currentPosition).Unit
		local targetPosition = stablePosition + direction * PUSH_BACK_OFFSET

		local savedRotation = playerData[player.UserId].fallRotation or rootPart.Orientation.Y
		local targetCFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(savedRotation), 0)

		if USE_TWEEN then
			rootPart.Anchored = true
			local tweenInfo = TweenInfo.new(TWEEN_TIME, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
			local tween = TweenService:Create(rootPart, tweenInfo, {CFrame = targetCFrame})
			tween.Completed:Connect(function()
				rootPart.Anchored = false
			end)
			tween:Play()
		else
			rootPart.CFrame = targetCFrame
		end

		if rootPart:IsA("BasePart") then
			rootPart.Velocity = Vector3.new(0, 0, 0)
		end

		print("Player has been teleported - " .. player.Name .. " - to previous position: " .. tostring(targetPosition) .. " with rotation: " .. savedRotation)
	else
		print("Uh oh :( ")
	end
end

local function monitorPlayer(character, player)
	local humanoid = character:WaitForChild("Humanoid")
	local userId = player.UserId

	playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }

	humanoid.StateChanged:Connect(function(oldState, newState)
		if newState == Enum.HumanoidStateType.Freefall then
			if not playerData[userId].fallStart then
				playerData[userId].fallStart = tick()
				local rootPart = character:FindFirstChild("HumanoidRootPart")
				if rootPart then
					local y= rootPart.Orientation.Y
					playerData[userId].fallRotation = y
				end
			end
		elseif newState == Enum.HumanoidStateType.Landed 
			or newState == Enum.HumanoidStateType.Running 
			or newState == Enum.HumanoidStateType.RunningNoPhysics then
			playerData[userId].fallStart = nil  
			playerData[userId].fallRotation = nil
		end
	end)
end

local function onCharacterAdded(character, player)
	monitorPlayer(character, player)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		onCharacterAdded(character, player)
	end)
end)

Players.PlayerRemoving:Connect(function(player)
	playerData[player.UserId] = nil
end)

RunService.Heartbeat:Connect(function()
	for _, player in pairs(Players:GetPlayers()) do
		local character = player.Character
		if character and character:FindFirstChild("HumanoidRootPart") and character:FindFirstChild("Humanoid") then
			local userId = player.UserId
			local humanoid = character.Humanoid

			if isOnSolidGround(character) then
				addPosition(userId, character.HumanoidRootPart.Position)
			end

			if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
				local fallStart = playerData[userId] and playerData[userId].fallStart
				if fallStart and (tick() - fallStart) >= FALL_THRESHOLD_SECONDS then
					teleportPlayerToStablePosition(player, character)
					playerData[userId].fallStart = nil
					playerData[userId].fallRotation = nil
				end
			end
		end
	end
end)

68 Likes

Really liking this, have found two glitches which would be nice if you could fix though the first of which is double jumping (jumping twice in a row). For some reason it starts to initiate the Don’t Fall script and I float back to the location of my first jump. The second glitch is if you jump just after floating back to your previous location you start to float midair (I can provide a video if you like but you should be able to reproduce this easily enough.)

Overall though really great idea :+1:

3 Likes

Will update and fix this in the future. Thanks for letting me know.

4 Likes

Hi, I haven’t been able to reproduce the Double Jump activation bug. Can you provide a small clip?

2 Likes

Sometimes its three jumps sometimes its two jumps:

https://gyazo.com/fb5c33f55fd051b3f9b7f784c368a74d

https://gyazo.com/cbe407e320f7857c2582d1d985a1b13f

Also found out then when I jump on the edge it acts normally but when I walk off the edge it holds me in position afterwards:

https://gyazo.com/2f0ef1c1da33c602169a3a050eaacec5

1 Like

Actively working to fix this and in the process add settings to the module. :+1:

3 Likes

Updated to fix bugs found by @Alexander_Ashford

  • Fixed issue where the module would teleport you to the latest CFrame instead of looping through to find the safest point to move the player to.

  • Fixed issue where jumping multiple times can activate Don’t Fall (Hopefully)

The module now also comes with an ‘Instant Teleport’ mode that can be toggled using the following: Antifall.InstantTeleport(true) or Antifall.InstantTeleport(false)

1 Like

Lovely contribution to the DevForum Community!

Such a unique asset that after a quick search, isn’t anywhere else on the Forum! I’m sure people will make great use of this! :slight_smile:

Thanks for the contribution!
The_Marshlet

2 Likes

Thank you for this release!

I can already see a lot of people using this in future. Not sure if you’ve already added it, but you could also add in settings the amount of time it will take to return player back to the safe spot, pretty sure you can easily do it with the TweenService.

2 Likes

Thanks for the suggestion! I pushed out an update where you can edit the activation time and tween time.

Activation Time Example:
DontFall.EditActivationTime(1) --Player must be falling for 1 second before Don't Fall activates.

Tween Time Example:
DontFall.EditTweenTime(0.5) --When activated it will take 0.5 seconds to move the player back on a platform.

Edit: Thanks for all the support. Please keep giving awesome suggestions.

4 Likes

This module is a awesome alternative to invisible wall! It’s awesome.

:rofl:

1 Like

This is actually very creative…

I useally do it by teleportation… But this is actually a more creative and entertaining…

2 Likes

VERSION 2 - OUT NOW :yum:

image

Don’t Fall V2
[ Get the NEW module from HERE ! ]

What is Don’t Fall?
Don’t fall creates a log of the last 5 known positions of the player prior to falling and then allows you to either tween them back or teleport them back after they have been falling for X amount of seconds.

Using the new Don’t Fall is slightly different from the previous versions.

First, we require the module.

local DontFall = require(script:WaitForChild("DontFallV2"))

Then we create our tween information

local Info = TweenInfo.new(
	0.8, 
	Enum.EasingStyle.Sine, 
	Enum.EasingDirection.Out, 
	0, 
	false, 
	0 
)

And then create a new DontFall providing it with the information required

local DF = DontFall.new(
    0.5, --Time before a fall is counted (seconds)
	false, --Skip tween
	Info) -- Tween info

And now we have to start it by doing

DF:Start()
--or we can stop it by doing DF:Stop()

It also shouldn’t have many bugs now but if you do find one let me know and it now uses rays to find if the player is on a part so infinite fall loops shouldn’t exist now.

Enjoy :slight_smile:

2 Likes

The module is offsale btw

3 Likes

Version 4.0 is now available

(Yes I STILL ocassionally work on this - no clue why)

New update includes:

  • Now server sided
  • Now more efficient
  • No longer stores unnecessary player positions
  • Tween mode can be enabled and disabled easily
  • Tweens now display for other players
  • Now includes an offset
  • Now includes better fall threshold
  • Better variable naming
  • Prevents exploiters disabling the no fall script (as it’s now server sided)

Here’s the script: (just copy and paste it into a script inside ‘ServerScriptService’ )

--[[ 
    Simple Dont Fall
--]]

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

-- CONFIG
local FALL_THRESHOLD_SECONDS = 2 --- How long a player must be in freefall before correcting
local PUSH_BACK_OFFSET = 3 --- Studs to push back from the last stable positions
local USE_TWEEN = true --- Set to true to tween the player back smoothly
local TWEEN_TIME = 1.2 --- Duration of the tween (in seconds) if tweening is on
-- EXPERIMENTAL
local POSITION_HISTORY_MAX = 1 --- If you want to modify the script to log the pos history before a fall event occured

local playerData = {}

local function addPosition(userId, position)
	if not playerData[userId] then
		playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
	end
	local history = playerData[userId].history
	table.insert(history, position)
	if #history > POSITION_HISTORY_MAX then
		table.remove(history, 1)
	end
end

local function getLastStablePosition(userId)
	if playerData[userId] and #playerData[userId].history > 0 then
		return playerData[userId].history[#playerData[userId].history]
	end
	return nil
end

local function isOnSolidGround(character)
	local rootPart = character:FindFirstChild("HumanoidRootPart")
	if not rootPart then return false end

	local rayOrigin = rootPart.Position
	local rayDirection = Vector3.new(0, -5, 0) 
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {character} 
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	local result = workspace:Raycast(rayOrigin, rayDirection, rayParams)
	return (result ~= nil)
end

local function teleportPlayerToStablePosition(player, character)
	local stablePosition = getLastStablePosition(player.UserId)
	if stablePosition and character and character:FindFirstChild("HumanoidRootPart") then
		local rootPart = character.HumanoidRootPart
		local currentPosition = rootPart.Position

		local direction = (stablePosition - currentPosition).Unit
		local targetPosition = stablePosition + direction * PUSH_BACK_OFFSET

		local savedRotation = playerData[player.UserId].fallRotation or rootPart.Orientation.Y
		local targetCFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(savedRotation), 0)

		if USE_TWEEN then
			rootPart.Anchored = true
			local tweenInfo = TweenInfo.new(TWEEN_TIME, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
			local tween = TweenService:Create(rootPart, tweenInfo, {CFrame = targetCFrame})
			tween.Completed:Connect(function()
				rootPart.Anchored = false
			end)
			tween:Play()
		else
			rootPart.CFrame = targetCFrame
		end

		if rootPart:IsA("BasePart") then
			rootPart.Velocity = Vector3.new(0, 0, 0)
		end

		print("Player has been teleported - " .. player.Name .. " - to previous position: " .. tostring(targetPosition) .. " with rotation: " .. savedRotation)
	else
		print("Uh oh :( ")
	end
end

local function monitorPlayer(character, player)
	local humanoid = character:WaitForChild("Humanoid")
	local userId = player.UserId

	playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }

	humanoid.StateChanged:Connect(function(oldState, newState)
		if newState == Enum.HumanoidStateType.Freefall then
			if not playerData[userId].fallStart then
				playerData[userId].fallStart = tick()
				local rootPart = character:FindFirstChild("HumanoidRootPart")
				if rootPart then
					local y= rootPart.Orientation.Y
					playerData[userId].fallRotation = y
				end
			end
		elseif newState == Enum.HumanoidStateType.Landed 
			or newState == Enum.HumanoidStateType.Running 
			or newState == Enum.HumanoidStateType.RunningNoPhysics then
			playerData[userId].fallStart = nil  
			playerData[userId].fallRotation = nil
		end
	end)
end

local function onCharacterAdded(character, player)
	monitorPlayer(character, player)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		onCharacterAdded(character, player)
	end)
end)

Players.PlayerRemoving:Connect(function(player)
	playerData[player.UserId] = nil
end)

RunService.Heartbeat:Connect(function()
	for _, player in pairs(Players:GetPlayers()) do
		local character = player.Character
		if character and character:FindFirstChild("HumanoidRootPart") and character:FindFirstChild("Humanoid") then
			local userId = player.UserId
			local humanoid = character.Humanoid

			if isOnSolidGround(character) then
				addPosition(userId, character.HumanoidRootPart.Position)
			end

			if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
				local fallStart = playerData[userId] and playerData[userId].fallStart
				if fallStart and (tick() - fallStart) >= FALL_THRESHOLD_SECONDS then
					teleportPlayerToStablePosition(player, character)
					playerData[userId].fallStart = nil
					playerData[userId].fallRotation = nil
				end
			end
		end
	end
end)

4 Likes