InputHandler completed, compilation errors fixed

This commit is contained in:
loplkc loplkc 2022-02-25 12:36:01 -05:00
parent f48eff5f67
commit 1564575a7f
5 changed files with 110 additions and 75 deletions

View file

@ -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);
}

View file

@ -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<EnumAsType>(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<Enum.KeyCode>(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<Enum.UserInputState>(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) {

View file

@ -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

View file

@ -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);

12
src/services.d.ts vendored
View file

@ -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 {