diff --git a/src/locales/en-US/tutorials/flightTutorial.json b/src/locales/en-US/tutorials/flightTutorial.json index f00c26d29..1bfaa6f2a 100644 --- a/src/locales/en-US/tutorials/flightTutorial.json +++ b/src/locales/en-US/tutorials/flightTutorial.json @@ -7,7 +7,7 @@ "spaceShipRotationText2": "The yellow arrow on the screen is there to help you understand the orientation of the spaceship. Its opacity also indicates the rate of rotation.", "spaceShipRotationText3": "Try to get a feel for the controls by rotating the spaceship. You will get better at it with practice.", "spaceShipThrustTitle": "Spaceship Thrust", - "spaceShipThrustText1": "Assuming a QWERTY layout, you can throttle the main engines with {{keyIncrease}} and {{keyDecrease}}. Pressing {{keyKill}} will set the throttle to zero.", + "spaceShipThrustText1": "You can throttle the main engines with {{keyIncrease}} and {{keyDecrease}}. Pressing {{keyKill}} will set the throttle to zero.", "spaceShipThrustText2": "Your throttle is displayed as a vertical progress bar on the bottom right of the screen alongside your speed.", "spaceShipThrustText3": "Try flying around in the asteroid field to get familiar with the controls.", "spaceShipWarpDriveTitle": "Warp Drive", diff --git a/src/locales/fr-FR/tutorials/flightTutorial.json b/src/locales/fr-FR/tutorials/flightTutorial.json index debdb722b..5bc4b738e 100644 --- a/src/locales/fr-FR/tutorials/flightTutorial.json +++ b/src/locales/fr-FR/tutorials/flightTutorial.json @@ -7,7 +7,7 @@ "spaceShipRotationText2": "La flèche jaune à l'écran est là pour vous aider à comprendre l'orientation du vaisseau. Son opacité indique également la vitesse de rotation.", "spaceShipRotationText3": "Essayez de vous familiariser avec les commandes en faisant tourner le vaisseau. Vous vous améliorerez avec la pratique.", "spaceShipThrustTitle": "Propulsion du vaisseau", - "spaceShipThrustText1": "Sur un clavier QWERTY, vous pouvez ajuster la propulsion avec {{keyIncrease}} and {{keyDecrease}}. Presser {{keyKill}} mettra la propulsion à zéro.", + "spaceShipThrustText1": "Vous pouvez ajuster la propulsion avec {{keyIncrease}} et {{keyDecrease}}. Presser {{keyKill}} mettra la propulsion à zéro.", "spaceShipThrustText2": "Votre propulsion est affichée sous forme de barre de progression verticale en bas à droite de l'écran, à côté de votre vitesse.", "spaceShipThrustText3": "Essayez de voler dans le champ d'astéroïdes pour vous familiariser avec les commandes.", "spaceShipWarpDriveTitle": "Moteur à distorsion", diff --git a/src/ts/cosmosJourneyer.ts b/src/ts/cosmosJourneyer.ts index 0e782dff7..6b1d6f9c2 100644 --- a/src/ts/cosmosJourneyer.ts +++ b/src/ts/cosmosJourneyer.ts @@ -107,8 +107,8 @@ export class CosmosJourneyer { this.sidePanels = new SidePanels(this.starSystemView); this.mainMenu = new MainMenu(this.sidePanels, starSystemView); - this.mainMenu.onStartObservable.add(() => { - this.tutorialLayer.setTutorial(FlightTutorial.title, FlightTutorial.getContentPanelsHtml()); + this.mainMenu.onStartObservable.add(async () => { + this.tutorialLayer.setTutorial(FlightTutorial.title, await FlightTutorial.getContentPanelsHtml()); this.starSystemView.switchToSpaceshipControls(); }); @@ -117,10 +117,10 @@ export class CosmosJourneyer { await this.loadSaveData(saveData); }); - this.sidePanels.tutorialsPanelContent.onTutorialSelected.add((tutorial) => { + this.sidePanels.tutorialsPanelContent.onTutorialSelected.add(async (tutorial) => { this.mainMenu.hide(); this.resume(); - this.tutorialLayer.setTutorial(tutorial.title, tutorial.getContentPanelsHtml()); + this.tutorialLayer.setTutorial(tutorial.title, await tutorial.getContentPanelsHtml()); this.starSystemView.targetCursorLayer.setEnabled(true); this.starSystemView.getSpaceshipControls().spaceship.disableWarpDrive(); this.starSystemView.getSpaceshipControls().spaceship.setMainEngineThrottle(0); diff --git a/src/ts/spaceship/shipControls.ts b/src/ts/spaceship/shipControls.ts index aa020943c..76cdf203a 100644 --- a/src/ts/spaceship/shipControls.ts +++ b/src/ts/spaceship/shipControls.ts @@ -34,6 +34,7 @@ import { Transformable } from "../architecture/transformable"; import { ManagesLandingPads } from "../utils/managesLandingPads"; import { Sounds } from "../assets/sounds"; import { LandingPadSize } from "../assets/procedural/landingPad/landingPad"; +import { getGlobalKeyboardLayoutMap } from "../utils/keyboardAPI"; export class ShipControls implements Controls { readonly spaceship: Spaceship; @@ -66,12 +67,14 @@ export class ShipControls implements Controls { this.scene = scene; - SpaceShipControlsInputs.map.toggleWarpDrive.on("complete", () => { + SpaceShipControlsInputs.map.toggleWarpDrive.on("complete", async () => { if (!this.spaceship.canEngageWarpDrive() && this.spaceship.getWarpDrive().isDisabled()) { Sounds.CANNOT_ENGAGE_WARP_DRIVE.play(); return; } + const keyboardLayoutMap = await getGlobalKeyboardLayoutMap(); + this.spaceship.toggleWarpDrive(); if (this.spaceship.getWarpDrive().isEnabled()) { Sounds.ENGAGING_WARP_DRIVE.play(); @@ -88,7 +91,7 @@ export class ShipControls implements Controls { this.closestLandableFacility.getTransform().getAbsolutePosition() ); if (distanceToLandingFacility < 500e3) { - const bindingsString = pressInteractionToStrings(SpaceShipControlsInputs.map.emitLandingRequest).join(", "); + const bindingsString = pressInteractionToStrings(SpaceShipControlsInputs.map.emitLandingRequest, keyboardLayoutMap).join(", "); createNotification(`Don't forget to send a landing request with ${bindingsString} before approaching the facility`, 5000); } } @@ -125,13 +128,14 @@ export class ShipControls implements Controls { this.baseFov = this.thirdPersonCamera.fov; this.targetFov = this.baseFov; - this.spaceship.onLandingObservable.add(() => { + this.spaceship.onLandingObservable.add(async () => { + const keyboardLayoutMap = await getGlobalKeyboardLayoutMap(); Sounds.LANDING_COMPLETE.play(); Sounds.STRAUSS_BLUE_DANUBE.setVolume(0, 2); Sounds.STRAUSS_BLUE_DANUBE.stop(2); if (!this.spaceship.isLandedAtFacility()) { - const bindingsString = pressInteractionToStrings(StarSystemInputs.map.toggleSpaceShipCharacter).join(", "); + const bindingsString = pressInteractionToStrings(StarSystemInputs.map.toggleSpaceShipCharacter, keyboardLayoutMap).join(", "); createNotification(i18n.t("notifications:landingComplete", { bindingsString: bindingsString }), 5000); } }); diff --git a/src/ts/starSystem/starSystemView.ts b/src/ts/starSystem/starSystemView.ts index 5e8994d5a..09f596ad9 100644 --- a/src/ts/starSystem/starSystemView.ts +++ b/src/ts/starSystem/starSystemView.ts @@ -73,6 +73,7 @@ import { SpaceStationLayer } from "../ui/spaceStation/spaceStationLayer"; import { SeededStarSystemModel } from "./seededStarSystemModel"; import { placeSpaceStations } from "../society/spaceStationPlacement"; import { isSystemInHumanBubble } from "../society/starSystemSociety"; +import { getGlobalKeyboardLayoutMap } from "../utils/keyboardAPI"; /** * The star system view is the part of Cosmos Journeyer responsible to display the current star system, along with the @@ -294,10 +295,12 @@ export class StarSystemView implements View { this.targetCursorLayer.setTarget(null); }); - StarSystemInputs.map.toggleSpaceShipCharacter.on("complete", () => { + StarSystemInputs.map.toggleSpaceShipCharacter.on("complete", async () => { const characterControls = this.getCharacterControls(); const shipControls = this.getSpaceshipControls(); + const keyboardLayoutMap = await getGlobalKeyboardLayoutMap(); + if (this.scene.getActiveControls() === shipControls) { console.log("disembark"); @@ -332,7 +335,7 @@ export class StarSystemView implements View { if (!(control instanceof AxisComposite)) { throw new Error("Up down is not an axis composite"); } - createNotification(i18n.t("notifications:howToLiftOff", { bindingsString: axisCompositeToString(control)[1][1] }), 5000); + createNotification(i18n.t("notifications:howToLiftOff", { bindingsString: axisCompositeToString(control, keyboardLayoutMap)[1][1] }), 5000); } } }); diff --git a/src/ts/tutorials/flightTutorial.ts b/src/ts/tutorials/flightTutorial.ts index ea9bcce21..1ddaf3b9c 100644 --- a/src/ts/tutorials/flightTutorial.ts +++ b/src/ts/tutorials/flightTutorial.ts @@ -27,6 +27,7 @@ import rotationImageSrc from "../../asset/tutorials/flightTutorial/rotation.webp import thrustImageSrc from "../../asset/tutorials/flightTutorial/thrust.webp"; import warpImageSrc from "../../asset/tutorials/flightTutorial/warp.webp"; import congratsImageSrc from "../../asset/tutorials/flightTutorial/congrats.webp"; +import { getGlobalKeyboardLayoutMap } from "../utils/keyboardAPI"; export const FlightTutorial: Tutorial = { title: i18n.t("tutorials:flightTutorial:title"), @@ -43,15 +44,16 @@ export const FlightTutorial: Tutorial = { orbitalObjectIndex: 2 }, - getContentPanelsHtml(): string[] { + async getContentPanelsHtml(): Promise { + const keybordLayoutMap = await getGlobalKeyboardLayoutMap(); const welcomePanelHtml = `
Welcome to Cosmos Journeyer

${i18n.t("tutorials:flightTutorial:welcome")}

${i18n.t("tutorials:common:navigationInfo", { - nextKeys: pressInteractionToStrings(TutorialControlsInputs.map.nextPanel).join(` ${i18n.t("common:or")} `), - previousKeys: pressInteractionToStrings(TutorialControlsInputs.map.prevPanel).join(` ${i18n.t("common:or")} `), - quitKeys: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial).join(` ${i18n.t("common:or")} `) + nextKeys: pressInteractionToStrings(TutorialControlsInputs.map.nextPanel, keybordLayoutMap).join(` ${i18n.t("common:or")} `), + previousKeys: pressInteractionToStrings(TutorialControlsInputs.map.prevPanel, keybordLayoutMap).join(` ${i18n.t("common:or")} `), + quitKeys: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial, keybordLayoutMap).join(` ${i18n.t("common:or")} `) })}
`; @@ -68,7 +70,7 @@ export const FlightTutorial: Tutorial = { if (!(control instanceof AxisComposite)) { throw new Error("Expected control to be an AxisComposite"); } - const throttleStrings = axisCompositeToString(control); + const throttleStrings = axisCompositeToString(control, keybordLayoutMap); const thrustPanelHtml = `
@@ -76,7 +78,7 @@ export const FlightTutorial: Tutorial = {

${i18n.t("tutorials:flightTutorial:spaceShipThrustText1", { keyIncrease: throttleStrings[1][1], keyDecrease: throttleStrings[0][1], - keyKill: pressInteractionToStrings(SpaceShipControlsInputs.map.throttleToZero).join(` ${i18n.t("common:or")} `) + keyKill: pressInteractionToStrings(SpaceShipControlsInputs.map.throttleToZero, keybordLayoutMap).join(` ${i18n.t("common:or")} `) })}

Spaceship Thrust

${i18n.t("tutorials:flightTutorial:spaceShipThrustText2")}

@@ -87,7 +89,7 @@ export const FlightTutorial: Tutorial = {

${i18n.t("tutorials:flightTutorial:spaceShipWarpDriveTitle")}

${i18n.t("tutorials:flightTutorial:spaceShipWarpDriveText1")}

-

${i18n.t("tutorials:flightTutorial:spaceShipWarpDriveText2", { keyToggle: pressInteractionToStrings(SpaceShipControlsInputs.map.toggleWarpDrive).join(` ${i18n.t("common:or")} `) })}

+

${i18n.t("tutorials:flightTutorial:spaceShipWarpDriveText2", { keyToggle: pressInteractionToStrings(SpaceShipControlsInputs.map.toggleWarpDrive, keybordLayoutMap).join(` ${i18n.t("common:or")} `) })}

${i18n.t("tutorials:flightTutorial:spaceShipWarpDriveText3")}

Warp Drive
`; @@ -98,7 +100,7 @@ export const FlightTutorial: Tutorial = {

${i18n.t("tutorials:flightTutorial:congratulationsText1")}

${i18n.t("tutorials:common:tutorialEnding", { - keyQuit: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial).join(` ${i18n.t("common:or")} `) + keyQuit: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial, keybordLayoutMap).join(` ${i18n.t("common:or")} `) })}
`; diff --git a/src/ts/tutorials/templateTutorial.ts b/src/ts/tutorials/templateTutorial.ts index efa27f86f..074652580 100644 --- a/src/ts/tutorials/templateTutorial.ts +++ b/src/ts/tutorials/templateTutorial.ts @@ -4,12 +4,14 @@ import { Tutorial } from "./tutorial"; import welcomeImageSrc from "../../asset/tutorials/flightTutorial/welcome.webp"; import i18n from "../i18n"; +import { getGlobalKeyboardLayoutMap } from "../utils/keyboardAPI"; export const TemplateTutorial: Tutorial = { title: "Template Tutorial", coverImageSrc: welcomeImageSrc, description: "This is a template tutorial to help building more tutorials!", - getContentPanelsHtml(): string[] { + async getContentPanelsHtml(): Promise { + const keyboardLayoutMap = await getGlobalKeyboardLayoutMap(); const welcomePanelHtml = `
Welcome to Cosmos Journeyer @@ -17,9 +19,9 @@ export const TemplateTutorial: Tutorial = { ${i18n.t("tutorials:common:navigationInfo", { // This displays a small internationalized text to explain the keys to navigate the tutorial - nextKeys: pressInteractionToStrings(TutorialControlsInputs.map.nextPanel).join(` ${i18n.t("common:or")} `), - previousKeys: pressInteractionToStrings(TutorialControlsInputs.map.prevPanel).join(` ${i18n.t("common:or")} `), - quitKeys: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial).join(` ${i18n.t("common:or")} `) + nextKeys: pressInteractionToStrings(TutorialControlsInputs.map.nextPanel, keyboardLayoutMap).join(` ${i18n.t("common:or")} `), + previousKeys: pressInteractionToStrings(TutorialControlsInputs.map.prevPanel, keyboardLayoutMap).join(` ${i18n.t("common:or")} `), + quitKeys: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial, keyboardLayoutMap).join(` ${i18n.t("common:or")} `) })}
`; @@ -27,7 +29,7 @@ export const TemplateTutorial: Tutorial = {
${i18n.t("tutorials:common:tutorialEnding", { // This displays a small internationalized text to explain the keys to end the tutorial - keyQuit: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial).join(` ${i18n.t("common:or")} `) + keyQuit: pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial, keyboardLayoutMap).join(` ${i18n.t("common:or")} `) })}
`; diff --git a/src/ts/tutorials/tutorial.ts b/src/ts/tutorials/tutorial.ts index d033e3bf8..99243b061 100644 --- a/src/ts/tutorials/tutorial.ts +++ b/src/ts/tutorials/tutorial.ts @@ -24,5 +24,5 @@ export interface Tutorial { universeObjectIdentifier?: UniverseObjectIdentifier; - getContentPanelsHtml(): string[]; + getContentPanelsHtml(): Promise; } diff --git a/src/ts/ui/settingsPanel.ts b/src/ts/ui/settingsPanel.ts index 3cef10122..7a872b158 100644 --- a/src/ts/ui/settingsPanel.ts +++ b/src/ts/ui/settingsPanel.ts @@ -4,129 +4,132 @@ import Interaction from "@brianchirls/game-input/interactions/Interaction"; import DPadComposite from "@brianchirls/game-input/controls/DPadComposite"; import { axisCompositeToString, buttonInputToString, dPadCompositeToString, stickInputToString, vector2ToString } from "../utils/inputControlsString"; import { AxisComposite, ButtonInputControl, StickInputControl, Vector2InputControl } from "@brianchirls/game-input/browser"; +import { getGlobalKeyboardLayoutMap } from "../utils/keyboardAPI"; export function initSettingsPanel(): HTMLElement { const settingsPanel = document.getElementById("settingsPanel"); if (settingsPanel === null) throw new Error("#settings does not exist!"); - InputMaps.forEach((inputMap) => { - // create a div - // the name of the map will be an h3 - // each action will be a div with the name of the action and the bindings - const mapDiv = document.createElement("div"); - mapDiv.classList.add("map"); - const mapName = document.createElement("h3"); - // break camelCase with a space - mapName.textContent = inputMap.name.replace(/([A-Z])/g, " $1").trim(); - - mapDiv.appendChild(mapName); - - for (const [actionName, action] of Object.entries(inputMap.map)) { - const subActionMap: Map = new Map(); - - const actionOrInteraction = action as Action | Interaction; - const bindings = actionOrInteraction instanceof Action ? actionOrInteraction.bindings : actionOrInteraction.action.bindings; - bindings.forEach((binding) => { - if (binding.control instanceof DPadComposite) { - const strings = dPadCompositeToString(binding.control); - strings.forEach((string) => { - const [key, name] = string; - if (!subActionMap.has(key)) { - subActionMap.set(key, []); + getGlobalKeyboardLayoutMap().then((keyboardLayoutMap) => { + InputMaps.forEach((inputMap) => { + // create a div + // the name of the map will be an h3 + // each action will be a div with the name of the action and the bindings + const mapDiv = document.createElement("div"); + mapDiv.classList.add("map"); + const mapName = document.createElement("h3"); + // break camelCase with a space + mapName.textContent = inputMap.name.replace(/([A-Z])/g, " $1").trim(); + + mapDiv.appendChild(mapName); + + for (const [actionName, action] of Object.entries(inputMap.map)) { + const subActionMap: Map = new Map(); + + const actionOrInteraction = action as Action | Interaction; + const bindings = actionOrInteraction instanceof Action ? actionOrInteraction.bindings : actionOrInteraction.action.bindings; + bindings.forEach((binding) => { + if (binding.control instanceof DPadComposite) { + const strings = dPadCompositeToString(binding.control, keyboardLayoutMap); + strings.forEach((string) => { + const [key, name] = string; + if (!subActionMap.has(key)) { + subActionMap.set(key, []); + } + subActionMap.get(key)?.push(name); + }); + } else if (binding.control instanceof ButtonInputControl) { + const text = buttonInputToString(binding.control, keyboardLayoutMap); + if (!subActionMap.has("BUTTON")) { + subActionMap.set("BUTTON", []); } - subActionMap.get(key)?.push(name); - }); - } else if (binding.control instanceof ButtonInputControl) { - const text = buttonInputToString(binding.control); - if (!subActionMap.has("BUTTON")) { - subActionMap.set("BUTTON", []); + subActionMap.get("BUTTON")?.push(text); + } else if (binding.control instanceof AxisComposite) { + const strings = axisCompositeToString(binding.control, keyboardLayoutMap); + strings.forEach((string) => { + const [key, name] = string; + if (!subActionMap.has(key)) { + subActionMap.set(key, []); + } + subActionMap.get(key)?.push(name); + }); + } else if (binding.control instanceof StickInputControl) { + const strings = stickInputToString(binding.control); + strings.forEach((string) => { + const [key, name] = string; + if (!subActionMap.has(key)) { + subActionMap.set(key, []); + } + subActionMap.get(key)?.push(name); + }); + } else if (binding.control instanceof Vector2InputControl) { + const strings = vector2ToString(binding.control); + strings.forEach((string) => { + const [key, name] = string; + if (!subActionMap.has(key)) { + subActionMap.set(key, []); + } + subActionMap.get(key)?.push(name); + }); + } else { + throw new Error("Unknown control type"); } - subActionMap.get("BUTTON")?.push(text); - } else if (binding.control instanceof AxisComposite) { - const strings = axisCompositeToString(binding.control); - strings.forEach((string) => { - const [key, name] = string; - if (!subActionMap.has(key)) { - subActionMap.set(key, []); - } - subActionMap.get(key)?.push(name); - }); - } else if (binding.control instanceof StickInputControl) { - const strings = stickInputToString(binding.control); - strings.forEach((string) => { - const [key, name] = string; - if (!subActionMap.has(key)) { - subActionMap.set(key, []); - } - subActionMap.get(key)?.push(name); - }); - } else if (binding.control instanceof Vector2InputControl) { - const strings = vector2ToString(binding.control); - strings.forEach((string) => { - const [key, name] = string; - if (!subActionMap.has(key)) { - subActionMap.set(key, []); - } - subActionMap.get(key)?.push(name); - }); - } else { - throw new Error("Unknown control type"); - } - }); + }); - const actionDiv = document.createElement("div"); + const actionDiv = document.createElement("div"); - const label = document.createElement("p"); - // break camelCase with a space - label.textContent = actionName.replace(/([A-Z])/g, " $1").trim(); + const label = document.createElement("p"); + // break camelCase with a space + label.textContent = actionName.replace(/([A-Z])/g, " $1").trim(); - actionDiv.appendChild(label); + actionDiv.appendChild(label); - if (subActionMap.size === 1) { - actionDiv.classList.add("actionSingle"); + if (subActionMap.size === 1) { + actionDiv.classList.add("actionSingle"); - const valuesContainer = document.createElement("div"); - valuesContainer.classList.add("valuesContainer"); + const valuesContainer = document.createElement("div"); + valuesContainer.classList.add("valuesContainer"); - subActionMap.forEach((value, key) => { - value.forEach((v) => { - const valueContainer = document.createElement("p"); - valueContainer.innerText = v; - valuesContainer.appendChild(valueContainer); + subActionMap.forEach((value, key) => { + value.forEach((v) => { + const valueContainer = document.createElement("p"); + valueContainer.innerText = v; + valuesContainer.appendChild(valueContainer); + }); }); - }); - actionDiv.appendChild(valuesContainer); - } else { - actionDiv.classList.add("actionMultiple"); + actionDiv.appendChild(valuesContainer); + } else { + actionDiv.classList.add("actionMultiple"); - subActionMap.forEach((value, key) => { - const subActionDiv = document.createElement("div"); - subActionDiv.classList.add("subAction"); + subActionMap.forEach((value, key) => { + const subActionDiv = document.createElement("div"); + subActionDiv.classList.add("subAction"); - const subActionLabel = document.createElement("p"); - subActionLabel.textContent = key; + const subActionLabel = document.createElement("p"); + subActionLabel.textContent = key; - const valuesContainer = document.createElement("div"); - valuesContainer.classList.add("valuesContainer"); + const valuesContainer = document.createElement("div"); + valuesContainer.classList.add("valuesContainer"); - value.forEach((v) => { - const valueContainer = document.createElement("p"); - valueContainer.innerText = v; - valuesContainer.appendChild(valueContainer); - }); + value.forEach((v) => { + const valueContainer = document.createElement("p"); + valueContainer.innerText = v; + valuesContainer.appendChild(valueContainer); + }); - subActionDiv.appendChild(subActionLabel); - subActionDiv.appendChild(valuesContainer); + subActionDiv.appendChild(subActionLabel); + subActionDiv.appendChild(valuesContainer); - actionDiv.appendChild(subActionDiv); - }); - } + actionDiv.appendChild(subActionDiv); + }); + } - mapDiv.appendChild(actionDiv); - } + mapDiv.appendChild(actionDiv); + } - settingsPanel.appendChild(mapDiv); + settingsPanel.appendChild(mapDiv); + }); }); return settingsPanel; diff --git a/src/ts/ui/tutorial/tutorialLayer.ts b/src/ts/ui/tutorial/tutorialLayer.ts index d8ff5a06c..6aeef05fa 100644 --- a/src/ts/ui/tutorial/tutorialLayer.ts +++ b/src/ts/ui/tutorial/tutorialLayer.ts @@ -49,7 +49,7 @@ export class TutorialLayer implements IDisposable { stopButtonTextSpan.innerText = i18n.t("tutorials:common:quit"); this.quitButton.appendChild(stopButtonTextSpan); - pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial).forEach((key) => { + pressInteractionToStrings(TutorialControlsInputs.map.quitTutorial, null).forEach((key) => { const stopKeySpan = document.createElement("span"); stopKeySpan.classList.add("keySpan"); stopKeySpan.innerText = key; @@ -61,7 +61,7 @@ export class TutorialLayer implements IDisposable { prevButtonTextSpan.innerText = i18n.t("tutorials:common:previous"); this.prevButton.appendChild(prevButtonTextSpan); - pressInteractionToStrings(TutorialControlsInputs.map.prevPanel).forEach((key) => { + pressInteractionToStrings(TutorialControlsInputs.map.prevPanel, null).forEach((key) => { const prevKeySpan = document.createElement("span"); prevKeySpan.classList.add("keySpan"); prevKeySpan.innerText = key; @@ -73,7 +73,7 @@ export class TutorialLayer implements IDisposable { nextButtonTextSpan.innerText = i18n.t("tutorials:common:next"); this.nextButton.appendChild(nextButtonTextSpan); - pressInteractionToStrings(TutorialControlsInputs.map.nextPanel).forEach((key) => { + pressInteractionToStrings(TutorialControlsInputs.map.nextPanel, null).forEach((key) => { const nextKeySpan = document.createElement("span"); nextKeySpan.classList.add("keySpan"); nextKeySpan.innerText = key; diff --git a/src/ts/utils/inputControlsString.ts b/src/ts/utils/inputControlsString.ts index c97448014..a288262b6 100644 --- a/src/ts/utils/inputControlsString.ts +++ b/src/ts/utils/inputControlsString.ts @@ -6,18 +6,21 @@ export function stickInputToString(input: StickInputControl): [string, string][] const keys: [string, string][] = []; input.children.forEach((child, key) => { if (key === "x" || key === "y") return; - keys.push([key, input.name]); + else keys.push([key, input.name]); }); return keys; } -export function dPadCompositeToString(input: DPadComposite): [string, string][] { +export function dPadCompositeToString(input: DPadComposite, keyboardMap: Map | null): [string, string][] { const keys: [string, string][] = []; input.children.forEach((child, key) => { if (key === "x" || key === "y") return; let name = child.name; // remove the "key:" prefix name = name.replace("key:", ""); + if(keyboardMap?.has(name)) { + name = keyboardMap.get(name)?.toUpperCase() ?? name; + } keys.push([key, name]); }); return keys; @@ -30,26 +33,31 @@ export function vector2ToString(input: Vector2InputControl): [string, string][] ]; } -export function buttonInputToString(input: ButtonInputControl): string { +export function buttonInputToString(input: ButtonInputControl, keyboardMap: Map | null): string { let name = input.name; // remove the "key:" prefix name = name.replace("key:", ""); + if(keyboardMap?.has(name)) { + name = keyboardMap.get(name)?.toUpperCase() ?? name; + } return name; } -export function axisCompositeToString(input: AxisComposite): [string, string][] { +export function axisCompositeToString(input: AxisComposite, keyboardMap: Map | null): [string, string][] { const keys: [string, string][] = []; input.children.forEach((child, key) => { let name = child.name; - // remove the "key:" prefix name = name.replace("key:", ""); + if(keyboardMap?.has(name)) { + name = keyboardMap.get(name)?.toUpperCase() ?? name; + } keys.push([key, name]); }); return keys; } -export function pressInteractionToStrings(pressInteraction: PressInteraction): string[] { +export function pressInteractionToStrings(pressInteraction: PressInteraction, keyboardMap: Map | null): string[] { const bindings = pressInteraction.action.bindings; - return bindings.map((binding) => buttonInputToString(binding.control as ButtonInputControl)); + return bindings.map((binding) => buttonInputToString(binding.control as ButtonInputControl, keyboardMap)); } diff --git a/src/ts/utils/keyboardAPI.ts b/src/ts/utils/keyboardAPI.ts new file mode 100644 index 000000000..e66f74b02 --- /dev/null +++ b/src/ts/utils/keyboardAPI.ts @@ -0,0 +1,15 @@ +declare global { + interface Navigator { + keyboard: { + getLayoutMap: () => Promise>; + }; + } +} + +export function getGlobalKeyboardLayoutMap(): Promise> { + if (navigator.keyboard) { + return navigator.keyboard.getLayoutMap(); + } + console.warn("navigator.keyboard is not available, returning an empty map"); + return Promise.resolve(new Map()); +}