diff --git a/fission/src/Synthesis.tsx b/fission/src/Synthesis.tsx index 1a9f35b70e..65fa57c87d 100644 --- a/fission/src/Synthesis.tsx +++ b/fission/src/Synthesis.tsx @@ -25,7 +25,7 @@ import UpdateAvailableModal from "@/modals/UpdateAvailableModal" import ViewModal from "@/modals/ViewModal" import ConnectToMultiplayerModal from "@/modals/aether/ConnectToMultiplayerModal" import ServerHostingModal from "@/modals/aether/ServerHostingModal" -import ChangeInputsModal from "@/modals/configuring/ChangeInputsModal" +import ChangeInputsModal from "@/ui/modals/configuring/ChangeInputsModal.tsx" import ChooseMultiplayerModeModal from "@/modals/configuring/ChooseMultiplayerModeModal" import ChooseSingleplayerModeModal from "@/modals/configuring/ChooseSingleplayerModeModal" import ConfigMotorModal from "@/modals/configuring/ConfigMotorModal" @@ -49,12 +49,13 @@ import ScoringZonesPanel from "@/panels/configuring/scoring/ScoringZonesPanel" import ZoneConfigPanel from "@/panels/configuring/scoring/ZoneConfigPanel" import ScoreboardPanel from "@/panels/information/ScoreboardPanel" import DriverStationPanel from "@/panels/simulation/DriverStationPanel" -import ManageAssembliesModal from '@/modals/spawning/ManageAssembliesModal.tsx'; -import World from '@/systems/World.ts'; -import { AddRobotsModal, AddFieldsModal, SpawningModal } from '@/modals/spawning/SpawningModals.tsx'; -import ImportMirabufModal from '@/modals/mirabuf/ImportMirabufModal.tsx'; +import ManageAssembliesModal from "@/modals/spawning/ManageAssembliesModal.tsx" +import World from "@/systems/World.ts" +import { AddRobotsModal, AddFieldsModal, SpawningModal } from "@/modals/spawning/SpawningModals.tsx" +import ImportMirabufModal from "@/modals/mirabuf/ImportMirabufModal.tsx" +import ImportLocalMirabufModal from "@/modals/mirabuf/ImportLocalMirabufModal.tsx" +import ResetAllInputsModal from "./ui/modals/configuring/ResetAllInputsModal.tsx" import Skybox from './ui/components/Skybox.tsx'; -import ImportLocalMirabufModal from '@/modals/mirabuf/ImportLocalMirabufModal.tsx'; const DEFAULT_MIRA_PATH = "/api/mira/Robots/Team 2471 (2018)_v7.mira" @@ -110,10 +111,11 @@ function Synthesis() { return } - const mirabufSceneObject = new MirabufSceneObject(new MirabufInstance(parser)) + const mirabufSceneObject = new MirabufSceneObject(new MirabufInstance(parser), miraAssembly.info!.name!) World.SceneRenderer.RegisterSceneObject(mirabufSceneObject) })() } + setup() let mainLoopHandle = 0 @@ -183,6 +185,7 @@ const initialModals = [ , , , + , , , , diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index 8fa995a5b9..ee798fc236 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -19,16 +19,19 @@ interface RnDebugMeshes { } class MirabufSceneObject extends SceneObject { - private _mirabufInstance: MirabufInstance - private _debugBodies: Map | null - private _physicsLayerReserve: LayerReserve | undefined = undefined + private _assemblyName: string; + private _mirabufInstance: MirabufInstance; + private _mechanism: Mechanism; + private _brain: SynthesisBrain | undefined; - private _mechanism: Mechanism + private _debugBodies: Map | null; + private _physicsLayerReserve: LayerReserve | undefined = undefined; - public constructor(mirabufInstance: MirabufInstance) { + public constructor(mirabufInstance: MirabufInstance, assemblyName: string) { super() this._mirabufInstance = mirabufInstance + this._assemblyName = assemblyName; this._mechanism = World.PhysicsSystem.CreateMechanismFromParser(this._mirabufInstance.parser) if (this._mechanism.layerReserve) { @@ -60,10 +63,10 @@ class MirabufSceneObject extends SceneObject { } // Simulation - World.SimulationSystem.RegisterMechanism(this._mechanism) - const simLayer = World.SimulationSystem.GetSimulationLayer(this._mechanism)! - const brain = new SynthesisBrain(this._mechanism) - simLayer.SetBrain(brain) + World.SimulationSystem.RegisterMechanism(this._mechanism); + const simLayer = World.SimulationSystem.GetSimulationLayer(this._mechanism)!; + this._brain = new SynthesisBrain(this._mechanism, this._assemblyName); + simLayer.SetBrain(this._brain); } public Update(): void { @@ -108,18 +111,16 @@ class MirabufSceneObject extends SceneObject { World.PhysicsSystem.DestroyMechanism(this._mechanism) this._mirabufInstance.Dispose(World.SceneRenderer.scene) this._debugBodies?.forEach(x => { - World.SceneRenderer.scene.remove(x.colliderMesh, x.comMesh) - x.colliderMesh.geometry.dispose() - x.comMesh.geometry.dispose() - ;(x.colliderMesh.material as THREE.Material).dispose() - ;(x.comMesh.material as THREE.Material).dispose() - }) - this._debugBodies?.clear() - this._physicsLayerReserve?.Release() - } - - public GetRootNodeId(): Jolt.BodyID | undefined { - return this._mechanism.nodeToBody.get(this._mechanism.rootBody) + World.SceneRenderer.scene.remove(x.colliderMesh, x.comMesh); + x.colliderMesh.geometry.dispose(); + x.comMesh.geometry.dispose(); + (x.colliderMesh.material as THREE.Material).dispose(); + (x.comMesh.material as THREE.Material).dispose(); + }); + this._debugBodies?.clear(); + this._physicsLayerReserve?.Release(); + + this._brain?.clearControls(); } private CreateMeshForShape(shape: Jolt.Shape): THREE.Mesh { @@ -162,8 +163,8 @@ export async function CreateMirabuf(assembly: mirabuf.Assembly): Promise input.isGlobal)) + this.posKeyCode = posKeyCode ?? "" + this.posKeyModifiers = posKeyModifiers ?? EmptyModifierState + this.negKeyCode = negKeyCode ?? "" + this.negKeyModifiers = negKeyModifiers ?? EmptyModifierState + + this.gamepadAxisNumber = gamepadAxisNumber ?? -1 + this.joystickInverted = joystickInverted ?? false + + this.useGamepadButtons = useGamepadButtons ?? false + this.posGamepadButton = posGamepadButton ?? -1 + this.negGamepadButton = negGamepadButton ?? -1 } - // Robot specific controls like driving - public static get robotInputs(): { [key: string]: Input } { - return Object.fromEntries(Object.entries(InputSystem.allInputs).filter(([_, input]) => !input.isGlobal)) + // For keyboard: returns 1 if positive pressed, -1 if negative pressed, or 0 if none or both are pressed + // For gamepad axis: returns a range between -1 and 1 with a deadband in the middle + getValue(useGamepad: boolean): number { + // Gamepad joystick axis + if (useGamepad) { + if (!this.useGamepadButtons) + return InputSystem.getGamepadAxis(this.gamepadAxisNumber) * (this.joystickInverted ? -1 : 1) + + // Gamepad button axis + return ( + (InputSystem.isGamepadButtonPressed(this.posGamepadButton) ? 1 : 0) - + (InputSystem.isGamepadButtonPressed(this.negGamepadButton) ? 1 : 0) + ) + } + + // Keyboard button axis + return ( + (InputSystem.isKeyPressed(this.posKeyCode, this.posKeyModifiers) ? 1 : 0) - + (InputSystem.isKeyPressed(this.negKeyCode, this.negKeyModifiers) ? 1 : 0) + ) } + getCopy(): Input { + return new AxisInput( + this.inputName, + this.posKeyCode, + this.negKeyCode, + this.gamepadAxisNumber, + this.joystickInverted, + this.useGamepadButtons, + this.posGamepadButton, + this.negGamepadButton, + this.isGlobal, + this.posKeyModifiers, + this.negKeyModifiers + ) + } +} + +class InputSystem extends WorldSystem { + public static allInputs: Map = new Map() + + public static currentModifierState: ModifierState + // A list of keys currently being pressed private static _keysPressed: { [key: string]: boolean } = {} + private static _gpIndex: number | null + public static gamepad: Gamepad | null + + // The scheme most recently selected in the controls modal + public static selectedScheme: InputScheme | undefined + constructor() { super() - this.HandleKeyDown = this.HandleKeyDown.bind(this) - document.addEventListener("keydown", this.HandleKeyDown) + this.handleKeyDown = this.handleKeyDown.bind(this) + document.addEventListener("keydown", this.handleKeyDown) - this.HandleKeyUp = this.HandleKeyUp.bind(this) - document.addEventListener("keyup", this.HandleKeyUp) + this.handleKeyUp = this.handleKeyUp.bind(this) + document.addEventListener("keyup", this.handleKeyUp) - // TODO: Load saved inputs from mira (robot specific) & global inputs + this.gamepadConnected = this.gamepadConnected.bind(this) + window.addEventListener("gamepadconnected", this.gamepadConnected) - for (const key in defaultInputs) { - if (Object.prototype.hasOwnProperty.call(defaultInputs, key)) { - InputSystem.allInputs[key] = defaultInputs[key] - } - } + this.gamepadDisconnected = this.gamepadDisconnected.bind(this) + window.addEventListener("gamepaddisconnected", this.gamepadDisconnected) + + document.addEventListener("visibilitychange", () => { + if (document.hidden) this.clearKeyData() + }) } public Update(_: number): void { - if (!document.hasFocus()) { - for (const keyCode in InputSystem._keysPressed) delete InputSystem._keysPressed[keyCode] - return - } + InputSystem + // Fetch current gamepad information + if (InputSystem._gpIndex == null) InputSystem.gamepad = null + else InputSystem.gamepad = navigator.getGamepads()[InputSystem._gpIndex] - InputSystem._currentModifierState = { + if (!document.hasFocus()) this.clearKeyData() + + InputSystem.currentModifierState = { ctrl: InputSystem.isKeyPressed("ControlLeft") || InputSystem.isKeyPressed("ControlRight"), alt: InputSystem.isKeyPressed("AltLeft") || InputSystem.isKeyPressed("AltRight"), shift: InputSystem.isKeyPressed("ShiftLeft") || InputSystem.isKeyPressed("ShiftRight"), @@ -80,48 +193,67 @@ class InputSystem extends WorldSystem { } public Destroy(): void { - document.removeEventListener("keydown", this.HandleKeyDown) - document.removeEventListener("keyup", this.HandleKeyUp) + document.removeEventListener("keydown", this.handleKeyDown) + document.removeEventListener("keyup", this.handleKeyUp) + window.removeEventListener("gamepadconnected", this.gamepadConnected) + window.removeEventListener("gamepaddisconnected", this.gamepadDisconnected) } - // Called when any key is pressed - private HandleKeyDown(event: KeyboardEvent) { + // Called when any key is first pressed + private handleKeyDown(event: KeyboardEvent) { InputSystem._keysPressed[event.code] = true } // Called when any key is released - private HandleKeyUp(event: KeyboardEvent) { + private handleKeyUp(event: KeyboardEvent) { InputSystem._keysPressed[event.code] = false } - // Returns true if the given key is currently down - private static isKeyPressed(key: string): boolean { - return !!InputSystem._keysPressed[key] + private clearKeyData() { + for (const keyCode in InputSystem._keysPressed) delete InputSystem._keysPressed[keyCode] } - // If an input exists, return true if it is pressed - public static getInput(inputName: string): boolean { - // Checks if there is an input assigned to this action - if (inputName in this.allInputs) { - const targetInput = this.allInputs[inputName] + // Called once when a gamepad is first connected + private gamepadConnected(event: GamepadEvent) { + console.log( + "Gamepad connected at index %d: %s. %d buttons, %d axes.", + event.gamepad.index, + event.gamepad.id, + event.gamepad.buttons.length, + event.gamepad.axes.length + ) - // Check for input modifiers - if (!this.CompareModifiers(InputSystem._currentModifierState, targetInput.modifiers)) return false + InputSystem._gpIndex = event.gamepad.index + } - return this.isKeyPressed(targetInput.keyCode) - } + // Called once when a gamepad is first disconnected + private gamepadDisconnected(event: GamepadEvent) { + console.log("Gamepad disconnected from index %d: %s", event.gamepad.index, event.gamepad.id) - // If the input does not exist, returns false - return false + InputSystem._gpIndex = null } - // Combines two inputs into a positive/negative axis - public static GetAxis(positive: string, negative: string) { - return (this.getInput(positive) ? 1 : 0) - (this.getInput(negative) ? 1 : 0) + // Returns true if the given key is currently down + public static isKeyPressed(key: string, modifiers?: ModifierState): boolean { + if (modifiers != null && !InputSystem.compareModifiers(InputSystem.currentModifierState, modifiers)) + return false + + return !!InputSystem._keysPressed[key] + } + + // If an input exists, return true if it is pressed + public static getInput(inputName: string, assemblyId: string): number { + // Looks for an input assigned to this action + const targetScheme = this.allInputs.get(assemblyId) + const targetInput = targetScheme?.inputs.find(input => input.inputName == inputName) + + if (targetScheme == null || targetInput == null) return 0 + + return targetInput.getValue(targetScheme.usesGamepad) } // Returns true if two modifier states are identical - private static CompareModifiers(state1: ModifierState, state2: ModifierState): boolean { + private static compareModifiers(state1: ModifierState, state2: ModifierState): boolean { if (!state1 || !state2) return false return ( @@ -131,6 +263,33 @@ class InputSystem extends WorldSystem { state1.shift == state2.shift ) } + + // Returns a number between -1 and 1 with a deadband + public static getGamepadAxis(axisNumber: number): number { + if (InputSystem.gamepad == null) return 0 + + if (axisNumber < 0 || axisNumber >= InputSystem.gamepad.axes.length) return 0 + + const value = InputSystem.gamepad.axes[axisNumber] + + // Return value with a deadband + return Math.abs(value) < 0.15 ? 0 : value + } + + // Returns true if a gamepad is connected and a certain button is pressed + public static isGamepadButtonPressed(buttonNumber: number): boolean { + if (InputSystem.gamepad == null) return false + + if (buttonNumber < 0 || buttonNumber >= InputSystem.gamepad.buttons.length) return false + + const button = InputSystem.gamepad.buttons[buttonNumber] + if (button == null) return false + + return button.pressed + } } export default InputSystem +export { Input } +export { ButtonInput } +export { AxisInput } diff --git a/fission/src/systems/physics/Mechanism.ts b/fission/src/systems/physics/Mechanism.ts index 0761203f4c..939d79acec 100644 --- a/fission/src/systems/physics/Mechanism.ts +++ b/fission/src/systems/physics/Mechanism.ts @@ -8,18 +8,20 @@ export interface MechanismConstraint { } class Mechanism { - public rootBody: string - public nodeToBody: Map - public constraints: Array - public stepListeners: Array - public layerReserve: LayerReserve | undefined + public rootBody: string; + public nodeToBody: Map; + public constraints: Array; + public stepListeners: Array; + public layerReserve: LayerReserve | undefined; + public controllable: boolean; - public constructor(rootBody: string, bodyMap: Map, layerReserve?: LayerReserve) { - this.rootBody = rootBody - this.nodeToBody = bodyMap - this.constraints = [] - this.stepListeners = [] - this.layerReserve = layerReserve + public constructor(rootBody: string, bodyMap: Map, controllable: boolean, layerReserve?: LayerReserve) { + this.rootBody = rootBody; + this.nodeToBody = bodyMap; + this.constraints = []; + this.stepListeners = []; + this.controllable = controllable; + this.layerReserve = layerReserve; } public AddConstraint(mechConstraint: MechanismConstraint) { diff --git a/fission/src/systems/physics/PhysicsSystem.ts b/fission/src/systems/physics/PhysicsSystem.ts index 3ef4a4fcf0..7d9bce0944 100644 --- a/fission/src/systems/physics/PhysicsSystem.ts +++ b/fission/src/systems/physics/PhysicsSystem.ts @@ -50,7 +50,7 @@ const SUSPENSION_MIN_FACTOR = 0.1 const SUSPENSION_MAX_FACTOR = 0.3 /** - * The PhysicsSystem handles all Jolt Phyiscs interactions within Synthesis. + * The PhysicsSystem handles all Jolt Physics interactions within Synthesis. * This system can create physical representations of objects such as Robots, * Fields, and Game pieces, and simulate them. */ @@ -97,7 +97,7 @@ class PhysicsSystem extends WorldSystem { * * @param halfExtents The half extents of the Box. * @param mass Mass of the Box. Leave undefined to make Box static. - * @param position Posiition of the Box (default: 0, 0, 0) + * @param position Position of the Box (default: 0, 0, 0) * @param rotation Rotation of the Box (default 0, 0, 0, 1) * @returns Reference to Jolt Body */ @@ -191,18 +191,18 @@ class PhysicsSystem extends WorldSystem { public CreateMechanismFromParser(parser: MirabufParser): Mechanism { const layer = parser.assembly.dynamic ? new LayerReserve() : undefined - const bodyMap = this.CreateBodiesFromParser(parser, layer) - const rootBody = parser.rootNode - const mechanism = new Mechanism(rootBody, bodyMap, layer) - this.CreateJointsFromParser(parser, mechanism) - return mechanism + const bodyMap = this.CreateBodiesFromParser(parser, layer); + const rootBody = parser.rootNode; + const mechanism = new Mechanism(rootBody, bodyMap, parser.assembly.dynamic, layer); + this.CreateJointsFromParser(parser, mechanism); + return mechanism; } /** * Creates all the joints for a mirabuf assembly given an already compiled mapping of rigid nodes to bodies. * * @param parser Mirabuf parser with complete set of rigid nodes and assembly data. - * @param mechainsm Mapping of the name of rigid groups to Jolt bodies. Retrieved from CreateBodiesFromParser. + * @param mechanism Mapping of the name of rigid groups to Jolt bodies. Retrieved from CreateBodiesFromParser. */ public CreateJointsFromParser(parser: MirabufParser, mechanism: Mechanism) { const jointData = parser.assembly.data!.joints! @@ -300,7 +300,7 @@ class PhysicsSystem extends WorldSystem { * @param jointDefinition Joint definition. * @param bodyA Parent body to connect. * @param bodyB Child body to connect. - * @param versionNum Version number of the export. Used for compatability purposes. + * @param versionNum Version number of the export. Used for compatibility purposes. * @returns Resulting Jolt Hinge Constraint. */ private CreateHingeConstraint( @@ -339,7 +339,7 @@ class PhysicsSystem extends WorldSystem { hingeConstraintSettings.mHingeAxis1 ) - // Some values that are meant to be exactly PI are perceived as being past it, causing unexpected beavior. + // Some values that are meant to be exactly PI are perceived as being past it, causing unexpected behavior. // This safety check caps the values to be within [-PI, PI] wth minimal difference in precision. const piSafetyCheck = (v: number) => Math.min(3.14158, Math.max(-3.14158, v)) diff --git a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts index c9bb881a85..5d6ad16647 100644 --- a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts +++ b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts @@ -4,22 +4,19 @@ import Behavior from "./Behavior" import InputSystem from "@/systems/input/InputSystem" class ArcadeDriveBehavior extends Behavior { - leftWheels: WheelDriver[] - rightWheels: WheelDriver[] + private leftWheels: WheelDriver[]; + private rightWheels: WheelDriver[]; + private _assemblyName: string; private _driveSpeed = 30 private _turnSpeed = 30 - constructor( - leftWheels: WheelDriver[], - rightWheels: WheelDriver[], - leftStimuli: WheelRotationStimulus[], - rightStimuli: WheelRotationStimulus[] - ) { - super(leftWheels.concat(rightWheels), leftStimuli.concat(rightStimuli)) - - this.leftWheels = leftWheels - this.rightWheels = rightWheels + constructor(leftWheels: WheelDriver[], rightWheels: WheelDriver[], leftStimuli: WheelRotationStimulus[], rightStimuli: WheelRotationStimulus[], assemblyName: string) { + super(leftWheels.concat(rightWheels), leftStimuli.concat(rightStimuli)); + + this.leftWheels = leftWheels; + this.rightWheels = rightWheels; + this._assemblyName = assemblyName; } // Sets the drivetrains target linear and rotational velocity @@ -32,10 +29,10 @@ class ArcadeDriveBehavior extends Behavior { } public Update(_: number): void { - this.DriveSpeeds( - InputSystem.GetAxis("arcadeForward", "arcadeBackward") * this._driveSpeed, - InputSystem.GetAxis("arcadeRight", "arcadeLeft") * this._turnSpeed - ) + const driveInput = InputSystem.getInput("arcadeDrive", this._assemblyName); + const turnInput = InputSystem.getInput("arcadeTurn", this._assemblyName); + + this.DriveSpeeds(driveInput*this._driveSpeed, turnInput*this._turnSpeed); } } diff --git a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts index 890dfb4325..db4d038ad8 100644 --- a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts @@ -1,36 +1,21 @@ import HingeDriver from "../driver/HingeDriver" import HingeStimulus from "../stimulus/HingeStimulus" import Behavior from "./Behavior" -import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" +import InputSystem from "@/systems/input/InputSystem" class GenericArmBehavior extends Behavior { private _hingeDriver: HingeDriver - - private _positiveInput: string - private _negativeInput: string + private _inputName: string + private _assemblyName: string private _rotationalSpeed = 6 - constructor(hingeDriver: HingeDriver, hingeStimulus: HingeStimulus, jointIndex: number) { + constructor(hingeDriver: HingeDriver, hingeStimulus: HingeStimulus, jointIndex: number, assemblyName: string) { super([hingeDriver], [hingeStimulus]) - this._hingeDriver = hingeDriver - this._positiveInput = "joint " + jointIndex + " Positive" - this._negativeInput = "joint " + jointIndex + " Negative" - - // TODO: load inputs from mira - InputSystem.allInputs[this._positiveInput] = { - name: this._positiveInput, - keyCode: "Digit" + jointIndex.toString(), - isGlobal: false, - modifiers: emptyModifierState, - } - InputSystem.allInputs[this._negativeInput] = { - name: this._negativeInput, - keyCode: "Digit" + jointIndex.toString(), - isGlobal: false, - modifiers: { ctrl: false, alt: false, shift: true, meta: false }, - } + this._hingeDriver = hingeDriver + this._inputName = "joint " + jointIndex + this._assemblyName = assemblyName } // Sets the arms target rotational velocity @@ -39,7 +24,7 @@ class GenericArmBehavior extends Behavior { } public Update(_: number): void { - this.rotateArm(InputSystem.GetAxis(this._positiveInput, this._negativeInput) * this._rotationalSpeed) + this.rotateArm(InputSystem.getInput(this._inputName, this._assemblyName) * this._rotationalSpeed) } } diff --git a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts index e31282a28c..e3165c40a8 100644 --- a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts @@ -1,36 +1,21 @@ import SliderDriver from "../driver/SliderDriver" import SliderStimulus from "../stimulus/SliderStimulus" import Behavior from "./Behavior" -import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" +import InputSystem from "@/systems/input/InputSystem" class GenericElevatorBehavior extends Behavior { private _sliderDriver: SliderDriver - - private _positiveInput: string - private _negativeInput: string + private _inputName: string + private _assemblyName: string private _linearSpeed = 2.5 - constructor(sliderDriver: SliderDriver, sliderStimulus: SliderStimulus, jointIndex: number) { + constructor(sliderDriver: SliderDriver, sliderStimulus: SliderStimulus, jointIndex: number, assemblyName: string) { super([sliderDriver], [sliderStimulus]) - this._sliderDriver = sliderDriver - this._positiveInput = "joint " + jointIndex + " Positive" - this._negativeInput = "joint " + jointIndex + " Negative" - - // TODO: load inputs from mira - InputSystem.allInputs[this._positiveInput] = { - name: this._positiveInput, - keyCode: "Digit" + jointIndex.toString(), - isGlobal: false, - modifiers: emptyModifierState, - } - InputSystem.allInputs[this._negativeInput] = { - name: this._negativeInput, - keyCode: "Digit" + jointIndex.toString(), - isGlobal: false, - modifiers: { ctrl: false, alt: false, shift: true, meta: false }, - } + this._sliderDriver = sliderDriver + this._inputName = "joint " + jointIndex + this._assemblyName = assemblyName } // Changes the elevators target position @@ -39,7 +24,7 @@ class GenericElevatorBehavior extends Behavior { } public Update(_: number): void { - this.moveElevator(InputSystem.GetAxis(this._positiveInput, this._negativeInput) * this._linearSpeed) + this.moveElevator(InputSystem.getInput(this._inputName, this._assemblyName) * this._linearSpeed) } } diff --git a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts index cbce7c4b61..d1af884b31 100644 --- a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts +++ b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts @@ -1,42 +1,54 @@ -import Mechanism from "@/systems/physics/Mechanism" -import Brain from "../Brain" -import Behavior from "../behavior/Behavior" -import World from "@/systems/World" -import WheelDriver from "../driver/WheelDriver" -import WheelRotationStimulus from "../stimulus/WheelStimulus" -import ArcadeDriveBehavior from "../behavior/ArcadeDriveBehavior" -import { SimulationLayer } from "../SimulationSystem" -import Jolt from "@barclah/jolt-physics" -import JOLT from "@/util/loading/JoltSyncLoader" -import HingeDriver from "../driver/HingeDriver" -import HingeStimulus from "../stimulus/HingeStimulus" -import GenericArmBehavior from "../behavior/GenericArmBehavior" -import SliderDriver from "../driver/SliderDriver" -import SliderStimulus from "../stimulus/SliderStimulus" -import GenericElevatorBehavior from "../behavior/GenericElevatorBehavior" +import Mechanism from "@/systems/physics/Mechanism"; +import Brain from "../Brain"; +import Behavior from "../behavior/Behavior"; +import World from "@/systems/World"; +import WheelDriver from "../driver/WheelDriver"; +import WheelRotationStimulus from "../stimulus/WheelStimulus"; +import ArcadeDriveBehavior from "../behavior/ArcadeDriveBehavior"; +import { SimulationLayer } from "../SimulationSystem"; +import Jolt from "@barclah/jolt-physics"; +import JOLT from "@/util/loading/JoltSyncLoader"; +import HingeDriver from "../driver/HingeDriver"; +import HingeStimulus from "../stimulus/HingeStimulus"; +import GenericArmBehavior from "../behavior/GenericArmBehavior"; +import SliderDriver from "../driver/SliderDriver"; +import SliderStimulus from "../stimulus/SliderStimulus"; +import GenericElevatorBehavior from "../behavior/GenericElevatorBehavior"; +import InputSystem, { AxisInput } from "@/systems/input/InputSystem"; +import DefaultInputs from "@/systems/input/DefaultInputs"; class SynthesisBrain extends Brain { private _behaviors: Behavior[] = [] private _simLayer: SimulationLayer - _leftWheelIndices: number[] = [] - // Tracks how many joins have been made for unique controls - _currentJointIndex = 1 + private _currentJointIndex = 1; + + private static _currentRobotIndex = 0; - public constructor(mechanism: Mechanism) { - super(mechanism) + // Tracks how many robots are spawned for control identification + private _assemblyName: string - this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)! + public constructor(mechanism: Mechanism, assemblyName: string) { + super(mechanism); + + this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)!; + this._assemblyName = "[" + SynthesisBrain._currentRobotIndex.toString() + "] " + assemblyName; if (!this._simLayer) { console.log("SimulationLayer is undefined") return } - this.ConfigureArcadeDriveBehavior() - this.ConfigureArmBehaviors() - this.ConfigureElevatorBehaviors() + // Only adds controls to mechanisms that are controllable (ignores fields) + if (mechanism.controllable) { + this.configureArcadeDriveBehavior(); + this.configureArmBehaviors(); + this.configureElevatorBehaviors(); + this.configureInputs(); + + SynthesisBrain._currentRobotIndex++; + } } public Enable(): void {} @@ -49,14 +61,14 @@ class SynthesisBrain extends Brain { this._behaviors = [] } + public clearControls(): void { + InputSystem.allInputs.delete(this._assemblyName); + } + // Creates an instance of ArcadeDriveBehavior and automatically configures it - public ConfigureArcadeDriveBehavior() { - const wheelDrivers: WheelDriver[] = this._simLayer.drivers.filter( - driver => driver instanceof WheelDriver - ) as WheelDriver[] - const wheelStimuli: WheelRotationStimulus[] = this._simLayer.stimuli.filter( - stimulus => stimulus instanceof WheelRotationStimulus - ) as WheelRotationStimulus[] + public configureArcadeDriveBehavior() { + const wheelDrivers: WheelDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof WheelDriver) as WheelDriver[]; + const wheelStimuli: WheelRotationStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof WheelRotationStimulus) as WheelRotationStimulus[]; // Two body constraints are part of wheels and are used to determine which way a wheel is facing const fixedConstraints: Jolt.TwoBodyConstraint[] = this._mechanism.constraints @@ -89,38 +101,58 @@ class SynthesisBrain extends Brain { } } - this._behaviors.push(new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli)) + this._behaviors.push(new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli, this._assemblyName)); } // Creates instances of ArmBehavior and automatically configures them - public ConfigureArmBehaviors() { - const hingeDrivers: HingeDriver[] = this._simLayer.drivers.filter( - driver => driver instanceof HingeDriver - ) as HingeDriver[] - const hingeStimuli: HingeStimulus[] = this._simLayer.stimuli.filter( - stimulus => stimulus instanceof HingeStimulus - ) as HingeStimulus[] + public configureArmBehaviors() { + const hingeDrivers: HingeDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof HingeDriver) as HingeDriver[]; + const hingeStimuli: HingeStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof HingeStimulus) as HingeStimulus[]; for (let i = 0; i < hingeDrivers.length; i++) { - this._behaviors.push(new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex)) - this._currentJointIndex++ + this._behaviors.push(new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex, this._assemblyName)); + this._currentJointIndex++; } } // Creates instances of ElevatorBehavior and automatically configures them - public ConfigureElevatorBehaviors() { - const sliderDrivers: SliderDriver[] = this._simLayer.drivers.filter( - driver => driver instanceof SliderDriver - ) as SliderDriver[] - const sliderStimuli: SliderStimulus[] = this._simLayer.stimuli.filter( - stimulus => stimulus instanceof SliderStimulus - ) as SliderStimulus[] + public configureElevatorBehaviors() { + const sliderDrivers: SliderDriver[] = this._simLayer.drivers.filter((driver) => driver instanceof SliderDriver) as SliderDriver[]; + const sliderStimuli: SliderStimulus[] = this._simLayer.stimuli.filter((stimulus) => stimulus instanceof SliderStimulus) as SliderStimulus[]; for (let i = 0; i < sliderDrivers.length; i++) { - this._behaviors.push( - new GenericElevatorBehavior(sliderDrivers[i], sliderStimuli[i], this._currentJointIndex) - ) - this._currentJointIndex++ + this._behaviors.push(new GenericElevatorBehavior(sliderDrivers[i], sliderStimuli[i], this._currentJointIndex, this._assemblyName)); + this._currentJointIndex++; + } + } + + public configureInputs() { + const scheme = DefaultInputs.ALL_INPUT_SCHEMES[SynthesisBrain._currentRobotIndex]; + + InputSystem.allInputs.set(this._assemblyName, {schemeName: this._assemblyName, usesGamepad: scheme?.usesGamepad ?? false, inputs: []}); + const inputList = InputSystem.allInputs.get(this._assemblyName)!.inputs; + + if (scheme) { + const arcadeDrive = scheme.inputs.find(i => i.inputName === "arcadeDrive"); + if (arcadeDrive) + inputList.push(arcadeDrive.getCopy()); + else + inputList.push(new AxisInput("arcadeDrive")); + + const arcadeTurn = scheme.inputs.find(i => i.inputName === "arcadeTurn"); + if (arcadeTurn) + inputList.push(arcadeTurn.getCopy()); + else + inputList.push(new AxisInput("arcadeTurn")); + + for (let i = 1; i < this._currentJointIndex; i++) { + const controlPreset = scheme.inputs.find(input => input.inputName == ("joint " + i)) + + if (controlPreset) + inputList.push(controlPreset.getCopy()); + else + inputList.push(new AxisInput("joint " + i)); + } } } } diff --git a/fission/src/ui/components/Checkbox.tsx b/fission/src/ui/components/Checkbox.tsx index 59acc610a4..ace5b67f29 100644 --- a/fission/src/ui/components/Checkbox.tsx +++ b/fission/src/ui/components/Checkbox.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import Stack, { StackDirection } from "./Stack" import Label, { LabelSize } from "./Label" import { Switch } from "@mui/base/Switch" @@ -11,7 +11,8 @@ type CheckboxProps = { onClick?: (checked: boolean) => void } -const Checkbox: React.FC = ({ label, className, onClick }) => { +const Checkbox: React.FC = ({ label, className, defaultState, stateOverride, onClick }) => { + const [state] = useState(defaultState) return ( ) diff --git a/fission/src/ui/components/Dropdown.tsx b/fission/src/ui/components/Dropdown.tsx index 05b684d4a0..96592caca2 100644 --- a/fission/src/ui/components/Dropdown.tsx +++ b/fission/src/ui/components/Dropdown.tsx @@ -30,8 +30,39 @@ type DropdownProps = { onSelect: (opt: string) => void } -const Dropdown: React.FC = ({ label, options, onSelect }) => { - const [optionList, _setOptionList] = useState(options) +const Dropdown: React.FC = ({ + label, + className, + options, + onSelect, +}) => { + const [expanded, setExpanded] = useState(false) + const [optionList, setOptionList] = useState(options) + + type DropdownOptionProps = { + value: string + children?: ReactNode + className?: string + } + + const DropdownOption: React.FC = ({ + children, + value, + className, + }) => ( + { + const newOptions = options.filter(item => item !== value) + newOptions.unshift(value) + setOptionList(newOptions) + if (onSelect) onSelect(value) + }} + className={`block relative duration-100 hover:backdrop-brightness-90 w-full h-full px-2 py-2 ${className}`} + style={{ maxWidth: '249px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', paddingRight: '25px'}} + > + {children} + + ) return ( <> diff --git a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx index 9b3a1bffaf..d63561c968 100644 --- a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx +++ b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx @@ -4,19 +4,28 @@ import { FaGamepad } from "react-icons/fa6" import Stack, { StackDirection } from "../../components/Stack" import Label, { LabelSize } from "../../components/Label" import LabeledButton, { LabelPlacement } from "../../components/LabeledButton" -import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" +import InputSystem, { AxisInput, ButtonInput, ModifierState, EmptyModifierState } from "@/systems/input/InputSystem" +import Dropdown from "@/ui/components/Dropdown" +import Checkbox from "@/ui/components/Checkbox" +import DefaultInputs, { InputScheme } from "@/systems/input/DefaultInputs" +import Button from "@/ui/components/Button" +import { useModalControlContext } from "@/ui/ModalContext" // capitalize first letter -const transformKeyName = (control: Input) => { +// TODO: assumes all inputs are keyboard buttons +const transformKeyName = (keyCode: string, keyModifiers: ModifierState) => { let prefix = "" - if (control.modifiers) { - if (control.modifiers.meta) prefix += "Meta + " - if (control.modifiers.shift) prefix += "Shift + " - if (control.modifiers.ctrl) prefix += "Ctrl + " - if (control.modifiers.alt) prefix += "Alt + " + if (keyModifiers) { + if (keyModifiers.meta) prefix += "Meta + " + if (keyModifiers.shift) prefix += "Shift + " + if (keyModifiers.ctrl) prefix += "Ctrl + " + if (keyModifiers.alt) prefix += "Alt + " } - return prefix + keyCodeToCharacter(control.keyCode) + const displayName = prefix + keyCodeToCharacter(keyCode) + if (displayName == "") return "N/A" + + return displayName } // Converts camelCase to Title Case for the inputs modal @@ -26,6 +35,7 @@ const toTitleCase = (camelCase: string) => { return finalResult } +// Special characters only const codeToCharacterMap: { [code: string]: string } = { Slash: "/", Comma: ",", @@ -35,11 +45,31 @@ const codeToCharacterMap: { [code: string]: string } = { BackQuote: "`", Minus: "-", Equal: "=", - Backslash: "\\", //TODO + Backslash: "\\", Semicolon: ";", Quote: '"', } +const gamepadButtons: string[] = [ + "A", + "B", + "X", + "Y", + "Left Bumper", + "Right Bumper", + "Back", + "Start", + "Left Stick", + "Right Stick", + "UNKNOWN", + "UNKNOWN2", + "Dpad Up", + "Dpad Down", + "Dpad Left", + "Dpad Right", +] +const gamepadAxes: string[] = ["N/A", "Left X", "Left Y", "Right X", "Right Y"] + // Converts a key code to displayable character (ex: KeyA -> "A") const keyCodeToCharacter = (code: string) => { if (code.startsWith("Key")) return code.charAt(3) @@ -48,88 +78,420 @@ const keyCodeToCharacter = (code: string) => { if (code in codeToCharacterMap) return codeToCharacterMap[code] + if (code.startsWith("Gamepad")) return gamepadButtons[parseInt(code.substring(8))] + return code } +const moveElementToTop = (arr: string[], element: string | undefined) => { + if (element == undefined) { + return arr + } + + arr = arr.includes(element) ? [element, ...arr.filter(item => item !== element)] : arr + return arr +} + const ChangeInputsModal: React.FC = ({ modalId }) => { - const [loadedRobot, setLoadedRobot] = useState("") + const { openModal } = useModalControlContext() + + const [selectedScheme, setSelectedScheme] = useState(undefined) + const [useGamepad, setUseGamepad] = useState(false) + const [selectedInput, setSelectedInput] = useState("") + const [chosenKey, setChosenKey] = useState("") - const [modifierState, setModifierState] = useState(emptyModifierState) + const [chosenButton, setChosenButton] = useState(-1) + const [modifierState, setModifierState] = useState(EmptyModifierState) - useEffect(() => { - setTimeout(() => setLoadedRobot("Dozer v9"), 1) - }) + const [chosenGamepadAxis, setChosenGamepadAxis] = useState(-1) + const [chosenResetScheme, setChosenResetScheme] = useState("WASD") + const [useButtons, setUseButtons] = useState({}) + + // If there is a robot spawned, set it as the selected robot + if (selectedScheme == null && InputSystem.allInputs.size > 0) { + setTimeout(() => { + if (!InputSystem.selectedScheme) InputSystem.selectedScheme = InputSystem.allInputs.values().next().value + + setUseButtons({}) + setSelectedScheme(InputSystem.selectedScheme) + setUseGamepad(InputSystem.selectedScheme?.usesGamepad ?? false) + }, 1) + } + + const axisInputs = selectedScheme?.inputs.filter((input): input is AxisInput => input instanceof AxisInput) + + // Stores the states for using buttons or a joystick axis for gamepad axis inputs + type UseButtonsState = { + [key: string]: boolean + } + + if (axisInputs && Object.keys(useButtons).length == 0) { + axisInputs.forEach(input => { + useButtons[input.inputName] = input.useGamepadButtons + }) + } + + // Assign keyboard inputs when a key is pressed + if (!useGamepad && selectedInput && chosenKey) { + if (selectedInput.startsWith("pos")) { + const input = selectedScheme?.inputs.find( + input => input.inputName == selectedInput.substring(3) + ) as AxisInput + input.posKeyCode = chosenKey + input.posKeyModifiers = modifierState + } else if (selectedInput.startsWith("neg")) { + const input = selectedScheme?.inputs.find( + input => input.inputName == selectedInput.substring(3) + ) as AxisInput + input.negKeyCode = chosenKey + input.negKeyModifiers = modifierState + } else { + const input = selectedScheme?.inputs.find(input => input.inputName == selectedInput) as ButtonInput + input.keyCode = chosenKey + input.keyModifiers = modifierState + } - if (selectedInput && chosenKey) { - const selected = InputSystem.allInputs[selectedInput] - selected.keyCode = chosenKey - selected.modifiers = modifierState setChosenKey("") setSelectedInput("") - setModifierState(emptyModifierState) + setModifierState(EmptyModifierState) + } + // Assign gamepad button inputs when a button is pressed + else if (useGamepad && selectedInput && chosenButton != -1) { + if (selectedInput.startsWith("pos")) { + const input = selectedScheme?.inputs.find( + input => input.inputName == selectedInput.substring(3) + ) as AxisInput + input.posGamepadButton = chosenButton + } else if (selectedInput.startsWith("neg")) { + const input = selectedScheme?.inputs.find( + input => input.inputName == selectedInput.substring(3) + ) as AxisInput + input.negGamepadButton = chosenButton + } else { + const input = selectedScheme?.inputs.find(input => input.inputName == selectedInput) as ButtonInput + + input.gamepadButton = chosenButton + } + + setChosenButton(-1) + setSelectedInput("") + } + + // Assign gamepad axis inputs when a gamepad axis is selected + if (useGamepad && selectedInput && chosenGamepadAxis != -1) { + const selected = selectedScheme?.inputs.find(input => input.inputName == selectedInput) as AxisInput + selected.gamepadAxisNumber = chosenGamepadAxis - 1 + + setChosenGamepadAxis(-1) + setSelectedInput("") + } + + useEffect(() => { + const checkGamepadState = () => { + if (InputSystem.gamepad !== null) { + const pressedButtons = InputSystem.gamepad.buttons + .map((button, index) => (button.pressed ? index : null)) + .filter(index => index !== null) + .map(index => index!) + + if (pressedButtons.length > 0) setChosenButton(pressedButtons[0]) + else setChosenButton(-1) + } + requestAnimationFrame(checkGamepadState) + } + + checkGamepadState() + }) + + const KeyboardButtonSelection = (c: ButtonInput) => { + return ( + { + setSelectedInput(c.inputName) + }} + /> + ) + } + + const KeyboardAxisSelection = (c: AxisInput) => { + return ( +
+ + {/* Positive key */} + + { + setSelectedInput("pos" + c.inputName) + }} + /> + {/* Negative key */} + { + setSelectedInput("neg" + c.inputName) + }} + /> + +
+ ) + } + + const JoystickButtonSelection = (c: ButtonInput) => { + return ( + { + setSelectedInput(c.inputName) + }} + /> + ) + } + + const JoystickAxisSelection = (c: AxisInput) => { + return ( + { + setSelectedInput(c.inputName) + setChosenGamepadAxis(gamepadAxes.indexOf(value)) + }} + /> + ) + } + + const GamepadButtonAxisSelection = (c: AxisInput) => { + return ( +
+ + + {/* // Positive gamepad button */} + { + setSelectedInput("pos" + c.inputName) + }} + /> + {/* // Negative gamepad button */} + { + setSelectedInput("neg" + c.inputName) + }} + /> + +
+ ) } return ( } modalId={modalId}> - -
{ - setChosenKey(selectedInput ? e.code : "") - setModifierState({ - ctrl: e.ctrlKey, - alt: e.altKey, - shift: e.shiftKey, - meta: e.metaKey, - }) - }} - > - {loadedRobot ? ( - <> - - {Object.values(InputSystem.robotInputs).map(c => ( - { - setSelectedInput(c.name) + {Array.from(InputSystem.allInputs.keys()).length > 0 ? ( + <> + +
+ + { + const newScheme = InputSystem.allInputs.get(value) + if (newScheme == selectedScheme) return + + setSelectedScheme(undefined) + InputSystem.selectedScheme = newScheme }} /> - ))} - - ) : ( - - )} -
-
{ - setChosenKey(selectedInput ? e.key : "") - setModifierState({ - ctrl: e.ctrlKey, - alt: e.altKey, - shift: e.shiftKey, - meta: e.metaKey, - }) - }} - > - - {Object.values(InputSystem.globalInputs).map(c => ( - { - setSelectedInput(c.name) + {selectedScheme ? ( + <> + { + setUseGamepad(val) + if (selectedScheme) selectedScheme.usesGamepad = val + }} + /> + + {/* */} +
+ scheme.schemeName + )} + onSelect={value => { + setChosenResetScheme(value) + }} + /> +
+
+
{ + setChosenKey(selectedInput ? e.code : "") + setModifierState({ + ctrl: e.ctrlKey, + alt: e.altKey, + shift: e.shiftKey, + meta: e.metaKey, + }) }} - /> - ))} -
-
+ > + {selectedScheme ? ( + <> + + {selectedScheme.inputs.map(c => { + if (!useGamepad) { + // Keyboard button + if (c instanceof ButtonInput) { + return KeyboardButtonSelection(c) + } + // Keyboard Axis + else if (c instanceof AxisInput) { + return KeyboardAxisSelection(c) + } + } else { + // Joystick Button + if (c instanceof ButtonInput) { + return JoystickButtonSelection(c) + } + + // Gamepad axis + else if (c instanceof AxisInput) { + return ( +
+ {useButtons[c.inputName] + ? GamepadButtonAxisSelection(c) + : // Gamepad joystick axis + JoystickAxisSelection(c)} + + {/* // Button to switch between two buttons and a joystick axis */} + { + setUseButtons(prevState => ({ + ...prevState, + [c.inputName]: val, + })) + c.useGamepadButtons = val + }} + /> + {/* // Button to invert the joystick axis */} + { + c.joystickInverted = val + }} + /> +
+ ) + } + } + })} +
+ + ) : ( + + )} +
+
+ + ) : ( + + )}
) } diff --git a/fission/src/ui/modals/configuring/ResetAllInputsModal.tsx b/fission/src/ui/modals/configuring/ResetAllInputsModal.tsx new file mode 100644 index 0000000000..87471434d8 --- /dev/null +++ b/fission/src/ui/modals/configuring/ResetAllInputsModal.tsx @@ -0,0 +1,44 @@ +import React from "react" +import Modal, { ModalPropsImpl } from "@/components/Modal" +import { GrFormClose } from "react-icons/gr" +import { useModalControlContext } from "@/ui/ModalContext" +import InputSystem from "@/systems/input/InputSystem" +import DefaultInputs from "@/systems/input/DefaultInputs" + +const ResetAllInputsModal: React.FC = ({ modalId }) => { + const { openModal } = useModalControlContext() + + return ( + } + modalId={modalId} + onAccept={() => { + let i = 0 + InputSystem.allInputs.forEach(currentScheme => { + const scheme = DefaultInputs.ALL_INPUT_SCHEMES[i] + if (!currentScheme || !scheme) return + + scheme.inputs.forEach(newInput => { + const currentInput = currentScheme.inputs.find(i => i.inputName == newInput.inputName) + + if (currentInput) { + const inputIndex = currentScheme.inputs.indexOf(currentInput) + + currentScheme.inputs[inputIndex] = newInput.getCopy() + } + }) + currentScheme.usesGamepad = scheme.usesGamepad + + i++ + }) + openModal("change-inputs") + }} + onCancel={() => { + openModal("change-inputs") + }} + > + ) +} + +export default ResetAllInputsModal diff --git a/fission/vite.config.ts b/fission/vite.config.ts index 9d57b34383..5f19b30927 100644 --- a/fission/vite.config.ts +++ b/fission/vite.config.ts @@ -27,8 +27,8 @@ export default defineConfig({ { find: '@/components', replacement: path.resolve(__dirname, 'src', 'ui', 'components') }, { find: '@/modals', replacement: path.resolve(__dirname, 'src', 'ui', 'modals') }, { find: '@/panels', replacement: path.resolve(__dirname, 'src', 'ui', 'panels') }, - { find: '@', replacement: path.resolve(__dirname, 'src') } - ] + { find: '@', replacement: path.resolve(__dirname, 'src') }, + ], }, test: { globals: true, @@ -37,8 +37,8 @@ export default defineConfig({ enabled: true, name: 'chromium', headless: true, - provider: 'playwright' - } + provider: 'playwright', + }, }, server: { // this ensures that the browser opens upon server start @@ -51,17 +51,17 @@ export default defineConfig({ target: `http://localhost:${serverPort}${basePath}`, changeOrigin: true, secure: false, - rewrite: (path) => path.replace(/^\/api\/mira/, '/Downloadables/Mira') + rewrite: path => path.replace(/^\/api\/mira/, '/Downloadables/Mira'), }, '/api/auth': { target: `http://localhost:${dockerServerPort}/`, changeOrigin: true, - secure: false - } + secure: false, + }, }, }, build: { target: 'esnext', }, - base: basePath + base: basePath, })