local J=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 p=setmetatable({},J)p.__index=p function p.new(w)local P=setmetatable(J.new(),p)P.CONTROL_ACTION_PRIORITY=w P.textFocusReleasedConn=nil P.textFocusGainedConn=nil P.windowFocusReleasedConn=nil P.forwardValue=0 P.backwardValue=0 P.leftValue=0 P.rightValue=0 P.jumpEnabled=true return P end function p.Enable(p,J)if not UserInputService.KeyboardEnabled then return false end if J==p.enabled then return true end p.forwardValue=0 p.backwardValue=0 p.leftValue=0 p.rightValue=0 p.moveVector=ZERO_VECTOR3 p.jumpRequested=false p:UpdateJump()if J then p:BindContextActions()p:ConnectFocusEventListeners()else p:UnbindContextActions()p:DisconnectFocusEventListeners()end p.enabled=J return true end function p.UpdateMovement(p,J)if J==Enum.UserInputState.Cancel then p.moveVector=ZERO_VECTOR3 else p.moveVector=Vector3.new(p.leftValue+p.rightValue,0,p.forwardValue+p.backwardValue)end end function p.UpdateJump(J)J.isJumping=J.jumpRequested end function p.BindContextActions(J)local p=function(p,w,P)J.forwardValue=w==Enum.UserInputState.Begin and-1 or 0 J:UpdateMovement(w)return Enum.ContextActionResult.Pass end local w=function(p,w,P)J.backwardValue=w==Enum.UserInputState.Begin and 1 or 0 J:UpdateMovement(w)return Enum.ContextActionResult.Pass end local P=function(p,w,P)J.leftValue=w==Enum.UserInputState.Begin and-1 or 0 J:UpdateMovement(w)return Enum.ContextActionResult.Pass end local W=function(p,w,P)J.rightValue=w==Enum.UserInputState.Begin and 1 or 0 J:UpdateMovement(w)return Enum.ContextActionResult.Pass end local B=function(p,w,P)J.jumpRequested=J.jumpEnabled and w==Enum.UserInputState.Begin J:UpdateJump()return Enum.ContextActionResult.Pass end ContextActionService:BindActionAtPriority("\109\111\118\101\070\111\114\119\097\114\100\065\099\116\105\111\110",p,false,J.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",w,false,J.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterBackward)ContextActionService:BindActionAtPriority("\109\111\118\101\076\101\102\116\065\099\116\105\111\110",P,false,J.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterLeft)ContextActionService:BindActionAtPriority("\109\111\118\101\082\105\103\104\116\065\099\116\105\111\110",W,false,J.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterRight)ContextActionService:BindActionAtPriority("\106\117\109\112\065\099\116\105\111\110",B,false,J.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterJump)end function p.UnbindContextActions(J)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 p.ConnectFocusEventListeners(J)local function p()J.moveVector=ZERO_VECTOR3 J.forwardValue=0 J.backwardValue=0 J.leftValue=0 J.rightValue=0 J.jumpRequested=false J:UpdateJump()end local function w(p)J.jumpRequested=false J:UpdateJump()end J.textFocusReleasedConn=UserInputService.TextBoxFocusReleased:Connect(p)J.textFocusGainedConn=UserInputService.TextBoxFocused:Connect(w)J.windowFocusReleasedConn=UserInputService.WindowFocused:Connect(p)end function p.DisconnectFocusEventListeners(J)if J.textFocusReleasedConn then J.textFocusReleasedConn:Disconnect()J.textFocusReleasedConn=nil end if J.textFocusGainedConn then J.textFocusGainedConn:Disconnect()J.textFocusGainedConn=nil end if J.windowFocusReleasedConn then J.windowFocusReleasedConn:Disconnect()J.windowFocusReleasedConn=nil end end return p
--[[ The Module ]]
--
local BaseCharacterController = require(script.Parent:WaitForChild("BaseCharacterController"))
local Keyboard = setmetatable({}, BaseCharacterController)
Keyboard.__index = Keyboard

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

	self.jumpEnabled = true

	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.jumpRequested = false
	self:UpdateJump()

	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:UpdateJump()
	self.isJumping = self.jumpRequested
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.
	-- We return ContextActionResult.Pass here for legacy reasons.
	-- Many games rely on gameProcessedEvent being false on UserInputService.InputBegan for these control actions.
	local handleMoveForward = function(actionName, inputState, inputObject)
		self.forwardValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
		self:UpdateMovement(inputState)
		return Enum.ContextActionResult.Pass
	end

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

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

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

	local handleJumpAction = function(actionName, inputState, inputObject)
		self.jumpRequested = self.jumpEnabled and (inputState == Enum.UserInputState.Begin)
		self:UpdateJump()
		return Enum.ContextActionResult.Pass
	end

	-- TODO: Revert to KeyCode bindings so that in the future the abstraction layer from actual keys to
	-- movement direction is done in Lua
	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
	)
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.jumpRequested = false
		self:UpdateJump()
	end

	local function onTextFocusGained(textboxFocused)
		self.jumpRequested = false
		self:UpdateJump()
	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.textFocusReleasedConn then
		self.textFocusReleasedConn:Disconnect()
		self.textFocusReleasedConn = 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
