local K=require(script.Parent:WaitForChild("\066\097\115\101\067\104\097\114\097\099\116\101\114\067\111\110\116\114\111\108\108\101\114"))local L=setmetatable({},K)L.__index=L local M,J=pcall(function()return(UserSettings()):IsUserFeatureEnabled("\085\115\101\114\080\108\097\121\101\114\083\099\114\105\112\116\115\066\105\110\100\065\116\080\114\105\111\114\105\116\121")end)local V=M and J function L.new(M)local J=setmetatable(K.new(),L)J.CONTROL_ACTION_PRIORITY=M J.textFocusReleasedConn=nil J.textFocusGainedConn=nil J.windowFocusReleasedConn=nil J.forwardValue=0 J.backwardValue=0 J.leftValue=0 J.rightValue=0 return J end function L.Enable(L,K)if not UserInputService.KeyboardEnabled then return false end if K==L.enabled then return true end L.forwardValue=0 L.backwardValue=0 L.leftValue=0 L.rightValue=0 L.moveVector=ZERO_VECTOR3 L.isJumping=false if K then L:BindContextActions()L:ConnectFocusEventListeners()else L:UnbindContextActions()L:DisconnectFocusEventListeners()end L.enabled=K return true end function L.UpdateMovement(L,K)if K==Enum.UserInputState.Cancel then L.moveVector=ZERO_VECTOR3 else L.moveVector=Vector3.new(L.leftValue+L.rightValue,0,L.forwardValue+L.backwardValue)end end function L.BindContextActions(K)local L=function(L,M,J)K.forwardValue=M==Enum.UserInputState.Begin and-1 or 0 K:UpdateMovement(M)if V then return Enum.ContextActionResult.Sink end end local M=function(L,M,J)K.backwardValue=M==Enum.UserInputState.Begin and 1 or 0 K:UpdateMovement(M)if V then return Enum.ContextActionResult.Sink end end local J=function(L,M,J)K.leftValue=M==Enum.UserInputState.Begin and-1 or 0 K:UpdateMovement(M)if V then return Enum.ContextActionResult.Sink end end local U=function(L,M,J)K.rightValue=M==Enum.UserInputState.Begin and 1 or 0 K:UpdateMovement(M)if V then return Enum.ContextActionResult.Sink end end local c=function(L,M,J)K.isJumping=M==Enum.UserInputState.Begin if V then return Enum.ContextActionResult.Sink end end if V then ContextActionService:BindActionAtPriority("\109\111\118\101\070\111\114\119\097\114\100\065\099\116\105\111\110",L,false,K.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterForward)ContextActionService:BindActionAtPriority("\109\111\118\101\066\097\099\107\119\097\114\100\065\099\116\105\111\110",M,false,K.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterBackward)ContextActionService:BindActionAtPriority("\109\111\118\101\076\101\102\116\065\099\116\105\111\110",J,false,K.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterLeft)ContextActionService:BindActionAtPriority("\109\111\118\101\082\105\103\104\116\065\099\116\105\111\110",U,false,K.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterRight)ContextActionService:BindActionAtPriority("\106\117\109\112\065\099\116\105\111\110",c,false,K.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterJump)else ContextActionService:BindAction("\109\111\118\101\070\111\114\119\097\114\100\065\099\116\105\111\110",L,false,Enum.PlayerActions.CharacterForward)ContextActionService:BindAction("\109\111\118\101\066\097\099\107\119\097\114\100\065\099\116\105\111\110",M,false,Enum.PlayerActions.CharacterBackward)ContextActionService:BindAction("\109\111\118\101\076\101\102\116\065\099\116\105\111\110",J,false,Enum.PlayerActions.CharacterLeft)ContextActionService:BindAction("\109\111\118\101\082\105\103\104\116\065\099\116\105\111\110",U,false,Enum.PlayerActions.CharacterRight)ContextActionService:BindAction("\106\117\109\112\065\099\116\105\111\110",c,false,Enum.PlayerActions.CharacterJump)end end function L.UnbindContextActions(K)ContextActionService:UnbindAction("\109\111\118\101\070\111\114\119\097\114\100\065\099\116\105\111\110")ContextActionService:UnbindAction("\109\111\118\101\066\097\099\107\119\097\114\100\065\099\116\105\111\110")ContextActionService:UnbindAction("\109\111\118\101\076\101\102\116\065\099\116\105\111\110")ContextActionService:UnbindAction("\109\111\118\101\082\105\103\104\116\065\099\116\105\111\110")ContextActionService:UnbindAction("\106\117\109\112\065\099\116\105\111\110")end function L.ConnectFocusEventListeners(K)local function L()K.moveVector=ZERO_VECTOR3 K.forwardValue=0 K.backwardValue=0 K.leftValue=0 K.rightValue=0 K.isJumping=false end local function M(L)K.isJumping=false end K.textFocusReleasedConn=UserInputService.TextBoxFocusReleased:Connect(L)K.textFocusGainedConn=UserInputService.TextBoxFocused:Connect(M)K.windowFocusReleasedConn=UserInputService.WindowFocused:Connect(L)end function L.DisconnectFocusEventListeners(K)if K.textFocusReleasedCon then K.textFocusReleasedCon:Disconnect()K.textFocusReleasedCon=nil end if K.textFocusGainedConn then K.textFocusGainedConn:Disconnect()K.textFocusGainedConn=nil end if K.windowFocusReleasedConn then K.windowFocusReleasedConn:Disconnect()K.windowFocusReleasedConn=nil end end return L
--[[ The Module ]]
--
local BaseCharacterController = require(script.Parent:WaitForChild("BaseCharacterController"))
local Keyboard = setmetatable({}, BaseCharacterController)
Keyboard.__index = Keyboard

local bindAtPriorityFlagExists, bindAtPriorityFlagEnabled = pcall(function()
	return UserSettings():IsUserFeatureEnabled("UserPlayerScriptsBindAtPriority")
end)
local FFlagPlayerScriptsBindAtPriority = bindAtPriorityFlagExists and bindAtPriorityFlagEnabled

function Keyboard.new(CONTROL_ACTION_PRIORITY)
	local self = setmetatable(BaseCharacterController.new(), Keyboard)

	self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY

	self.textFocusReleasedConn = nil
	self.textFocusGainedConn = nil
	self.windowFocusReleasedConn = nil

	self.forwardValue = 0
	self.backwardValue = 0
	self.leftValue = 0
	self.rightValue = 0
	return self
end

function Keyboard:Enable(enable)
	if not UserInputService.KeyboardEnabled then
		return false
	end

	if enable == self.enabled then
		-- Module is already in the state being requested. True is returned here since the module will be in the state
		-- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
		-- no action was necessary. False indicates failure to be in requested/expected state.
		return true
	end

	self.forwardValue = 0
	self.backwardValue = 0
	self.leftValue = 0
	self.rightValue = 0
	self.moveVector = ZERO_VECTOR3
	self.isJumping = false

	if enable then
		self:BindContextActions()
		self:ConnectFocusEventListeners()
	else
		self:UnbindContextActions()
		self:DisconnectFocusEventListeners()
	end

	self.enabled = enable
	return true
end

function Keyboard:UpdateMovement(inputState)
	if inputState == Enum.UserInputState.Cancel then
		self.moveVector = ZERO_VECTOR3
	else
		self.moveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
	end
end

function Keyboard:BindContextActions()
	-- Note: In the previous version of this code, the movement values were not zeroed-out on UserInputState. Cancel, now they are,
	-- which fixes them from getting stuck on.
	local handleMoveForward = function(actionName, inputState, inputObject)
		self.forwardValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
		self:UpdateMovement(inputState)
		if FFlagPlayerScriptsBindAtPriority then
			return Enum.ContextActionResult.Sink
		end
	end

	local handleMoveBackward = function(actionName, inputState, inputObject)
		self.backwardValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
		self:UpdateMovement(inputState)
		if FFlagPlayerScriptsBindAtPriority then
			return Enum.ContextActionResult.Sink
		end
	end

	local handleMoveLeft = function(actionName, inputState, inputObject)
		self.leftValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
		self:UpdateMovement(inputState)
		if FFlagPlayerScriptsBindAtPriority then
			return Enum.ContextActionResult.Sink
		end
	end

	local handleMoveRight = function(actionName, inputState, inputObject)
		self.rightValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
		self:UpdateMovement(inputState)
		if FFlagPlayerScriptsBindAtPriority then
			return Enum.ContextActionResult.Sink
		end
	end

	local handleJumpAction = function(actionName, inputState, inputObject)
		self.isJumping = (inputState == Enum.UserInputState.Begin)
		if FFlagPlayerScriptsBindAtPriority then
			return Enum.ContextActionResult.Sink
		end
	end

	-- TODO: Revert to KeyCode bindings so that in the future the abstraction layer from actual keys to
	-- movement direction is done in Lua
	if FFlagPlayerScriptsBindAtPriority then
		ContextActionService:BindActionAtPriority(
			"moveForwardAction",
			handleMoveForward,
			false,
			self.CONTROL_ACTION_PRIORITY,
			Enum.PlayerActions.CharacterForward
		)
		ContextActionService:BindActionAtPriority(
			"moveBackwardAction",
			handleMoveBackward,
			false,
			self.CONTROL_ACTION_PRIORITY,
			Enum.PlayerActions.CharacterBackward
		)
		ContextActionService:BindActionAtPriority(
			"moveLeftAction",
			handleMoveLeft,
			false,
			self.CONTROL_ACTION_PRIORITY,
			Enum.PlayerActions.CharacterLeft
		)
		ContextActionService:BindActionAtPriority(
			"moveRightAction",
			handleMoveRight,
			false,
			self.CONTROL_ACTION_PRIORITY,
			Enum.PlayerActions.CharacterRight
		)
		ContextActionService:BindActionAtPriority(
			"jumpAction",
			handleJumpAction,
			false,
			self.CONTROL_ACTION_PRIORITY,
			Enum.PlayerActions.CharacterJump
		)
	else
		ContextActionService:BindAction(
			"moveForwardAction",
			handleMoveForward,
			false,
			Enum.PlayerActions.CharacterForward
		)
		ContextActionService:BindAction(
			"moveBackwardAction",
			handleMoveBackward,
			false,
			Enum.PlayerActions.CharacterBackward
		)
		ContextActionService:BindAction("moveLeftAction", handleMoveLeft, false, Enum.PlayerActions.CharacterLeft)
		ContextActionService:BindAction("moveRightAction", handleMoveRight, false, Enum.PlayerActions.CharacterRight)
		ContextActionService:BindAction("jumpAction", handleJumpAction, false, Enum.PlayerActions.CharacterJump)
	end
end

function Keyboard:UnbindContextActions()
	ContextActionService:UnbindAction("moveForwardAction")
	ContextActionService:UnbindAction("moveBackwardAction")
	ContextActionService:UnbindAction("moveLeftAction")
	ContextActionService:UnbindAction("moveRightAction")
	ContextActionService:UnbindAction("jumpAction")
end

function Keyboard:ConnectFocusEventListeners()
	local function onFocusReleased()
		self.moveVector = ZERO_VECTOR3
		self.forwardValue = 0
		self.backwardValue = 0
		self.leftValue = 0
		self.rightValue = 0
		self.isJumping = false
	end

	local function onTextFocusGained(textboxFocused)
		self.isJumping = false
	end

	self.textFocusReleasedConn = UserInputService.TextBoxFocusReleased:Connect(onFocusReleased)
	self.textFocusGainedConn = UserInputService.TextBoxFocused:Connect(onTextFocusGained)
	self.windowFocusReleasedConn = UserInputService.WindowFocused:Connect(onFocusReleased)
end

function Keyboard:DisconnectFocusEventListeners()
	if self.textFocusReleasedCon then
		self.textFocusReleasedCon:Disconnect()
		self.textFocusReleasedCon = nil
	end
	if self.textFocusGainedConn then
		self.textFocusGainedConn:Disconnect()
		self.textFocusGainedConn = nil
	end
	if self.windowFocusReleasedConn then
		self.windowFocusReleasedConn:Disconnect()
		self.windowFocusReleasedConn = nil
	end
end

return Keyboard
