|
1 | 1 | import { random } from "@/util/Random" |
2 | 2 | import PreferencesSystem from "../preferences/PreferencesSystem" |
3 | 3 | import DefaultInputs from "./DefaultInputs" |
4 | | -import InputSystem, { AxisInput, ButtonInput, Input } from "./InputSystem" |
| 4 | +import InputSystem, { AxisInput, ButtonInput, Input, KeyDescriptor } from "./InputSystem" |
| 5 | +import { DriveType } from "@/systems/simulation/behavior/Behavior.ts" |
| 6 | +import SynthesisBrain from "@/systems/simulation/synthesis_brain/SynthesisBrain.ts" |
5 | 7 |
|
6 | 8 | export type InputScheme = { |
7 | 9 | schemeName: string |
8 | 10 | descriptiveName: string |
9 | 11 | customized: boolean |
10 | 12 | usesGamepad: boolean |
11 | 13 | usesTouchControls: boolean |
| 14 | + supportedDrivetrains: DriveType[] |
12 | 15 | inputs: Input[] |
13 | 16 | } |
14 | 17 |
|
| 18 | +export enum InputSchemeUseType { |
| 19 | + IN_USE, // bound to a robot |
| 20 | + CONFLICT, // has keys overlapping with a bound scheme |
| 21 | + AVAILABLE, // no overlap and not bound |
| 22 | +} |
| 23 | + |
| 24 | +export type InputSchemeAvailability = { scheme: InputScheme; status: InputSchemeUseType; conflicts_with_names?: string } |
| 25 | + |
15 | 26 | class InputSchemeManager { |
16 | 27 | // References to the current custom schemes to avoid parsing every time they are requested |
17 | 28 | private static _customSchemes: InputScheme[] | undefined |
@@ -85,31 +96,72 @@ class InputSchemeManager { |
85 | 96 |
|
86 | 97 | // Add default schemes if they have not been customized |
87 | 98 | this.defaultInputSchemes.forEach(defaultScheme => { |
88 | | - if ( |
89 | | - allSchemes.some(s => { |
90 | | - return s.schemeName === defaultScheme.schemeName |
91 | | - }) |
92 | | - ) |
93 | | - return |
94 | | - |
| 99 | + if (allSchemes.some(s => s.schemeName === defaultScheme.schemeName)) return |
95 | 100 | allSchemes.push(defaultScheme) |
96 | 101 | }) |
97 | 102 |
|
98 | 103 | return allSchemes |
99 | 104 | } |
100 | 105 |
|
101 | 106 | /** Creates an array of every input scheme that is not currently in use by a robot */ |
102 | | - public static get availableInputSchemes(): InputScheme[] { |
| 107 | + private static get _availableInputSchemes(): InputSchemeAvailability[] { |
103 | 108 | const allSchemes = this.allInputSchemes |
104 | 109 |
|
105 | | - // Remove schemes that are in use |
106 | | - const schemesInUse = Array.from(InputSystem.brainIndexSchemeMap.values()) |
107 | | - return allSchemes.filter(scheme => !schemesInUse.includes(scheme)) |
| 110 | + // Remove schemes that have conflicts |
| 111 | + const usedKeyMap = new Map<KeyDescriptor, string[]>() |
| 112 | + const result: Record<string, InputSchemeAvailability> = {} |
| 113 | + for (const scheme of InputSystem.brainIndexSchemeMap.values()) { |
| 114 | + result[scheme.schemeName] = { scheme, status: InputSchemeUseType.IN_USE } |
| 115 | + scheme?.inputs?.forEach(input => { |
| 116 | + input.keysUsed |
| 117 | + .filter(key => key != null) |
| 118 | + .forEach(key => { |
| 119 | + const entry = usedKeyMap.get(key) |
| 120 | + if (entry != null) { |
| 121 | + entry.push(scheme.schemeName) |
| 122 | + } else { |
| 123 | + usedKeyMap.set(key, [scheme.schemeName]) |
| 124 | + } |
| 125 | + }) |
| 126 | + }) |
| 127 | + } |
| 128 | + |
| 129 | + allSchemes.forEach(scheme => { |
| 130 | + const conflictingSchemes = scheme.inputs.flatMap(input => |
| 131 | + input.keysUsed.flatMap(key => usedKeyMap.get(key) ?? []) |
| 132 | + ) |
| 133 | + console.log(conflictingSchemes) |
| 134 | + if (conflictingSchemes.length > 0) { |
| 135 | + result[scheme.schemeName] ??= { |
| 136 | + scheme, |
| 137 | + status: InputSchemeUseType.CONFLICT, |
| 138 | + conflicts_with_names: [...new Set(conflictingSchemes)].join(", "), |
| 139 | + } |
| 140 | + } else { |
| 141 | + result[scheme.schemeName] = { scheme, status: InputSchemeUseType.AVAILABLE } |
| 142 | + } |
| 143 | + }) |
| 144 | + return Object.values(result) |
| 145 | + } |
| 146 | + |
| 147 | + /** Creates an array of every input scheme that is not currently in use by a robot */ |
| 148 | + public static availableInputSchemesByType(driveType?: DriveType): InputSchemeAvailability[] { |
| 149 | + const allSchemes = this._availableInputSchemes |
| 150 | + if (driveType == null) { |
| 151 | + return allSchemes |
| 152 | + } |
| 153 | + return allSchemes.filter(entry => entry.scheme.supportedDrivetrains.includes(driveType)) |
| 154 | + } |
| 155 | + |
| 156 | + /** Creates an array of every input scheme that is not currently in use by a robot */ |
| 157 | + public static availableInputSchemesByBrain(brainIndex: number): InputSchemeAvailability[] { |
| 158 | + const driveType = SynthesisBrain.brainIndexMap.get(brainIndex)?.driveType |
| 159 | + return this.availableInputSchemesByType(driveType) |
108 | 160 | } |
109 | 161 |
|
110 | 162 | /** @returns a random available robot name */ |
111 | 163 | public static get randomAvailableName(): string { |
112 | | - const usedNames = this.availableInputSchemes.map(s => s.schemeName) |
| 164 | + const usedNames = this.allInputSchemes.map(s => s.schemeName) |
113 | 165 |
|
114 | 166 | const randomName = () => { |
115 | 167 | const index = Math.floor(random() * DefaultInputs.NAMES.length) |
|
0 commit comments