diff --git a/fission/src/aps/APS.ts b/fission/src/aps/APS.ts index 065c976e9d..009f4a4901 100644 --- a/fission/src/aps/APS.ts +++ b/fission/src/aps/APS.ts @@ -1,4 +1,3 @@ -import { MainHUD_AddToast } from "@/components/MainHUD" import { Random } from "@/util/Random" const APS_AUTH_KEY = "aps_auth" diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts index ef6f34fef9..b2783a81a5 100644 --- a/fission/src/mirabuf/MirabufSceneObject.ts +++ b/fission/src/mirabuf/MirabufSceneObject.ts @@ -96,9 +96,10 @@ class MirabufSceneObject extends SceneObject { colliderMesh.position.setFromMatrixPosition(transform) colliderMesh.rotation.setFromRotationMatrix(transform) - const comTransform = JoltMat44_ThreeMatrix4(body.GetCenterOfMassTransform()) - comMesh.position.setFromMatrixPosition(comTransform) - comMesh.rotation.setFromRotationMatrix(comTransform) + const comTransform = JoltMat44_ThreeMatrix4(body.GetCenterOfMassTransform()); + + comMesh.position.setFromMatrixPosition(comTransform); + comMesh.rotation.setFromRotationMatrix(comTransform); } }) } diff --git a/fission/src/systems/World.ts b/fission/src/systems/World.ts index 3619788f31..e38fac6813 100644 --- a/fission/src/systems/World.ts +++ b/fission/src/systems/World.ts @@ -1,8 +1,9 @@ import * as THREE from "three" -import PhysicsSystem from "./physics/PhysicsSystem" -import SceneRenderer from "./scene/SceneRenderer" -import SimulationSystem from "./simulation/SimulationSystem" +import PhysicsSystem from "./physics/PhysicsSystem"; +import SceneRenderer from "./scene/SceneRenderer"; +import SimulationSystem from "./simulation/SimulationSystem"; +import InputSystem from "./input/InputSystem"; class World { private static _isAlive: boolean = false @@ -11,6 +12,7 @@ class World { private static _sceneRenderer: SceneRenderer private static _physicsSystem: PhysicsSystem private static _simulationSystem: SimulationSystem + private static _inputSystem: InputSystem public static get isAlive() { return World._isAlive @@ -25,6 +27,9 @@ class World { public static get SimulationSystem() { return World._simulationSystem } + public static get InputSystem() { + return World._inputSystem + } public static InitWorld() { if (World._isAlive) return @@ -32,9 +37,10 @@ class World { World._clock = new THREE.Clock() World._isAlive = true - World._sceneRenderer = new SceneRenderer() - World._physicsSystem = new PhysicsSystem() - World._simulationSystem = new SimulationSystem() + World._sceneRenderer = new SceneRenderer(); + World._physicsSystem = new PhysicsSystem(); + World._simulationSystem = new SimulationSystem(); + World._inputSystem = new InputSystem(); } public static DestroyWorld() { @@ -42,16 +48,18 @@ class World { World._isAlive = false - World._physicsSystem.Destroy() - World._sceneRenderer.Destroy() - World._simulationSystem.Destroy() + World._physicsSystem.Destroy(); + World._sceneRenderer.Destroy(); + World._simulationSystem.Destroy(); + World._inputSystem.Destroy(); } public static UpdateWorld() { - const deltaT = World._clock.getDelta() - World._simulationSystem.Update(deltaT) - World._physicsSystem.Update(deltaT) - World._sceneRenderer.Update(deltaT) + const deltaT = World._clock.getDelta(); + World._simulationSystem.Update(deltaT); + World._physicsSystem.Update(deltaT); + World._inputSystem.Update(deltaT); + World._sceneRenderer.Update(deltaT); } } diff --git a/fission/src/systems/input/InputSystem.ts b/fission/src/systems/input/InputSystem.ts index e69de29bb2..cd7f4610e6 100644 --- a/fission/src/systems/input/InputSystem.ts +++ b/fission/src/systems/input/InputSystem.ts @@ -0,0 +1,130 @@ +import WorldSystem from "../WorldSystem"; + +declare global { + type ModifierState = { + alt: boolean + ctrl: boolean + shift: boolean + meta: boolean + } + + type Input = { + name: string + keyCode: string + isGlobal: boolean + modifiers: ModifierState + } +} + +export const emptyModifierState: ModifierState = { ctrl: false, alt: false, shift: false, meta: false }; + +// When a robot is loaded, default inputs replace any unassigned inputs +const defaultInputs: { [key: string]: Input } = { + "intake": { name: "intake", keyCode: "KeyE", isGlobal: true, modifiers: emptyModifierState }, + "shootGamepiece": { name: "shootGamepiece", keyCode: "KeyQ", isGlobal: true, modifiers: emptyModifierState }, + "enableGodMode": { name: "enableGodMode", keyCode: "KeyG", isGlobal: true, modifiers: emptyModifierState }, + + "arcadeForward": { name: "arcadeForward", keyCode: "KeyW", isGlobal: false, modifiers: emptyModifierState }, + "arcadeBackward": { name: "arcadeBackward", keyCode: "KeyS", isGlobal: false, modifiers: emptyModifierState }, + "arcadeLeft": { name: "arcadeLeft", keyCode: "KeyA", isGlobal: false, modifiers: emptyModifierState }, + "arcadeRight": { name: "arcadeRight", keyCode: "KeyD", isGlobal: false, modifiers: emptyModifierState }, +} + +class InputSystem extends WorldSystem { + public static allInputs: { [key: string]: Input } = { } + private static _currentModifierState: ModifierState; + + // Inputs global to all of synthesis like camera controls + public static get globalInputs(): { [key: string]: Input } { + return Object.fromEntries( + Object.entries(InputSystem.allInputs) + .filter(([_, input]) => input.isGlobal)); + } + + // Robot specific controls like driving + public static get robotInputs(): { [key: string]: Input } { + return Object.fromEntries( + Object.entries(InputSystem.allInputs) + .filter(([_, input]) => !input.isGlobal)); + } + + // A list of keys currently being pressed + private static _keysPressed: { [key: string]: boolean } = {}; + + constructor() { + super(); + + this.HandleKeyDown = this.HandleKeyDown.bind(this); + document.addEventListener('keydown', this.HandleKeyDown); + + this.HandleKeyUp = this.HandleKeyUp.bind(this); + document.addEventListener('keyup', this.HandleKeyUp); + + // TODO: Load saved inputs from mira (robot specific) & global inputs + + for (const key in defaultInputs) { + if (Object.prototype.hasOwnProperty.call(defaultInputs, key)) { + InputSystem.allInputs[key] = defaultInputs[key]; + } + } + } + + public Update(_: number): void { + if (!document.hasFocus()) { + for (const keyCode in InputSystem._keysPressed) + delete InputSystem._keysPressed[keyCode]; + return; + } + + InputSystem._currentModifierState = { ctrl: InputSystem.isKeyPressed("ControlLeft") || InputSystem.isKeyPressed("ControlRight"), alt: InputSystem.isKeyPressed("AltLeft") || InputSystem.isKeyPressed("AltRight"), shift: InputSystem.isKeyPressed("ShiftLeft") || InputSystem.isKeyPressed("ShiftRight"), meta: InputSystem.isKeyPressed("MetaLeft") || InputSystem.isKeyPressed("MetaRight") } + } + + public Destroy(): void { + document.removeEventListener('keydown', this.HandleKeyDown); + document.removeEventListener('keyup', this.HandleKeyUp); + } + + // Called when any key is pressed + private HandleKeyDown(event: KeyboardEvent) { + InputSystem._keysPressed[event.code] = true; + } + + // Called when any key is released + 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]; + } + + // 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]; + + // Check for input modifiers + if (!this.CompareModifiers(InputSystem._currentModifierState, targetInput.modifiers)) + return false; + + return this.isKeyPressed(targetInput.keyCode); + } + + // If the input does not exist, returns false + return false; + } + + // 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 two modifier states are identical + private static CompareModifiers(state1: ModifierState, state2: ModifierState) : boolean { + return state1.alt == state2.alt && state1.ctrl == state2.ctrl && state1.meta == state2.meta && state1.shift == state2.shift; + } +} + +export default InputSystem; \ No newline at end of file diff --git a/fission/src/systems/simulation/SimulationSystem.ts b/fission/src/systems/simulation/SimulationSystem.ts index a512eadc6d..0bc789040e 100644 --- a/fission/src/systems/simulation/SimulationSystem.ts +++ b/fission/src/systems/simulation/SimulationSystem.ts @@ -58,9 +58,9 @@ class SimulationLayer { private _drivers: Driver[] private _stimuli: Stimulus[] - public get brain() { - return this._brain - } + public get brain() { return this._brain; } + public get drivers() { return this._drivers; } + public get stimuli() { return this._stimuli; } constructor(mechanism: Mechanism) { this._mechanism = mechanism @@ -101,10 +101,13 @@ class SimulationLayer { public SetBrain(brain: T | undefined) { if (this._brain) this._brain.Disable() - this._brain = brain - - if (this._brain) this._brain.Enable() + this._brain = brain; + + if (this._brain) { + this._brain.Enable(); + } } } -export default SimulationSystem +export default SimulationSystem; +export {SimulationLayer}; \ No newline at end of file diff --git a/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts new file mode 100644 index 0000000000..4d5ed5497c --- /dev/null +++ b/fission/src/systems/simulation/behavior/ArcadeDriveBehavior.ts @@ -0,0 +1,35 @@ +import WheelDriver from "../driver/WheelDriver"; +import WheelRotationStimulus from "../stimulus/WheelStimulus"; +import Behavior from "./Behavior"; +import InputSystem from "@/systems/input/InputSystem"; + +class ArcadeDriveBehavior extends Behavior { + leftWheels: WheelDriver[]; + rightWheels: WheelDriver[]; + + 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; + } + + // Sets the drivetrains target linear and rotational velocity + private DriveSpeeds(linearVelocity: number, rotationVelocity: number) { + const leftSpeed = linearVelocity + rotationVelocity; + const rightSpeed = linearVelocity - rotationVelocity; + + this.leftWheels.forEach((wheel) => wheel.targetWheelSpeed = leftSpeed); + this.rightWheels.forEach((wheel) => wheel.targetWheelSpeed = rightSpeed); + } + + public Update(_: number): void { + this.DriveSpeeds(InputSystem.GetAxis("arcadeForward", "arcadeBackward")*this._driveSpeed, + InputSystem.GetAxis("arcadeRight", "arcadeLeft")*this._turnSpeed); + } +} + +export default ArcadeDriveBehavior; \ No newline at end of file diff --git a/fission/src/systems/simulation/synthesis_brain/Behavior.ts b/fission/src/systems/simulation/behavior/Behavior.ts similarity index 90% rename from fission/src/systems/simulation/synthesis_brain/Behavior.ts rename to fission/src/systems/simulation/behavior/Behavior.ts index 5745035a0c..fcfc54cca3 100644 --- a/fission/src/systems/simulation/synthesis_brain/Behavior.ts +++ b/fission/src/systems/simulation/behavior/Behavior.ts @@ -16,6 +16,8 @@ abstract class Behavior { this._drivers = drivers this._stimuli = stimuli } + + public abstract Update(deltaT: number): void; } export default Behavior diff --git a/fission/src/systems/simulation/behavior/GenericArmBehavior.ts b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts new file mode 100644 index 0000000000..6e5a0b7c0c --- /dev/null +++ b/fission/src/systems/simulation/behavior/GenericArmBehavior.ts @@ -0,0 +1,37 @@ +import HingeDriver from "../driver/HingeDriver"; +import HingeStimulus from "../stimulus/HingeStimulus"; +import Behavior from "./Behavior"; +import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem"; + +class GenericArmBehavior extends Behavior { + private _hingeDriver: HingeDriver; + + private _positiveInput: string; + private _negativeInput: string; + + private _rotationalSpeed = 30; + + constructor(hingeDriver: HingeDriver, hingeStimulus: HingeStimulus, jointIndex: number) { + 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 } }; + } + + // Sets the arms target rotational velocity + rotateArm(rotationalVelocity: number) { + this._hingeDriver.targetVelocity = rotationalVelocity; + } + + public Update(_: number): void { + this.rotateArm(InputSystem.GetAxis(this._positiveInput, this._negativeInput)*this._rotationalSpeed); + } +} + +export default GenericArmBehavior; \ No newline at end of file diff --git a/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts new file mode 100644 index 0000000000..1190cff16a --- /dev/null +++ b/fission/src/systems/simulation/behavior/GenericElevatorBehavior.ts @@ -0,0 +1,37 @@ +import SliderDriver from "../driver/SliderDriver"; +import SliderStimulus from "../stimulus/SliderStimulus"; +import Behavior from "./Behavior"; +import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem"; + +class GenericElevatorBehavior extends Behavior { + private _sliderDriver: SliderDriver; + + private _positiveInput: string; + private _negativeInput: string; + + private _linearSpeed = 1; + + constructor(sliderDriver: SliderDriver, sliderStimulus: SliderStimulus, jointIndex: number) { + 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 } }; + } + + // Changes the elevators target position + moveElevator(positionDelta: number) { + this._sliderDriver.targetPosition += positionDelta; + } + + public Update(deltaT: number): void { + this.moveElevator(InputSystem.GetAxis(this._positiveInput, this._negativeInput)*this._linearSpeed*deltaT); + } +} + +export default GenericElevatorBehavior; \ No newline at end of file diff --git a/fission/src/systems/simulation/driver/SliderDriver.ts b/fission/src/systems/simulation/driver/SliderDriver.ts index 3fb4aa5d5a..5cd0282524 100644 --- a/fission/src/systems/simulation/driver/SliderDriver.ts +++ b/fission/src/systems/simulation/driver/SliderDriver.ts @@ -1,7 +1,8 @@ -import Jolt from "@barclah/jolt-physics" -import Driver from "./Driver" -import { SIMULATION_PERIOD } from "@/systems/physics/PhysicsSystem" -import JOLT from "@/util/loading/JoltSyncLoader" +import Jolt from "@barclah/jolt-physics"; +import Driver from "./Driver"; +import { SIMULATION_PERIOD } from "@/systems/physics/PhysicsSystem"; +import JOLT from "@/util/loading/JoltSyncLoader"; +import InputSystem from "@/systems/input/InputSystem"; class SliderDriver extends Driver { private _constraint: Jolt.SliderConstraint @@ -46,7 +47,8 @@ class SliderDriver extends Driver { } public Update(_: number): void { - this._constraint.SetTargetPosition(this._targetPosition) + this._targetPosition += ((InputSystem.getInput("sliderUp") ? 1 : 0) - (InputSystem.getInput("sliderDown") ? 1 : 0))*3; + this._constraint.SetTargetPosition(this._targetPosition); } } diff --git a/fission/src/systems/simulation/driver/WheelDriver.ts b/fission/src/systems/simulation/driver/WheelDriver.ts index c39433ff18..5758e357ba 100644 --- a/fission/src/systems/simulation/driver/WheelDriver.ts +++ b/fission/src/systems/simulation/driver/WheelDriver.ts @@ -15,22 +15,17 @@ class WheelDriver extends Driver { this._targetWheelSpeed = radsPerSec } + public get constraint(): Jolt.VehicleConstraint { return this._constraint } + public constructor(constraint: Jolt.VehicleConstraint) { super() - this._constraint = constraint - this._wheel = JOLT.castObject(this._constraint.GetWheel(0), JOLT.WheelWV) - - console.log(`Wheel X: ${constraint.GetVehicleBody().GetCenterOfMassPosition().GetX().toFixed(5)}`) - if (constraint.GetVehicleBody().GetCenterOfMassPosition().GetX() < 0) { - this._targetWheelSpeed = 10.0 - } else { - this._targetWheelSpeed = 10.0 - } + this._constraint = constraint; + this._wheel = JOLT.castObject(this._constraint.GetWheel(0), JOLT.WheelWV); } - public Update(_: number): void { - this._wheel.SetAngularVelocity(this._targetWheelSpeed) + public Update(_: number): void { + this._wheel.SetAngularVelocity(this._targetWheelSpeed); } } diff --git a/fission/src/systems/simulation/stimulus/WheelStimulus.ts b/fission/src/systems/simulation/stimulus/WheelStimulus.ts index cfb6206085..27426933bd 100644 --- a/fission/src/systems/simulation/stimulus/WheelStimulus.ts +++ b/fission/src/systems/simulation/stimulus/WheelStimulus.ts @@ -36,7 +36,7 @@ class WheelRotationStimulus extends EncoderStimulus { public Update(deltaT: number): void { if (this._accum) { - this._wheelRotationAccum += this._wheel.GetAngularVelocity() * deltaT + this._wheelRotationAccum += this._wheel.GetAngularVelocity() * deltaT; } } diff --git a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts index 5c9020ffd1..7ce89ec572 100644 --- a/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts +++ b/fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts @@ -1,19 +1,112 @@ -import Mechanism from "@/systems/physics/Mechanism" -import Brain from "../Brain" -import Behavior from "./Behavior" +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"; + class SynthesisBrain extends Brain { - public _behaviors: Behavior[] = [] + private _behaviors: Behavior[] = []; + private _simLayer: SimulationLayer; + + _leftWheelIndices: number[] = []; + + // Tracks how many joins have been made for unique controls + _currentJointIndex = 1; public constructor(mechanism: Mechanism) { - super(mechanism) + super(mechanism); + + this._simLayer = World.SimulationSystem.GetSimulationLayer(mechanism)!; + + if (!this._simLayer) { + console.log("SimulationLayer is undefined"); + return; + } + + this.ConfigureArcadeDriveBehavior(); + this.ConfigureArmBehaviors(); + this.ConfigureElevatorBehaviors(); + } + + public Enable(): void { } + + public Update(deltaT: number): void { + this._behaviors.forEach((b) => b.Update(deltaT)); } - public Update(_: number): void {} - public Enable(): void {} public Disable(): void { this._behaviors = [] } + + // 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[]; + + // 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.filter((mechConstraint) => mechConstraint.constraint instanceof JOLT.TwoBodyConstraint).map((mechConstraint) => mechConstraint.constraint as Jolt.TwoBodyConstraint); + + const leftWheels: WheelDriver[] = []; + const leftStimuli: WheelRotationStimulus[] = []; + + const rightWheels: WheelDriver[] = []; + const rightStimuli: WheelRotationStimulus[] = []; + + // Determines which wheels and stimuli belong to which side of the robot + for (let i = 0; i < wheelDrivers.length; i++) { + const wheelPos = fixedConstraints[i].GetConstraintToBody1Matrix().GetTranslation(); + + const robotCOM = World.PhysicsSystem.GetBody(this._mechanism.constraints[0].childBody).GetCenterOfMassPosition() as Jolt.Vec3; + const rightVector = new JOLT.Vec3(1, 0, 0); + + const dotProduct = rightVector.Dot(wheelPos.Sub(robotCOM)) + + if (dotProduct < 0) { + rightWheels.push(wheelDrivers[i]); + rightStimuli.push(wheelStimuli[i]); + } + else { + leftWheels.push(wheelDrivers[i]); + leftStimuli.push(wheelStimuli[i]); + } + } + + this._behaviors.push(new ArcadeDriveBehavior(leftWheels, rightWheels, leftStimuli, rightStimuli)); + } + + // 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[]; + + for (let i = 0; i < hingeDrivers.length; i++) { + this._behaviors.push(new GenericArmBehavior(hingeDrivers[i], hingeStimuli[i], this._currentJointIndex)); + 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[]; + + for (let i = 0; i < sliderDrivers.length; i++) { + this._behaviors.push(new GenericElevatorBehavior(sliderDrivers[i], sliderStimuli[i], this._currentJointIndex)); + this._currentJointIndex++; + } + } } export default SynthesisBrain diff --git a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx index e30aa163bd..80673645a3 100644 --- a/fission/src/ui/modals/configuring/ChangeInputsModal.tsx +++ b/fission/src/ui/modals/configuring/ChangeInputsModal.tsx @@ -1,65 +1,76 @@ import React, { useEffect, useState } from "react" import Modal, { ModalPropsImpl } from "@/components/Modal" 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 Stack, { StackDirection } from "../../components/Stack" +import Label, { LabelSize } from "../../components/Label" +import LabeledButton, { LabelPlacement } from "../../components/LabeledButton" +import InputSystem, { emptyModifierState } from "@/systems/input/InputSystem" -type ModifierState = { - alt?: boolean - ctrl?: boolean - shift?: boolean - meta?: boolean -} +// capitalize first letter +const transformKeyName = (control: Input) => { + 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 + " + } -type Control = { - name: string - key: string - modifiers?: ModifierState + return prefix + keyCodeToCharacter(control.keyCode); } - -const globalControls: { [key: string]: Control } = { - "Intake": { name: "Intake", key: "E" }, - "Shoot Gamepiece": { name: "Shoot Gamepiece", key: "Q" }, - "Enable God Mode": { name: "Enable God Mode", key: "G" }, + +// Converts camelCase to Title Case for the inputs modal +const toTitleCase = (camelCase: string) => { +const result = camelCase.replace(/([A-Z])/g, " $1"); +const finalResult = result.charAt(0).toUpperCase() + result.slice(1); + return finalResult; } -const robotControls: { [key: string]: Control } = { - "Arcade Forward": { name: "Arcade Forward", key: "W" }, - "Arcade Backward": { name: "Arcade Backward", key: "A" }, - "Arcade Left": { name: "Arcade Left", key: "S" }, - "Arcade Right": { name: "Arcade Right", key: "D" }, -} +const codeToCharacterMap: { [code: string]: string } = { + "Slash": "/", + "Comma": ",", + "Period": ".", + "BracketLeft": "{", + "BracketRight": "}", + "BackQuote": "`", + "Minus": "-", + "Equal": "=", + "Backslash": "\\", //TODO + "Semicolon": ";", + "Quote": "\"" +}; -// capitalize first letter -const transformKeyName = (control: Control) => { - let suffix = "" - if (control.modifiers) { - if (control.modifiers.meta) suffix += " + Meta" - if (control.modifiers.shift) suffix += " + Shift" - if (control.modifiers.ctrl) suffix += " + Ctrl" - if (control.modifiers.alt) suffix += " + Alt" - } - return control.key[0].toUpperCase() + control.key.substring(1) + suffix +// Converts a key code to displayable character (ex: KeyA -> "A") +const keyCodeToCharacter = (code: string) => { + if (code.startsWith("Key")) + return code.charAt(3); + + if (code.startsWith("Digit")) + return code.charAt(5); + + if (code in codeToCharacterMap) + return codeToCharacterMap[code]; + + return code; } const ChangeInputsModal: React.FC = ({ modalId }) => { const [loadedRobot, setLoadedRobot] = useState("") const [selectedInput, setSelectedInput] = useState("") const [chosenKey, setChosenKey] = useState("") - const [modifierState, setModifierState] = useState({}) + const [modifierState, setModifierState] = useState(emptyModifierState) useEffect(() => { - setTimeout(() => setLoadedRobot("Dozer v9"), 2_000) + setTimeout(() => setLoadedRobot("Dozer v9"), 1) }) if (selectedInput && chosenKey) { - const selected = globalControls[selectedInput] - selected.key = chosenKey + const selected = InputSystem.allInputs[selectedInput] + selected.keyCode = chosenKey selected.modifiers = modifierState setChosenKey("") setSelectedInput("") - setModifierState({}) + setModifierState(emptyModifierState) } return ( @@ -68,7 +79,7 @@ const ChangeInputsModal: React.FC = ({ modalId }) => {
{ - setChosenKey(selectedInput ? e.key : "") + setChosenKey(selectedInput ? e.code : "") setModifierState({ ctrl: e.ctrlKey, alt: e.altKey, @@ -80,9 +91,10 @@ const ChangeInputsModal: React.FC = ({ modalId }) => { {loadedRobot ? ( <> - {Object.values(robotControls).map(c => ( + {Object.values(InputSystem.robotInputs).map(c => ( { @@ -108,9 +120,10 @@ const ChangeInputsModal: React.FC = ({ modalId }) => { }} > - {Object.values(globalControls).map(c => ( + {Object.values(InputSystem.globalInputs).map(c => ( {