From 1564575a7f401054c01ba60d7b1e049d87a2ed6d Mon Sep 17 00:00:00 2001 From: loplkc Date: Fri, 25 Feb 2022 12:36:01 -0500 Subject: [PATCH] InputHandler completed, compilation errors fixed --- src/client/ClientMessenger.ts | 2 +- src/client/InputHandler.ts | 155 +++++++++++++++++++++------------- src/client/ModesLocal.ts | 4 + src/client/init.client.ts | 12 ++- src/services.d.ts | 12 --- 5 files changed, 110 insertions(+), 75 deletions(-) diff --git a/src/client/ClientMessenger.ts b/src/client/ClientMessenger.ts index 723e9ca..da21c94 100644 --- a/src/client/ClientMessenger.ts +++ b/src/client/ClientMessenger.ts @@ -3,7 +3,7 @@ export function bindToServerMessage(functionToBind: Callback) { assert(Output?.IsA("RemoteEvent"), 'Remote event "Input" is of incorrect class or nil'); Output.OnClientEvent.Connect(functionToBind); } -export function messageServer(messageType: clientMessageType, messageContent?: unknown) { +export function messageServer(messageType: string, messageContent?: unknown) { assert(Input?.IsA("RemoteEvent"), 'Remote event "Output" is of incorrect class or nil'); Input.FireServer(messageType, messageContent); } diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 5c89dbe..599c27f 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -5,28 +5,49 @@ const Workspace = game.GetService("Workspace"); const CAMERA = Workspace.CurrentCamera as Camera; assert(CAMERA, 'Camera of "' + Players.LocalPlayer.DisplayName + '"does not exist! (HOW???)'); -export type inputBindings = { - [input in keyof controlBindings]?: [((argument1?: unknown, argument2?: unknown) => void), unknown, unknown]; -}; - -export interface inputBinder { - assignControlBindings: (controlBindings: controlBindings) => void; - assignInputBindings: (inputBindings: inputBindings) => void; - removeInputBindings: (inputBindings: inputBindings) => void; +function enumTypeIs(value: unknown, EnumAsObject: Enum): value is EnumAsType { + if (typeIs(value, "EnumItem")) { + return value.Name in EnumAsObject + } else { + return false + } +} +type validInput = Enum.KeyCode // + Include controller "keys" +function isValidInput(value: unknown): value is validInput { + return enumTypeIs(value, Enum.KeyCode) +} +const actionAssignmentsReference: string[] = [ + "clicker1", // What is used to click on things (enemies in game, UI elements) + "diamond1", // Diamond controls + "diamond2", + "diamond3", + "diamond4", + "special1", // Special controls + "special2", +] +export interface actionAssignments { // Based on the reference array + clicker1?: validInput; // What is used to click on things (enemies in game, UI elements) + diamond1?: validInput; // Diamond controls + diamond2?: validInput; + diamond3?: validInput; + diamond4?: validInput; + special1?: validInput; // Special controls + special2?: validInput; +} +type action = keyof actionAssignments +function isValidAction(value: string): value is keyof actionAssignments { + return value in actionAssignmentsReference; // uh oh +} +type actionBinding = [action, ((actionName?: string, state?: Enum.UserInputState, inputObject?: InputObject) => void)]; +type unknownTable = {[numberKey: number]: unknown, [stringKey: string]: unknown} +export function isUnknownTable(thing: unknown): thing is unknownTable { + return typeIs(thing, "table") } -interface inputHandler extends inputBinder { - controlHandler: (actionName: string, state: Enum.UserInputState, inputObject: InputObject) => void; - controlBindings?: controlBindings - boundInputs: inputBindings - storedInput?: string // + compound inputs -} - -const hitParams = new RaycastParams(); -//hitParams.FilterDescendantsInstances = {efFolder,Plr.Character} -hitParams.FilterType = Enum.RaycastFilterType.Blacklist; -function getMouseLocation(): [Vector3, Vector3, Instance | undefined] { - //hitParams.FilterDescendantsInstances = {efFolder,Plr.Character} +function getMouseLocation(filterDescendantsInstances: any[]): [Vector3, Vector3, Instance | undefined] { // May be unnecessary + const hitParams = new RaycastParams(); + hitParams.FilterType = Enum.RaycastFilterType.Blacklist; + hitParams.FilterDescendantsInstances = filterDescendantsInstances const mouseLocation = UserInputService.GetMouseLocation(); const unitRay = CAMERA.ViewportPointToRay(mouseLocation.X, mouseLocation.Y); const cast = Workspace.Raycast(unitRay.Origin, unitRay.Direction.mul(1000), hitParams); @@ -37,51 +58,65 @@ function getMouseLocation(): [Vector3, Vector3, Instance | undefined] { } } -function isValidInput(controlBindings: controlBindings, value: string): value is keyof controlBindings { - return value in controlBindings; // uh oh +export function translateInputState(state: any) { + if (enumTypeIs(state, Enum.UserInputState)) { + // + Translate to simple boolean + } } -//function(actionName: string, state: Enum.UserInputState, inputObject: InputObject) { -// inputParameters[0](inputParameters[1]) -//} -export function makeInputHandler() { - const t: inputHandler = {//return { - assignControlBindings: function(controlBindings: controlBindings) { - this.controlBindings = controlBindings // WOW!!!!! - }, - assignInputBindings: function(inputBindings: inputBindings) { - const controlBindings = this.controlBindings; - if (controlBindings) { - for (let input in inputBindings) { - if (isValidInput(controlBindings, input)) { - // + Check for this.boundInputs[input] - const inputParameters = inputBindings[input] - if (inputParameters) { - this.boundInputs[input] = inputParameters; - const controlArray = controlBindings[input]; - for (const control of controlArray) { - ContextActionService.BindAction(input, this.controlHandler, false, control) - } - } - } - } +export interface actionBinder { + assignInputsToActions: (actionAssignments: unknownTable) => void; // The client gets these from the server (the player saves them to datastores) + bindFunctionsToActions: (actionBindings: actionBinding[]) => void; + unbindFunctionsFromActions: (actions: action[]) => void; +} + +class actionHandler implements actionBinder { // + Needs a semaphore if concurrency issues arise + constructor() { + // Fortnite + } + assignInputsToActions(actionAssignments: unknownTable) { + let newActionAssignments: actionAssignments = {} + actionAssignmentsReference.forEach(action => { + const input: unknown = actionAssignments[action] + if (isValidAction(action) && isValidInput(input)) { + newActionAssignments[action] = input } - }, - removeInputBindings: function(inputBindings: inputBindings) {}, - controlHandler: function(actionName: string, state: Enum.UserInputState, inputObject: InputObject) { - const controlBindings = this.controlBindings; - if (controlBindings) { - if (isValidInput(controlBindings, actionName)) { - const inputParameters = this.boundInputs[actionName] - if (inputParameters) { - inputParameters[0](inputParameters[1], inputParameters[2]) - } + }) + } + bindFunctionsToActions(actionBindings: actionBinding[]) { + const actionAssignments = this.actionAssignments; + const boundActions = this.boundActions; + if (actionAssignments) { + actionBindings.forEach(actionBinding => { + const action = actionBinding[0] + const input = actionAssignments[action]; + if (!boundActions[action] && input) { + boundActions[action] = true; + ContextActionService.BindAction(action, actionBinding[1], false, input); + } else { + // ??? } + }); + } + } + unbindFunctionsFromActions(actions: action[]) { + const boundActions = this.boundActions + actions.forEach(action => { + if (boundActions[action]) { + boundActions[action] = undefined; + ContextActionService.UnbindAction(action); + } else { + // ??? } - }, - - boundInputs: {} - } //as inputHandler; + }) + } + + actionAssignments?: actionAssignments; + boundActions: {[action: string]: boolean | undefined} = {}; +} + +export function makeActionBinder(): actionBinder { + return new actionHandler(); } /* function handleInput(input: InputObject, otherInteraction: boolean) { diff --git a/src/client/ModesLocal.ts b/src/client/ModesLocal.ts index cd02482..0df35e6 100644 --- a/src/client/ModesLocal.ts +++ b/src/client/ModesLocal.ts @@ -1,3 +1,7 @@ + +interface modeLocal { + //aura?: [effectEntry, bodyPart?, number?][]; // effect, part it is attached to (default root), how many times it should be called per frame (default 1) +} export const gamer: { [modeId: string]: modeLocal } = { ["Flawless"]: { //aura: [[["Ball"]]], // Will come back to this later diff --git a/src/client/init.client.ts b/src/client/init.client.ts index 29b346f..45f0beb 100644 --- a/src/client/init.client.ts +++ b/src/client/init.client.ts @@ -4,6 +4,7 @@ const RunService = game.GetService("RunService"); import { bindToServerMessage, messageServer } from "./ClientMessenger"; import { handleGuiInput, drawGui, closeGui } from "./GuiHandler"; import { makeEffectRunner, effectRunner } from "./EffectMaker"; +import { makeActionBinder, actionBinder, isUnknownTable } from "./InputHandler"; const LOCALPLAYER = Players.LocalPlayer; const PLAYERGUI = LOCALPLAYER.WaitForChild("PlayerGui", 1) as PlayerGui; assert( @@ -18,7 +19,7 @@ function openMainMenu(playerGui: PlayerGui) { for (const mainMenuButton of mainMenuButtons) { mainMenuButton[0].Activated.Connect(function () { handleGuiInput(messageServer, mainMenuButton[1][0], mainMenuButton[1][1]); - }); // + Good ETC extension - Add support for other controller types + }); } } @@ -29,11 +30,14 @@ function handleServerMessage(messageType: unknown, messageContent: unknown) { } else if (messageType === "enterGame") { closeGui(PLAYERGUI, "MainMenu"); inMainMenu = false; + } else if (messageType === "bindActions") { + if (isUnknownTable(messageContent)) { + mainActionBinder.assignInputsToActions(messageContent) + } } } // Bind functions -bindToServerMessage(handleServerMessage); const effectRunners: effectRunner[] = []; // + Put stuff in the effectRunners table @@ -42,3 +46,7 @@ RunService.RenderStepped.Connect(function(deltaTime) { effectRunner.runEffects(deltaTime) }); }) + +const mainActionBinder: actionBinder = makeActionBinder() + +bindToServerMessage(handleServerMessage); \ No newline at end of file diff --git a/src/services.d.ts b/src/services.d.ts index f14894b..fa90d7f 100644 --- a/src/services.d.ts +++ b/src/services.d.ts @@ -8,20 +8,8 @@ type meshType = "Ball"; type effectState = [CFrame, Vector3, Color3, number]; // The number is transparency type effectEntry = [meshType, EnumItem, effectState[]]; // The enumitem is material -interface modeLocal { - aura?: [effectEntry, bodyPart?, number?][]; // effect, part it is attached to (default root), how many times it should be called per frame (default 1) -} */ // Genuinely require being on both sides - but in the services file? No shot! -type acceptedControls = Enum.KeyCode[] // + Include controller "keys" -interface controlBindings { - clicker1: acceptedControls; // What is used to click on things (enemies in game, UI elements) - diamond1: acceptedControls; // Diamond controls - diamond2: acceptedControls; - diamond3: acceptedControls; - diamond4: acceptedControls; - special1: acceptedControls; // Special controls -} /*interface hookInEntry {