diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index be6ae81225..a7d05a7bb8 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -168,6 +168,10 @@ class MirabufSceneObject extends SceneObject { this._brain?.clearControls() } + public GetRootNodeId(): Jolt.BodyID | undefined { + return this._mechanism.nodeToBody.get(this._mechanism.rootBody) + } + private CreateMeshForShape(shape: Jolt.Shape): THREE.Mesh { const scale = new JOLT.Vec3(1, 1, 1) const triangleContext = new JOLT.ShapeGetTriangles( diff --git a/fission/src/systems/input/InputSystem.ts b/fission/src/systems/input/InputSystem.ts index 56f4ccf8bb..442c330a89 100644 --- a/fission/src/systems/input/InputSystem.ts +++ b/fission/src/systems/input/InputSystem.ts @@ -1,4 +1,5 @@ import WorldSystem from "../WorldSystem" +import PreferencesSystem from "../preferences/PreferencesSystem" import { InputScheme } from "./DefaultInputs" export type ModifierState = { @@ -143,8 +144,6 @@ class AxisInput extends Input { } class InputSystem extends WorldSystem { - public static allInputs: Map = new Map() - public static currentModifierState: ModifierState // A list of keys currently being pressed @@ -241,11 +240,11 @@ class InputSystem extends WorldSystem { 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 an input exists, return it's value + public static getInput(inputName: string, assemblyName: string, assemblyIndex: number): number { + const targetScheme = PreferencesSystem.getRobotPreferences(assemblyName).inputsSchemes[assemblyIndex] + + const targetInput = targetScheme?.inputs.find(input => input.inputName == inputName) as Input if (targetScheme == null || targetInput == null) return 0 diff --git a/fission/src/systems/preferences/PreferenceTypes.ts b/fission/src/systems/preferences/PreferenceTypes.ts new file mode 100644 index 0000000000..03ed82f0db --- /dev/null +++ b/fission/src/systems/preferences/PreferenceTypes.ts @@ -0,0 +1,73 @@ +import { Vector3Tuple, Vector4Tuple } from "three" +import { InputScheme } from "../input/DefaultInputs" + +export type GlobalPreference = + | "ScreenMode" + | "QualitySettings" + | "ZoomSensitivity" + | "PitchSensitivity" + | "YawSensitivity" + | "ReportAnalytics" + | "UseMetric" + | "RenderScoringZones" + +export const RobotPreferencesKey: string = "Robots" +export const FieldPreferencesKey: string = "Fields" + +export const DefaultGlobalPreferences: { [key: string]: unknown } = { + ScreenMode: "Windowed", + QualitySettings: "High", + ZoomSensitivity: 15, + PitchSensitivity: 10, + YawSensitivity: 3, + ReportAnalytics: false, + UseMetric: false, + RenderScoringZones: true, +} + +export type IntakePreferences = { + location: Vector3Tuple + diameter: number +} + +export type EjectorPreferences = { + location: Vector3Tuple + ejectorVelocity: number +} + +export type RobotPreferences = { + inputsSchemes: InputScheme[] + intake: IntakePreferences + ejector: EjectorPreferences +} + +export type Alliance = "Blue" | "Red" + +export type ScoringZonePreferences = { + name: string + alliance: Alliance + parent: string + points: number + destroyGamepiece: boolean + persistentPoints: boolean + localPosition: Vector3Tuple + localRotation: Vector4Tuple + localScale: Vector3Tuple +} + +export type FieldPreferences = { + defaultSpawnLocation: Vector3Tuple + scoringZones: ScoringZonePreferences[] +} + +export function DefaultRobotPreferences(): RobotPreferences { + return { + inputsSchemes: [], + intake: { location: [0, 0, 0], diameter: 1 }, + ejector: { location: [0, 0, 0], ejectorVelocity: 1 }, + } +} + +export function DefaultFieldPreferences(): FieldPreferences { + return { defaultSpawnLocation: [0, 1, 0], scoringZones: [] } +} diff --git a/fission/src/systems/preferences/PreferencesSystem.ts b/fission/src/systems/preferences/PreferencesSystem.ts new file mode 100644 index 0000000000..9a34a08bb6 --- /dev/null +++ b/fission/src/systems/preferences/PreferencesSystem.ts @@ -0,0 +1,151 @@ +import { + DefaultFieldPreferences, + DefaultGlobalPreferences, + DefaultRobotPreferences, + FieldPreferences, + FieldPreferencesKey, + GlobalPreference, + RobotPreferences, + RobotPreferencesKey, +} from "./PreferenceTypes" + +class PreferenceEvent extends Event { + public prefName: GlobalPreference + public prefValue: object + + constructor(prefName: GlobalPreference, prefValue: object) { + super("preferenceChanged") + this.prefName = prefName + this.prefValue = prefValue + } +} + +class PreferencesSystem { + private static _preferences: { [key: string]: object } + private static _localStorageKey = "Preferences" + + /** Event dispatched when any global preference is updated */ + public static addEventListener(callback: (e: PreferenceEvent) => unknown) { + window.addEventListener("preferenceChanged", callback as EventListener) + } + + /** Sets a global preference to be a value of a specific type */ + public static setGlobalPreference(key: GlobalPreference, value: T) { + if (this._preferences == undefined) this.loadPreferences() + + window.dispatchEvent(new PreferenceEvent(key, value)) + this._preferences[key] = value + } + + /** Gets any preference from the preferences map */ + private static getPreference(key: string): T | undefined { + if (this._preferences == undefined) this.loadPreferences() + + return this._preferences[key] as T + } + + /** Gets a global preference, or it's default value if it does not exist in the preferences map */ + public static getGlobalPreference(key: GlobalPreference): T { + const customPref = this.getPreference(key) + if (customPref != undefined) return customPref + + const defaultPref = DefaultGlobalPreferences[key] + if (defaultPref != undefined) return defaultPref as T + + throw new Error("Preference '" + key + "' is not assigned a default!") + } + + /** Gets a RobotPreferences object for a robot of a specific mira name */ + public static getRobotPreferences(miraName: string): RobotPreferences { + const allRoboPrefs = this.getAllRobotPreferences() + + if (allRoboPrefs[miraName] == undefined) { + const defaultPrefs = DefaultRobotPreferences() + allRoboPrefs[miraName] = defaultPrefs + return defaultPrefs + } + + return allRoboPrefs[miraName] + } + + /** Gets preferences for every robot in local storage */ + public static getAllRobotPreferences(): { [key: string]: RobotPreferences } { + let allRoboPrefs = this.getPreference<{ [key: string]: RobotPreferences }>(RobotPreferencesKey) + + if (allRoboPrefs == undefined) { + allRoboPrefs = {} + this._preferences[RobotPreferencesKey] = allRoboPrefs + } + + return allRoboPrefs + } + + /** Gets a FieldPreferences object for a robot of a specific mira name */ + public static getFieldPreferences(miraName: string): FieldPreferences { + const allFieldPrefs = this.getAllFieldPreferences() + + if (allFieldPrefs[miraName] == undefined) { + const defaultPrefs = DefaultFieldPreferences() + allFieldPrefs[miraName] = defaultPrefs + return defaultPrefs + } + + return allFieldPrefs[miraName] + } + + /** Gets preferences for every robot in local storage */ + public static getAllFieldPreferences(): { [key: string]: FieldPreferences } { + let allFieldPrefs = this.getPreference<{ [key: string]: FieldPreferences }>(RobotPreferencesKey) + + if (allFieldPrefs == undefined) { + allFieldPrefs = {} + this._preferences[FieldPreferencesKey] = allFieldPrefs + } + + return allFieldPrefs + } + + /** Load all preferences from local storage */ + public static loadPreferences() { + const loadedPrefs = window.localStorage.getItem(this._localStorageKey) + + if (loadedPrefs == undefined) { + console.log("Preferences not found in local storage!") + this._preferences = {} + return + } + + try { + this._preferences = JSON.parse(loadedPrefs) + } catch (e) { + console.error(e) + this._preferences = {} + } + } + + /** Save all preferences to local storage */ + public static savePreferences() { + if (this._preferences == undefined) { + console.log("Preferences not loaded!") + return + } + + const prefsString = JSON.stringify(this._preferences) + + if (prefsString == undefined) { + console.log("Preferences loaded but undefined") + return + } + + window.localStorage.setItem(this._localStorageKey, prefsString) + } + + /** Remove all preferences from local storage */ + public static clearPreferences() { + window.localStorage.removeItem(this._localStorageKey) + this._preferences = {} + console.log("Cleared all preferences") + } +} + +export default PreferencesSystem diff --git a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts index 3f2a96b0d1..374f5684d9 100644 --- a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts +++ b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts @@ -7,6 +7,7 @@ class ArcadeDriveBehavior extends Behavior { private leftWheels: WheelDriver[] private rightWheels: WheelDriver[] private _assemblyName: string + private _assemblyIndex: number private _driveSpeed = 30 private _turnSpeed = 30 @@ -16,13 +17,15 @@ class ArcadeDriveBehavior extends Behavior { rightWheels: WheelDriver[], leftStimuli: WheelRotationStimulus[], rightStimuli: WheelRotationStimulus[], - assemblyName: string + assemblyName: string, + assemblyIndex: number ) { super(leftWheels.concat(rightWheels), leftStimuli.concat(rightStimuli)) this.leftWheels = leftWheels this.rightWheels = rightWheels this._assemblyName = assemblyName + this._assemblyIndex = assemblyIndex } // Sets the drivetrains target linear and rotational velocity @@ -35,8 +38,8 @@ class ArcadeDriveBehavior extends Behavior { } public Update(_: number): void { - const driveInput = InputSystem.getInput("arcadeDrive", this._assemblyName) - const turnInput = InputSystem.getInput("arcadeTurn", this._assemblyName) + const driveInput = InputSystem.getInput("arcadeDrive", this._assemblyName, this._assemblyIndex) + const turnInput = InputSystem.getInput("arcadeTurn", this._assemblyName, this._assemblyIndex) 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 db4d038ad8..fc9e9bbd5d 100644 --- a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts @@ -7,15 +7,23 @@ class GenericArmBehavior extends Behavior { private _hingeDriver: HingeDriver private _inputName: string private _assemblyName: string + private _assemblyIndex: number private _rotationalSpeed = 6 - constructor(hingeDriver: HingeDriver, hingeStimulus: HingeStimulus, jointIndex: number, assemblyName: string) { + constructor( + hingeDriver: HingeDriver, + hingeStimulus: HingeStimulus, + jointIndex: number, + assemblyName: string, + assemblyIndex: number + ) { super([hingeDriver], [hingeStimulus]) this._hingeDriver = hingeDriver this._inputName = "joint " + jointIndex this._assemblyName = assemblyName + this._assemblyIndex = assemblyIndex } // Sets the arms target rotational velocity @@ -24,7 +32,9 @@ class GenericArmBehavior extends Behavior { } public Update(_: number): void { - this.rotateArm(InputSystem.getInput(this._inputName, this._assemblyName) * this._rotationalSpeed) + this.rotateArm( + InputSystem.getInput(this._inputName, this._assemblyName, this._assemblyIndex) * this._rotationalSpeed + ) } } diff --git a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts index e3165c40a8..f1c01bde56 100644 --- a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts +++ b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts @@ -7,15 +7,23 @@ class GenericElevatorBehavior extends Behavior { private _sliderDriver: SliderDriver private _inputName: string private _assemblyName: string + private _assemblyIndex: number private _linearSpeed = 2.5 - constructor(sliderDriver: SliderDriver, sliderStimulus: SliderStimulus, jointIndex: number, assemblyName: string) { + constructor( + sliderDriver: SliderDriver, + sliderStimulus: SliderStimulus, + jointIndex: number, + assemblyName: string, + assemblyIndex: number + ) { super([sliderDriver], [sliderStimulus]) this._sliderDriver = sliderDriver this._inputName = "joint " + jointIndex this._assemblyName = assemblyName + this._assemblyIndex = assemblyIndex } // Changes the elevators target position @@ -24,7 +32,9 @@ class GenericElevatorBehavior extends Behavior { } public Update(_: number): void { - this.moveElevator(InputSystem.getInput(this._inputName, this._assemblyName) * this._linearSpeed) + this.moveElevator( + InputSystem.getInput(this._inputName, this._assemblyName, this._assemblyIndex) * this._linearSpeed + ) } } diff --git a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts index 384e566a1c..9b33a0cbc5 100644 --- a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts +++ b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts @@ -14,8 +14,9 @@ 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" +import { AxisInput, ButtonInput, Input } from "@/systems/input/InputSystem" +import DefaultInputs, { InputScheme } from "@/systems/input/DefaultInputs" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" class SynthesisBrain extends Brain { private _behaviors: Behavior[] = [] @@ -24,16 +25,23 @@ class SynthesisBrain extends Brain { // Tracks how many joins have been made for unique controls private _currentJointIndex = 1 - private static _currentRobotIndex = 0 - - // Tracks how many robots are spawned for control identification private _assemblyName: string + private _assemblyIndex: number = 0 + + // Tracks the number of each specific mira file spawned + public static numberRobotsSpawned: { [key: string]: number } = {} + + // A list of all the robots spawned including their assembly index + public static robotsSpawned: string[] = [] + + // The total number of robots spawned + private static _currentRobotIndex: number = 0 public constructor(mechanism: Mechanism, assemblyName: string) { super(mechanism) this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)! - this._assemblyName = "[" + SynthesisBrain._currentRobotIndex.toString() + "] " + assemblyName + this._assemblyName = assemblyName if (!this._simLayer) { console.log("SimulationLayer is undefined") @@ -42,13 +50,22 @@ class SynthesisBrain extends Brain { // Only adds controls to mechanisms that are controllable (ignores fields) if (mechanism.controllable) { + if (SynthesisBrain.numberRobotsSpawned[assemblyName] == undefined) + SynthesisBrain.numberRobotsSpawned[assemblyName] = 0 + else SynthesisBrain.numberRobotsSpawned[assemblyName]++ + + this._assemblyIndex = SynthesisBrain.numberRobotsSpawned[assemblyName] + SynthesisBrain.robotsSpawned.push(this.getNumberedAssemblyName()) + this.configureArcadeDriveBehavior() this.configureArmBehaviors() this.configureElevatorBehaviors() this.configureInputs() - - SynthesisBrain._currentRobotIndex++ + } else { + this.configureField() } + + SynthesisBrain._currentRobotIndex++ } public Enable(): void {} @@ -62,7 +79,8 @@ class SynthesisBrain extends Brain { } public clearControls(): void { - InputSystem.allInputs.delete(this._assemblyName) + const index = SynthesisBrain.robotsSpawned.indexOf(this.getNumberedAssemblyName()) + SynthesisBrain.robotsSpawned.splice(index, 1) } // Creates an instance of ArcadeDriveBehavior and automatically configures it @@ -106,7 +124,14 @@ class SynthesisBrain extends Brain { } this._behaviors.push( - new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli, this._assemblyName) + new ArcadeDriveBehavior( + leftWheels, + rightWheels, + leftStimuli, + rightStimuli, + this._assemblyName, + this._assemblyIndex + ) ) } @@ -121,7 +146,13 @@ class SynthesisBrain extends Brain { for (let i = 0; i < hingeDrivers.length; i++) { this._behaviors.push( - new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex, this._assemblyName) + new GenericArmBehavior( + hingeDrivers[i], + hingeStimuli[i], + this._currentJointIndex, + this._assemblyName, + this._assemblyIndex + ) ) this._currentJointIndex++ } @@ -142,22 +173,32 @@ class SynthesisBrain extends Brain { sliderDrivers[i], sliderStimuli[i], this._currentJointIndex, - this._assemblyName + this._assemblyName, + this._assemblyIndex ) ) this._currentJointIndex++ } } - public configureInputs() { + private configureInputs() { + // Check for existing inputs + const robotConfig = PreferencesSystem.getRobotPreferences(this._assemblyName) + if (robotConfig.inputsSchemes[this._assemblyIndex] != undefined) { + SynthesisBrain.parseInputs(robotConfig.inputsSchemes[this._assemblyIndex]) + return + } + + // Configure with default inputs + const scheme = DefaultInputs.ALL_INPUT_SCHEMES[SynthesisBrain._currentRobotIndex] - InputSystem.allInputs.set(this._assemblyName, { + robotConfig.inputsSchemes[this._assemblyIndex] = { schemeName: this._assemblyName, usesGamepad: scheme?.usesGamepad ?? false, inputs: [], - }) - const inputList = InputSystem.allInputs.get(this._assemblyName)!.inputs + } + const inputList = robotConfig.inputsSchemes[this._assemblyIndex].inputs if (scheme) { const arcadeDrive = scheme.inputs.find(i => i.inputName === "arcadeDrive") @@ -176,6 +217,54 @@ class SynthesisBrain extends Brain { } } } + + private configureField() { + const fieldPrefs = PreferencesSystem.getFieldPreferences(this._assemblyName) + console.log("Loaded field prefs " + fieldPrefs) + + /** Put any scoring zone or other field configuration here */ + } + + private getNumberedAssemblyName(): string { + return `[${this._assemblyIndex}] ${this._assemblyName}` + } + + private static parseInputs(rawInputs: InputScheme) { + for (let i = 0; i < rawInputs.inputs.length; i++) { + const rawInput = rawInputs.inputs[i] + let parsedInput: Input + + if ((rawInput as ButtonInput).keyCode != undefined) { + const rawButton = rawInput as ButtonInput + + parsedInput = new ButtonInput( + rawButton.inputName, + rawButton.keyCode, + rawButton.gamepadButton, + rawButton.isGlobal, + rawButton.keyModifiers + ) + } else { + const rawAxis = rawInput as AxisInput + + parsedInput = new AxisInput( + rawAxis.inputName, + rawAxis.posKeyCode, + rawAxis.negKeyCode, + rawAxis.gamepadAxisNumber, + rawAxis.joystickInverted, + rawAxis.useGamepadButtons, + rawAxis.posGamepadButton, + rawAxis.negGamepadButton, + rawAxis.isGlobal, + rawAxis.posKeyModifiers, + rawAxis.negKeyModifiers + ) + } + + rawInputs.inputs[i] = parsedInput + } + } } export default SynthesisBrain diff --git a/fission/src/ui/components/MainHUD.tsx b/fission/src/ui/components/MainHUD.tsx index 48f4eeb917..2139ffdcdd 100644 --- a/fission/src/ui/components/MainHUD.tsx +++ b/fission/src/ui/components/MainHUD.tsx @@ -5,7 +5,7 @@ import { BiMenuAltLeft } from "react-icons/bi" import { GrFormClose } from "react-icons/gr" import { GiSteeringWheel } from "react-icons/gi" import { HiDownload } from "react-icons/hi" -import { IoGameControllerOutline, IoPeople, IoRefresh, IoTimer } from "react-icons/io5" +import { IoBug, IoGameControllerOutline, IoPeople, IoRefresh, IoTimer } from "react-icons/io5" import { useModalControlContext } from "@/ui/ModalContext" import { usePanelControlContext } from "@/ui/PanelContext" import { motion } from "framer-motion" @@ -20,6 +20,7 @@ import MirabufSceneObject from "@/mirabuf/MirabufSceneObject" import { Button } from "@mui/base/Button" import MirabufCachingService, { MiraType } from "@/mirabuf/MirabufLoader" import Jolt from "@barclah/jolt-physics" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" import { AiOutlineDoubleRight } from "react-icons/ai" type ButtonProps = { @@ -122,6 +123,11 @@ const MainHUD: React.FC = () => { onClick={() => openPanel("poker")} /> } onClick={TestGodMode} /> + } + onClick={() => PreferencesSystem.clearPreferences()} + /> } diff --git a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx index d63561c968..4f1d6d24ad 100644 --- a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx +++ b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx @@ -10,9 +10,10 @@ import Checkbox from "@/ui/components/Checkbox" import DefaultInputs, { InputScheme } from "@/systems/input/DefaultInputs" import Button from "@/ui/components/Button" import { useModalControlContext } from "@/ui/ModalContext" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" +import SynthesisBrain from "@/systems/simulation/synthesis_brain/SynthesisBrain" // capitalize first letter -// TODO: assumes all inputs are keyboard buttons const transformKeyName = (keyCode: string, keyModifiers: ModifierState) => { let prefix = "" if (keyModifiers) { @@ -109,9 +110,12 @@ const ChangeInputsModal: React.FC = ({ modalId }) => { const [useButtons, setUseButtons] = useState({}) // If there is a robot spawned, set it as the selected robot - if (selectedScheme == null && InputSystem.allInputs.size > 0) { + if (selectedScheme == null && Object.keys(PreferencesSystem.getAllRobotPreferences()).length > 0) { setTimeout(() => { - if (!InputSystem.selectedScheme) InputSystem.selectedScheme = InputSystem.allInputs.values().next().value + if (!InputSystem.selectedScheme) + InputSystem.selectedScheme = Object.values( + PreferencesSystem.getAllRobotPreferences() + )[0].inputsSchemes[0] setUseButtons({}) setSelectedScheme(InputSystem.selectedScheme) @@ -334,8 +338,16 @@ const ChangeInputsModal: React.FC = ({ modalId }) => { } return ( - } modalId={modalId}> - {Array.from(InputSystem.allInputs.keys()).length > 0 ? ( + } + modalId={modalId} + onAccept={() => { + PreferencesSystem.savePreferences() + InputSystem.selectedScheme = undefined + }} + > + {Object.keys(PreferencesSystem.getAllRobotPreferences()).length > 0 ? ( <>
@@ -344,11 +356,17 @@ const ChangeInputsModal: React.FC = ({ modalId }) => { label={"Select Robot"} // Moves the selected option to the start of the array options={moveElementToTop( - Array.from(InputSystem.allInputs.keys()), + SynthesisBrain.robotsSpawned, InputSystem?.selectedScheme?.schemeName )} onSelect={value => { - const newScheme = InputSystem.allInputs.get(value) + const roboName = value.substring(4) + const controlSchemeIndex: number = +value.charAt(1) + + const newScheme = + PreferencesSystem.getAllRobotPreferences()[roboName].inputsSchemes[ + controlSchemeIndex + ] if (newScheme == selectedScheme) return setSelectedScheme(undefined) diff --git a/fission/src/ui/modals/configuring/SettingsModal.tsx b/fission/src/ui/modals/configuring/SettingsModal.tsx index 37175752c2..d3f9cb42b7 100644 --- a/fission/src/ui/modals/configuring/SettingsModal.tsx +++ b/fission/src/ui/modals/configuring/SettingsModal.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import { useModalControlContext } from "@/ui/ModalContext" import Modal, { ModalPropsImpl } from "@/components/Modal" import { FaGear } from "react-icons/fa6" @@ -7,36 +7,135 @@ import Dropdown from "@/components/Dropdown" import Button from "@/components/Button" import Slider from "@/components/Slider" import Checkbox from "@/components/Checkbox" +import PreferencesSystem from "@/systems/preferences/PreferencesSystem" + +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 screenModeOptions = ["Windowed", "Fullscreen"] +const qualitySettingsOptions = ["Low", "Medium", "High", "Ultra"] const SettingsModal: React.FC = ({ modalId }) => { const { openModal } = useModalControlContext() + const [screenMode, setScreenMode] = useState(PreferencesSystem.getGlobalPreference("ScreenMode")) + const [qualitySettings, setQualitySettings] = useState( + PreferencesSystem.getGlobalPreference("QualitySettings") + ) + const [zoomSensitivity, setZoomSensitivity] = useState( + PreferencesSystem.getGlobalPreference("ZoomSensitivity") + ) + const [pitchSensitivity, setPitchSensitivity] = useState( + PreferencesSystem.getGlobalPreference("PitchSensitivity") + ) + const [yawSensitivity, setYawSensitivity] = useState( + PreferencesSystem.getGlobalPreference("YawSensitivity") + ) + const [reportAnalytics, setReportAnalytics] = useState( + PreferencesSystem.getGlobalPreference("ReportAnalytics") + ) + const [useMetric, setUseMetric] = useState(PreferencesSystem.getGlobalPreference("UseMetric")) + const [renderScoringZones, setRenderScoringZones] = useState( + PreferencesSystem.getGlobalPreference("RenderScoringZones") + ) + + const saveSettings = () => { + PreferencesSystem.setGlobalPreference("ScreenMode", screenMode) + PreferencesSystem.setGlobalPreference("QualitySettings", qualitySettings) + PreferencesSystem.setGlobalPreference("ZoomSensitivity", zoomSensitivity) + PreferencesSystem.setGlobalPreference("PitchSensitivity", pitchSensitivity) + PreferencesSystem.setGlobalPreference("YawSensitivity", yawSensitivity) + PreferencesSystem.setGlobalPreference("ReportAnalytics", reportAnalytics) + PreferencesSystem.setGlobalPreference("UseMetric", useMetric) + PreferencesSystem.setGlobalPreference("RenderScoringZones", renderScoringZones) + + PreferencesSystem.savePreferences() + } + return ( - } modalId={modalId}> + } + modalId={modalId} + onAccept={() => { + saveSettings() + }} + > - {}} /> - {}} /> + ("ScreenMode") + )} + onSelect={selected => { + setScreenMode(selected) + }} + /> + ("QualitySettings") + )} + onSelect={selected => { + setQualitySettings(selected) + }} + />