329 lines
11 KiB
Lua
329 lines
11 KiB
Lua
--[[
|
|
ControlScript - This module manages the selection of the current character control module
|
|
and calls update() on the active module on RenderStepped
|
|
|
|
2018 PlayerScripts Update - AllYourBlox
|
|
--]]
|
|
local ControlScript = {}
|
|
|
|
--[[ Roblox Services ]]--
|
|
local a = shared
|
|
local Players = game:GetService("Players")
|
|
local RunService = game:GetService("RunService")
|
|
local UserInputService = game:GetService("UserInputService")
|
|
local Settings = UserSettings()
|
|
local GameSettings = Settings.GameSettings
|
|
|
|
local activeControlModule = nil -- Used to prevent unnecessarily expensive checks on each input event
|
|
local activeController = nil
|
|
local touchJumpController = nil
|
|
local moveFunction = Players.LocalPlayer.Move
|
|
local humanoid = nil
|
|
local lastInputType = Enum.UserInputType.None
|
|
local cameraRelative = true
|
|
|
|
-- For Roblox VehicleController
|
|
local humanoidSeatedConn = nil
|
|
local vehicleController = nil
|
|
|
|
local touchControlFrame = nil
|
|
|
|
-- Modules - each returns a new() constructor function used to create controllers as needed
|
|
local Keyboard = require(script:WaitForChild("Keyboard"))
|
|
local Gamepad = require(script:WaitForChild("Gamepad"))
|
|
local TouchDPad = require(script:WaitForChild("TouchDPad"))
|
|
local DynamicThumbstick = require(script:WaitForChild("DynamicThumbstick"))
|
|
|
|
-- These controllers handle only walk/run movement, jumping is handled by the
|
|
-- TouchJump controller if any of these are active
|
|
local ClickToMove = require(script:WaitForChild("ClickToMoveController"))
|
|
local TouchThumbstick = require(script:WaitForChild("TouchThumbstick"))
|
|
local TouchThumbpad = require(script:WaitForChild("TouchThumbpad"))
|
|
local TouchJump = require(script:WaitForChild("TouchJump"))
|
|
|
|
-- Old control script notes that this must be required but is not used like a control module?
|
|
local VehicleController = require(script:WaitForChild("VehicleController"))
|
|
|
|
-- The Modules above are used to construct controller instances as-needed, and this
|
|
-- table is a map from Module to the instance created from it
|
|
local controllers = {}
|
|
|
|
-- Mapping from movement mode and lastInputType enum values to control modules to avoid huge if elseif switching
|
|
local movementEnumToModuleMap = {
|
|
[Enum.TouchMovementMode.DPad] = TouchDPad,
|
|
[Enum.DevTouchMovementMode.DPad] = TouchDPad,
|
|
[Enum.TouchMovementMode.Thumbpad] = TouchThumbpad,
|
|
[Enum.DevTouchMovementMode.Thumbpad] = TouchThumbpad,
|
|
[Enum.TouchMovementMode.Thumbstick] = TouchThumbstick,
|
|
[Enum.DevTouchMovementMode.Thumbstick] = TouchThumbstick,
|
|
[Enum.TouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
|
|
[Enum.DevTouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
|
|
[Enum.TouchMovementMode.ClickToMove] = ClickToMove,
|
|
[Enum.DevTouchMovementMode.ClickToMove] = ClickToMove,
|
|
|
|
-- Current default
|
|
[Enum.TouchMovementMode.Default] = TouchThumbstick,
|
|
|
|
[Enum.ComputerMovementMode.Default] = Keyboard,
|
|
[Enum.ComputerMovementMode.KeyboardMouse] = Keyboard,
|
|
[Enum.DevComputerMovementMode.KeyboardMouse] = Keyboard,
|
|
[Enum.DevComputerMovementMode.Scriptable] = nil,
|
|
[Enum.ComputerMovementMode.ClickToMove] = ClickToMove,
|
|
[Enum.DevComputerMovementMode.ClickToMove] = ClickToMove,
|
|
}
|
|
|
|
-- Keyboard controller is really keyboard and mouse controller
|
|
local computerInputTypeToModuleMap = {
|
|
[Enum.UserInputType.Keyboard] = Keyboard,
|
|
[Enum.UserInputType.MouseButton1] = Keyboard,
|
|
[Enum.UserInputType.MouseButton2] = Keyboard,
|
|
[Enum.UserInputType.MouseButton3] = Keyboard,
|
|
[Enum.UserInputType.MouseWheel] = Keyboard,
|
|
[Enum.UserInputType.MouseMovement] = Keyboard,
|
|
[Enum.UserInputType.Gamepad1] = Gamepad,
|
|
[Enum.UserInputType.Gamepad2] = Gamepad,
|
|
[Enum.UserInputType.Gamepad3] = Gamepad,
|
|
[Enum.UserInputType.Gamepad4] = Gamepad,
|
|
}
|
|
|
|
-- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
|
|
local function SelectComputerMovementModule()
|
|
if not (UserInputService.KeyboardEnabled or UserInputService.GamepadEnabled) then
|
|
return nil, false
|
|
end
|
|
local computerModule = nil
|
|
local DevMovementMode = Players.LocalPlayer.DevComputerMovementMode
|
|
if DevMovementMode == Enum.DevComputerMovementMode.UserChoice then
|
|
computerModule = computerInputTypeToModuleMap[lastInputType]
|
|
if not computerModule then
|
|
computerModule = movementEnumToModuleMap[GameSettings.ComputerMovementMode]
|
|
end
|
|
elseif DevMovementMode == Enum.DevComputerMovementMode.Scriptable then
|
|
return nil, true
|
|
elseif DevMovementMode == Enum.DevComputerMovementMode.ClickToMove then
|
|
computerModule = ClickToMove
|
|
else
|
|
computerModule = computerInputTypeToModuleMap[lastInputType]
|
|
if not computerModule then
|
|
computerModule = movementEnumToModuleMap[DevMovementMode]
|
|
end
|
|
end
|
|
return computerModule, true
|
|
end
|
|
|
|
-- Choose current Touch control module based on settings (user, dev)
|
|
-- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
|
|
local function SelectTouchModule()
|
|
if not UserInputService.TouchEnabled then
|
|
return nil, false
|
|
end
|
|
local touchModule = nil
|
|
local DevMovementMode = Players.LocalPlayer.DevTouchMovementMode
|
|
if DevMovementMode == Enum.DevTouchMovementMode.UserChoice then
|
|
touchModule = movementEnumToModuleMap[GameSettings.TouchMovementMode]
|
|
elseif DevMovementMode == Enum.DevTouchMovementMode.Scriptable then
|
|
return nil, true
|
|
else
|
|
touchModule = movementEnumToModuleMap[DevMovementMode]
|
|
end
|
|
return touchModule, true
|
|
end
|
|
|
|
local function OnRenderStepped()
|
|
if activeController and activeController.enabled and humanoid then
|
|
local moveVector = activeController:GetMoveVector()
|
|
|
|
local vehicleConsumedInput = false
|
|
if vehicleController then
|
|
moveVector, vehicleConsumedInput = vehicleController:Update(moveVector, activeControlModule==Gamepad)
|
|
end
|
|
|
|
-- User of vehicleConsumedInput is commented out to preserve legacy behavior, in case some game relies on Humanoid.MoveDirection still being set while in a VehicleSeat
|
|
--if not vehicleConsumedInput then
|
|
moveFunction(Players.LocalPlayer, moveVector, cameraRelative)
|
|
--end
|
|
|
|
humanoid.Jump = activeController:GetIsJumping() or (touchJumpController and touchJumpController:GetIsJumping())
|
|
end
|
|
end
|
|
|
|
local function OnHumanoidSeated(active, currentSeatPart)
|
|
if active then
|
|
if currentSeatPart and currentSeatPart:IsA("VehicleSeat") then
|
|
if not vehicleController then
|
|
vehicleController = VehicleController.new()
|
|
end
|
|
vehicleController:Enable(true, currentSeatPart)
|
|
end
|
|
else
|
|
if vehicleController then
|
|
vehicleController:Enable(false, currentSeatPart)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnCharacterAdded(char)
|
|
humanoid = char:FindFirstChildOfClass("Humanoid")
|
|
while not humanoid do
|
|
char.ChildAdded:wait()
|
|
humanoid = char:FindFirstChildOfClass("Humanoid")
|
|
end
|
|
|
|
if humanoidSeatedConn then
|
|
humanoidSeatedConn:Disconnect()
|
|
humanoidSeatedConn = nil
|
|
end
|
|
humanoidSeatedConn = humanoid.Seated:connect(OnHumanoidSeated)
|
|
end
|
|
|
|
local function OnCharacterRemoving(char)
|
|
humanoid = nil
|
|
end
|
|
|
|
-- Helper function to lazily instantiate a controller if it does not yet exist,
|
|
-- disable the active controller if it is different from the on being switched to,
|
|
-- and then enable the requested controller. The argument to this function must be
|
|
-- a reference to one of the control modules, i.e. Keyboard, Gamepad, etc.
|
|
local function SwitchToController(controlModule)
|
|
if not controlModule then
|
|
if activeController then
|
|
activeController:Enable(false)
|
|
end
|
|
activeController = nil
|
|
activeControlModule = nil
|
|
else
|
|
if not controllers[controlModule] then
|
|
controllers[controlModule] = controlModule.new()
|
|
end
|
|
|
|
if activeController ~= controllers[controlModule] then
|
|
if activeController then
|
|
activeController:Enable(false)
|
|
end
|
|
activeController = controllers[controlModule]
|
|
activeControlModule = controlModule -- Only used to check if controller switch is necessary
|
|
if touchControlFrame then
|
|
activeController:Enable(true, touchControlFrame)
|
|
else
|
|
activeController:Enable(true)
|
|
end
|
|
if touchControlFrame and (activeControlModule == TouchThumbpad
|
|
or activeControlModule == TouchThumbstick
|
|
or activeControlModule == ClickToMove
|
|
or activeControlModule == DynamicThumbstick) then
|
|
touchJumpController = controllers[TouchJump]
|
|
if not touchJumpController then
|
|
touchJumpController = TouchJump.new()
|
|
end
|
|
touchJumpController:Enable(true, touchControlFrame)
|
|
else
|
|
if touchJumpController then
|
|
touchJumpController:Enable(false)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnLastInputTypeChanged(newLastInputType)
|
|
if lastInputType == newLastInputType then
|
|
warn("LastInputType Change listener called with current type.")
|
|
end
|
|
lastInputType = newLastInputType
|
|
|
|
if lastInputType == Enum.UserInputType.Touch then
|
|
-- TODO: Check if touch module already active
|
|
local touchModule, success = SelectTouchModule()
|
|
if success then
|
|
while not touchControlFrame do
|
|
wait()
|
|
end
|
|
SwitchToController(touchModule)
|
|
end
|
|
else
|
|
local computerModule = computerInputTypeToModuleMap[lastInputType]
|
|
if computerModule then
|
|
SwitchToController(computerModule)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Called when any relevant values of GameSettings or LocalPlayer change, forcing re-evalulation of
|
|
-- current control scheme
|
|
local function OnComputerMovementModeChange()
|
|
local controlModule, success = SelectComputerMovementModule()
|
|
if success then
|
|
SwitchToController(controlModule)
|
|
end
|
|
end
|
|
|
|
local function OnTouchMovementModeChange()
|
|
local touchModule, success = SelectTouchModule()
|
|
if success then
|
|
while not touchControlFrame do
|
|
wait()
|
|
end
|
|
SwitchToController(touchModule)
|
|
end
|
|
end
|
|
|
|
Players.LocalPlayer.CharacterAdded:Connect(OnCharacterAdded)
|
|
Players.LocalPlayer.CharacterRemoving:Connect(OnCharacterRemoving)
|
|
if Players.LocalPlayer.Character then
|
|
OnCharacterAdded(Players.LocalPlayer.Character)
|
|
end
|
|
|
|
RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, OnRenderStepped)
|
|
|
|
UserInputService.LastInputTypeChanged:Connect(OnLastInputTypeChanged)
|
|
|
|
local propertyChangeListeners = {
|
|
GameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(OnTouchMovementModeChange),
|
|
Players.LocalPlayer:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(OnTouchMovementModeChange),
|
|
|
|
GameSettings:GetPropertyChangedSignal("ComputerMovementMode"):Connect(OnComputerMovementModeChange),
|
|
Players.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(OnComputerMovementModeChange),
|
|
}
|
|
|
|
--[[ Touch Device UI ]]--
|
|
local PlayerGui = nil
|
|
local touchGui = nil
|
|
local playerGuiAddedConn = nil
|
|
|
|
local function createTouchGuiContainer()
|
|
if touchGui then touchGui:Destroy() end
|
|
|
|
-- Container for all touch device guis
|
|
touchGui = Instance.new('ScreenGui')
|
|
touchGui.Name = "TouchGui"
|
|
touchGui.ResetOnSpawn = false
|
|
|
|
touchControlFrame = Instance.new("Frame")
|
|
touchControlFrame.Name = "TouchControlFrame"
|
|
touchControlFrame.Size = UDim2.new(1, 0, 1, 0)
|
|
touchControlFrame.BackgroundTransparency = 1
|
|
touchControlFrame.Parent = touchGui
|
|
|
|
touchGui.Parent = PlayerGui
|
|
end
|
|
|
|
if UserInputService.TouchEnabled then
|
|
PlayerGui = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui")
|
|
if PlayerGui then
|
|
createTouchGuiContainer()
|
|
OnLastInputTypeChanged(UserInputService:GetLastInputType())
|
|
else
|
|
playerGuiAddedConn = Players.LocalPlayer.ChildAdded:Connect(function(child)
|
|
if child:IsA("PlayerGui") then
|
|
PlayerGui = child
|
|
createTouchGuiContainer()
|
|
playerGuiAddedConn:Disconnect()
|
|
playerGuiAddedConn = nil
|
|
OnLastInputTypeChanged(UserInputService:GetLastInputType())
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
return ControlScript
|