local s=(require(((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\083\099\114\105\112\116\115")):WaitForChild("\067\111\110\115\111\108\101"))).sourced("\067\111\110\116\114\111\108 \077\111\100\117\108\101")s.log("\076\111\097\100\105\110\103 \100\101\112\101\110\100\101\110\099\105\101\115\046\046\046")local U=game:GetService("\082\117\110\083\101\114\118\105\099\101")local g=(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer local L=(game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\083\099\114\105\112\116\115")local P=require((script:WaitForChild("\077\111\110\105\116\111\114")):WaitForChild("\083\112\097\119\110"))local p=require(L:WaitForChild("\069\118\101\110\116"))s.log("\073\110\105\116\105\097\108\105\122\105\110\103 \099\111\110\115\116\097\110\116\115\046\046\046")local i="\067\111\110\116\114\111\108\115"s.log("\067\111\110\115\116\114\117\099\116\105\110\103 \102\117\110\099\116\105\111\110\115\046\046\046")local function C(s)g:Move(s)end local function F(s) end local function d(s) end s.log("\073\110\105\116\105\097\108\105\122\105\110\103 \118\097\114\105\097\098\108\101\115\046\046\046")local Q={}local I s.log("\067\111\110\115\116\114\117\099\116\105\110\103 \111\098\106\101\099\116\115\046\046\046")Q.__index=Q function Q.new()if I==nil then local s=setmetatable({},Q)s.controlsBound=false s.monitors={}s.event=p.new()s.OnCommand=s.event.Registrar I=s end return I end function Q.ListenForCommand(s,U)local g=function(g,L)if g==s then U(L)end end return g end function Q.SetMove(U,s)if U.moveListener==nil then U.moveListener=U.OnCommand:Connect(U.ListenForCommand(s,C))end end function Q.UnsetMove(s)if s.moveListener~=nil then s.OnCommand:Disconnect(s.moveListener)s.moveListener=nil end end function Q.SetDirection(U,s)if U.directionListener==nil then U.directionListener=U.OnCommand:Connect(U.ListenForCommand(s,F))end end function Q.UnsetDirection(s)if s.directionListener~=nil then s.OnCommand:Disconnect(s.directionListener)s.directionListener=nil end end function Q.SetCamera(U,s)if U.cameraListener==nil then U.cameraListener=U.OnCommand:Connect(U.ListenForCommand(s,d))end end function Q.UnsetCamera(s)if s.cameraListener~=nil then s.OnCommand:Disonnect(s.cameraListener)s.cameraListener=nil end end function Q.Bound(s)return s.controlsBound end function Q.BindControls(s)if not s.controlsBound then local g=s.ControlScheme.Name for U,L in pairs(s.ControlScheme.ControlSet)do if s.monitors[U]==nil then local p=P(L)p.SchemeName=g p:BindControl()s.monitors[U]=p end end local function L()for U,g in pairs(s.monitors)do if g:Updated()then s.event:Fire(U,g:GetValue())end end end U:BindToRenderStep(i,Enum.RenderPriority.Input.Value,L)s.controlsBound=true end end function Q.UnbindControls(s)if s.controlsBound then U:UnbindFromRenderStep(i)for s,U in pairs(s.monitors)do U:UnbindControl()end s.monitors={}s.controlsBound=false end end s.log("\068\111\110\101\046")return Q.new()
--- The main control module.
-- A singleton object responsible for accepting all inputs from the player and
-- interpretting controls. It recieves input from the user and fires
-- corresponding events. Recieves input based on provided ControlSchemes.
--
-- @author LastTalon
-- @version 0.1.1, 2020-10-01
-- @since 0.1
--
-- @module ControlModule

local Console = require(game:GetService("ReplicatedStorage"):WaitForChild("Scripts"):WaitForChild("Console")).sourced("Control Module")

-- Dependencies --
Console.log("Loading dependencies...")

local RunService = game:GetService("RunService")
local Player = game:GetService("Players").LocalPlayer
local SharedScripts = game:GetService("ReplicatedStorage"):WaitForChild("Scripts")

local SpawnMonitor = require(script:WaitForChild("Monitor"):WaitForChild("Spawn"))
local Event = require(SharedScripts:WaitForChild("Event"))

-- Constants --
Console.log("Initializing constants...")

local RenderStepName = "Controls"

-- Functions --
Console.log("Constructing functions...")

-- The default move listener.
local function onMove(value)
	Player:Move(value)
end

-- The default direction listener.
local function onDirection(value)
	-- TODO: Add default direction listener.
end

-- The default camera listener.
local function onCamera(value)
	-- TODO: Add default camera listener.
end

-- Variables --
Console.log("Initializing variables...")

local ControlModule = {}
local instance

-- Local Objects --
Console.log("Constructing objects...")

ControlModule.__index = ControlModule

--- The constructor for the singleton.
-- Creates a new singleton if none exists. This always returns same object
-- initially created.
--
-- @return the singleton
function ControlModule.new()
	if instance == nil then
		local self = setmetatable({}, ControlModule)
		self.controlsBound = false
		self.monitors = {}
		-- self.listeners = {}
		self.event = Event.new()
		self.OnCommand = self.event.Registrar
		instance = self
	end
	return instance
end

--- A generic listener function that activates another listener on a specific
-- command.
function ControlModule.ListenForCommand(command, fn)
	local listener = function(trigger, value)
		if trigger == command then
			fn(value)
		end
	end
	return listener
end

--- Sets the command name for the move action.
-- Sets the default move action to be active and activate on the provided
-- command name.
--
-- @param command the name of the command to activate on
function ControlModule:SetMove(command)
	if self.moveListener == nil then
		self.moveListener = self.OnCommand:Connect(self.ListenForCommand(command, onMove))
	end
end

--- Deactivates the default move action.
function ControlModule:UnsetMove()
	if self.moveListener ~= nil then
		self.OnCommand:Disconnect(self.moveListener)
		self.moveListener = nil
	end
end

--- Sets the command name for the direction action.
-- Sets the default direction action to be active and activate on the provided
-- command name.
--
-- @param command the name of the command to activate on
function ControlModule:SetDirection(command)
	if self.directionListener == nil then
		self.directionListener = self.OnCommand:Connect(self.ListenForCommand(command, onDirection))
	end
end

--- Deactivates the default direction action.
function ControlModule:UnsetDirection()
	if self.directionListener ~= nil then
		self.OnCommand:Disconnect(self.directionListener)
		self.directionListener = nil
	end
end

--- Sets the command name for the camera action.
-- Sets the default camera action to be active and activate on the provided
-- command name.
--
-- @param command the name of the command to activate on
function ControlModule:SetCamera(command)
	if self.cameraListener == nil then
		self.cameraListener = self.OnCommand:Connect(self.ListenForCommand(command, onCamera))
	end
end

--- Deactivates the default camera action.
function ControlModule:UnsetCamera()
	if self.cameraListener ~= nil then
		self.OnCommand:Disonnect(self.cameraListener)
		self.cameraListener = nil
	end
end

--- Tells if the controls are currently bound to the render step.
--
-- @return true if the controls are bound, false otherwise
function ControlModule:Bound()
	return self.controlsBound
end

--- Binds the controls and starts the control step.
function ControlModule:BindControls()
	if not self.controlsBound then
		local schemeName = self.ControlScheme.Name
		for command, control in pairs(self.ControlScheme.ControlSet) do
			if self.monitors[command] == nil then
				local monitor = SpawnMonitor(control)
				monitor.SchemeName = schemeName
				monitor:BindControl()
				self.monitors[command] = monitor
			end
		end
		
		-- The control step that is added to the render step.
		local function onControlStep()
			for command, monitor in pairs(self.monitors) do
				if monitor:Updated() then
					self.event:Fire(command, monitor:GetValue())
				end
			end
		end
		
		RunService:BindToRenderStep(RenderStepName, Enum.RenderPriority.Input.Value, onControlStep)
		self.controlsBound = true
	end
end

--- Unbinds the controls and stops the control step.
function ControlModule:UnbindControls()
	if self.controlsBound then
		RunService:UnbindFromRenderStep(RenderStepName)
		
		for _, monitor in pairs(self.monitors) do
			monitor:UnbindControl()
		end
		
		self.monitors = {}
		self.controlsBound = false
	end
end

-- End --
Console.log("Done.")

return ControlModule.new()
