local U=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 c=setmetatable({},U)c.__index=c function c.new(I)local G=setmetatable(U.new(),c)G.CONTROL_ACTION_PRIORITY=I G.textFocusReleasedConn=nil G.textFocusGainedConn=nil G.windowFocusReleasedConn=nil G.forwardValue=0 G.backwardValue=0 G.leftValue=0 G.rightValue=0 G.jumpEnabled=true return G end function c.Enable(c,U)if not UserInputService.KeyboardEnabled then return false end if U==c.enabled then return true end c.forwardValue=0 c.backwardValue=0 c.leftValue=0 c.rightValue=0 c.moveVector=ZERO_VECTOR3 c.jumpRequested=false c:UpdateJump()if U then c:BindContextActions()c:ConnectFocusEventListeners()else c:UnbindContextActions()c:DisconnectFocusEventListeners()end c.enabled=U return true end function c.UpdateMovement(c,U)if U==Enum.UserInputState.Cancel then c.moveVector=ZERO_VECTOR3 else c.moveVector=Vector3.new(c.leftValue+c.rightValue,0,c.forwardValue+c.backwardValue)end end function c.UpdateJump(U)U.isJumping=U.jumpRequested end function c.BindContextActions(U)local c=function(c,I,G)U.forwardValue=I==Enum.UserInputState.Begin and-1 or 0 U:UpdateMovement(I)return Enum.ContextActionResult.Pass end local I=function(c,I,G)U.backwardValue=I==Enum.UserInputState.Begin and 1 or 0 U:UpdateMovement(I)return Enum.ContextActionResult.Pass end local G=function(c,I,G)U.leftValue=I==Enum.UserInputState.Begin and-1 or 0 U:UpdateMovement(I)return Enum.ContextActionResult.Pass end local j=function(c,I,G)U.rightValue=I==Enum.UserInputState.Begin and 1 or 0 U:UpdateMovement(I)return Enum.ContextActionResult.Pass end local Q=function(c,I,G)U.jumpRequested=U.jumpEnabled and I==Enum.UserInputState.Begin U: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",c,false,U.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",I,false,U.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterBackward)ContextActionService:BindActionAtPriority("\109\111\118\101\076\101\102\116\065\099\116\105\111\110",G,false,U.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterLeft)ContextActionService:BindActionAtPriority("\109\111\118\101\082\105\103\104\116\065\099\116\105\111\110",j,false,U.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterRight)ContextActionService:BindActionAtPriority("\106\117\109\112\065\099\116\105\111\110",Q,false,U.CONTROL_ACTION_PRIORITY,Enum.PlayerActions.CharacterJump)end function c.UnbindContextActions(U)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 c.ConnectFocusEventListeners(U)local function c()U.moveVector=ZERO_VECTOR3 U.forwardValue=0 U.backwardValue=0 U.leftValue=0 U.rightValue=0 U.jumpRequested=false U:UpdateJump()end local function I(c)U.jumpRequested=false U:UpdateJump()end U.textFocusReleasedConn=UserInputService.TextBoxFocusReleased:Connect(c)U.textFocusGainedConn=UserInputService.TextBoxFocused:Connect(I)U.windowFocusReleasedConn=UserInputService.WindowFocused:Connect(c)end function c.DisconnectFocusEventListeners(U)if U.textFocusReleasedConn then U.textFocusReleasedConn:Disconnect()U.textFocusReleasedConn=nil end if U.textFocusGainedConn then U.textFocusGainedConn:Disconnect()U.textFocusGainedConn=nil end if U.windowFocusReleasedConn then U.windowFocusReleasedConn:Disconnect()U.windowFocusReleasedConn=nil end end return c
--
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
