Divorce leaving a seat and jumping, but leave keybind the same

User Story:

As a developer, it’s not possible to use seats in games where jumping is disabled. Not all games are platformers – some, such as Brick Bronze, disable jumping entirely to craft their own experience. In these types of games, seats cannot be used because seats require players to jump to exit the seat.

A Solution:

I was thinking that when the Jumping state of Humanoids was disabled, we could just destroy the joint and let them walk away without triggering a jump. Upon further thinking, I was wondering why characters jump to leave seats in the first place. It doesn’t really make sense for characters to jump out of a seat – normal people just stand up.

Regardless of whether the jump state is disabled or not, exiting a seat should not initiate a jump. It should just disconnect the character from the seat, and humanoids can automatically shift the character up as they would do if they were clipping into something, or seats can manually move the character up to the standing position.

It would probably be a good idea to look into how this would affect existing games though, to ensure they wouldn’t break. We would also need a way to prevent characters from leaving seats since it would no longer be possible to prevent them by disabling the jumping state.

22 Likes

I think that you should be able to leave a seat with backspace, like how you can dismount a skateboard with it

local player = game.Players.LocalPlayer
repeat wait() until player.Character
local hum = player.Character:WaitForChild("Humanoid")

game:GetService("UserInputService").JumpRequest:connect(function()
	if hum.Sit then
		hum.JumpPower = 50
		wait()
		hum.JumpPower = 0
	end
end)

Kind of hacky but if anyone is having this problem, there’s a quick local-script workaround. :slight_smile:

I agree that jumping out of a seat is weird behavior though.

4 Likes

If memory serves, the ControlScripts simply set Jump to true when you give it jump input. For those inclined, they could change this behavior so that if Sit is true, set it to false instead of setting jump to true.

EDIT: The principle is sound. Setting Sit to false will make players detach from the seat. However, this does not play well with the way ROBLOX handles jump input. Specifically, any frame the jump command is given and the humanoid is allowed to jump (not falling, not already jumping), it will. Basically, you detach from the seat in frame 1, and immediately begin jumping in frame 2, so there’s no net change in behavior.

There are 2 possibilities to fix this: disable the jump command when alighting so players have to let go and press the jump key again to begin jumping, or abstract the jump command and disable the abstracted command for a period of time after alighting.

Good idea, this works well:

local player = game.Players.LocalPlayer
repeat wait() until player.Character
local hum = player.Character:WaitForChild("Humanoid")

hum:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)

game:GetService("UserInputService").JumpRequest:connect(function()
	if hum.Sit then
		hum.Sit = false
	end
end)

:slight_smile:

5 Likes

Yup. I had to wait 0.2 seconds before allowing the player to jump again. This kind of works for desired behavior, right?

local player = game.Players.LocalPlayer
repeat wait() until player.Character
local hum = player.Character:WaitForChild("Humanoid")

game:GetService("UserInputService").JumpRequest:connect(function()
	local oldJumpPower = hum.JumpPower
	if hum.Sit then
		hum.JumpPower = 0
		hum.Sit = false
		wait(0.2)
		hum.JumpPower = oldJumpPower
	end
end)
7 Likes

So, I went the abstracted boolean route and got this far. This is a replacement for the MasterControl module which should fully enable people to have seats and no jumping.

It works well except for one critical bug: if you sit in a seat when isJumping is true, the SeatPart will not update accordingly and you will be unable to sit or jump until you reset.

Further modifications include public API to enable the dismount behavior and having the dismount behavior disabled by default, with the idea that this could be a PR to the Actual Real Repo.

--[[
	// FileName: MasterControl
	// Version 1.0
	// Written by: jeditkacheff
	// Description: All character control scripts go thru this script, this script makes sure all actions are performed
--]]

-- [[ Constants ]]--
local ZERO_VECTOR3 = Vector3.new(0, 0, 0)
local STATE_JUMPING = Enum.HumanoidStateType.Jumping
local STATE_FREEFALL = Enum.HumanoidStateType.Freefall
local STATE_LANDED = Enum.HumanoidStateType.Landed

--[[ Local Variables ]]--
local MasterControl = {}

local Players = game:GetService('Players')
local RunService = game:GetService('RunService')
local VRService = game:GetService('VRService')
local VREnabled = VRService.VREnabled

while not Players.LocalPlayer do
	Players.PlayerAdded:wait()
end
local LocalPlayer = Players.LocalPlayer
local LocalCharacter = LocalPlayer.Character
local CachedHumanoid = nil

local isJumping = false
local lastDismount = 0
local moveValue = Vector3.new(0, 0, 0)

local isJumpEnabled = true
local areControlsEnabled = true
local dismountDebounceTime = 0.333

--[[ Local Functions ]]--
function MasterControl:GetHumanoid()
	if LocalCharacter then
		if CachedHumanoid then
			return CachedHumanoid
		else
			CachedHumanoid = LocalCharacter:FindFirstChildOfClass("Humanoid")
			return CachedHumanoid
		end
	end
end

VRService.Changed:connect(function(prop)
	if prop == "VREnabled" then
		VREnabled = VRService.VREnabled
	end
end)

local characterAncestryChangedConn = nil
local characterChildRemovedConn = nil
local function characterAdded(character)
	if characterAncestryChangedConn then
		characterAncestryChangedConn:disconnect()
	end

	if characterChildRemovedConn then
		characterChildRemovedConn:disconnect()
	end	
	
	LocalCharacter = character
	CachedHumanoid = LocalCharacter:FindFirstChildOfClass("Humanoid")
	characterAncestryChangedConn = character.AncestryChanged:connect(function()
		if character.Parent == nil then
			LocalCharacter = nil
		else
			LocalCharacter = character
		end
	end)
	
	characterChildRemovedConn = character.ChildRemoved:connect(function(child)
		if child == CachedHumanoid then
			CachedHumanoid = nil
		end
	end)
end

if LocalCharacter then
	characterAdded(LocalCharacter)
end
LocalPlayer.CharacterAdded:connect(characterAdded)

local getHumanoid = MasterControl.GetHumanoid
local moveFunc = LocalPlayer.Move
local getUserCFrame = VRService.GetUserCFrame
local updateMovement = function()
	
	if not areControlsEnabled then return end
	
	local recentlyDismounted = lastDismount+dismountDebounceTime >= tick()
	
	local humanoid = getHumanoid()
	if not humanoid then return end
	--print("isJumpEnabled",isJumpEnabled,"isJumping",isJumping,"PlatformStand",humanoid.PlatformStand,"SeatPart",humanoid.SeatPart)
	if isJumping and humanoid.SeatPart then
		humanoid.Sit = false
		lastDismount = tick()
	elseif isJumpEnabled and isJumping and not humanoid.PlatformStand and not recentlyDismounted then
		local state = humanoid:GetState()
		if state ~= STATE_JUMPING and state ~= STATE_FREEFALL and state ~= STATE_LANDED then
			humanoid.Jump = isJumping
		end
	end
	
	local adjustedMoveValue = moveValue
	if VREnabled and workspace.CurrentCamera.HeadLocked then
		local vrFrame = getUserCFrame(VRService, Enum.UserCFrame.Head)
		local lookVector = Vector3.new(vrFrame.lookVector.X, 0, vrFrame.lookVector.Z).unit
		local rotation = CFrame.new(ZERO_VECTOR3, lookVector)
		adjustedMoveValue = rotation:vectorToWorldSpace(adjustedMoveValue)
	end

	moveFunc(LocalPlayer, adjustedMoveValue, true)
end

local function manualDismount()
	local humanoid = getHumanoid()
	if not humanoid then return end
	if humanoid.SeatPart then
		humanoid.Sit = false
		lastDismount = tick()
	end
end

local function manualJump()
	local humanoid = getHumanoid()
	if not humanoid then return end
	if lastDismount+dismountDebounceTime < tick() and not humanoid.PlatformStand then
		humanoid.Jump = isJumping
	end
end

--[[ Public API ]]--
function MasterControl:Init()
	RunService:BindToRenderStep("MasterControlStep", Enum.RenderPriority.Input.Value, updateMovement)
end

function MasterControl:Enable()
	areControlsEnabled = true
	isJumpEnabled = true
	
	if self.ControlState.Current then
		self.ControlState.Current:Enable()
	end
end

function MasterControl:Disable()
	
	if self.ControlState.Current then
		self.ControlState.Current:Disable()
	end
	
	--After current control state is disabled, moveValue has been set to zero,
	--Call updateMovement one last time to make sure this propagates to the engine -
	--Otherwise if disabled while humanoid is moving, humanoid won't stop moving.
	updateMovement()
	
	isJumping = false
	areControlsEnabled = false
end

function MasterControl:EnableJump()
	isJumpEnabled = true
	if areControlsEnabled and self.ControlState:IsTouchJumpModuleUsed() then
		self.TouchJumpModule:Enable()
	end
end

function MasterControl:DisableJump(affectGui)
	isJumpEnabled = false
	if affectGui and self.ControlState:IsTouchJumpModuleUsed() then
		self.TouchJumpModule:Disable()
	end
end

function MasterControl:AddToPlayerMovement(playerMoveVector)
	moveValue = Vector3.new(moveValue.X + playerMoveVector.X, moveValue.Y + playerMoveVector.Y, moveValue.Z + playerMoveVector.Z)
end

function MasterControl:GetMoveVector()
	return moveValue
end

function MasterControl:SetIsJumping(jumping)
	manualDismount()
	if not isJumpEnabled then return end
	isJumping = jumping
	if jumping then
		manualJump()
	end
end

function MasterControl:DoJump()
	manualDismount()
	if not isJumpEnabled then return end
	manualJump()
end

function MasterControl:SetDismountDebounce(amount)
	dismountDebounceTime = amount
end


return MasterControl

2 Likes