[Accessibility] Tweak Pause menu TTS functions (#3098)

* tweak kaleido tts

* tts announce what items are assigned to buttons; announce page on open
This commit is contained in:
Adam Bird 2023-08-13 11:41:04 -04:00 committed by GitHub
parent 05dde45a75
commit 78790fe8aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 291 additions and 75 deletions

View File

@ -1,24 +1,35 @@
{
"health": "health $0",
"magic": "magic $0",
"rupees": "rupees $0",
"0": "Deku Stick $0",
"1": "Deku Nut $0",
"2": "Bomb $0",
"3": "Fairy Bow $0",
"health": "Health - $0 Hearts",
"magic": "Magic - $0",
"rupees": "Rupees - $0",
"floor": "Floor $0",
"basement": "Basement $0",
"item_menu": "Select Item",
"map_menu": "Map - $0",
"quest_menu": "Quest Status",
"equip_menu": "Equipment",
"overworld": "Overworld",
"equipped": "$0 - Equipped",
"save_prompt": "Would you like to save?",
"game_saved": "Game saved",
"assigned_to": "Assigned to $0",
"0": "Deku Stick - $0",
"1": "Deku Nut - $0",
"2": "Bomb - $0",
"3": "Fairy Bow - $0",
"4": "Fire Arrow",
"5": "Din's Fire",
"6": "Fairy Slingshot $0",
"6": "Fairy Slingshot - $0",
"7": "Fairy Ocarina",
"8": "Ocarina of Time",
"9": "Bombchu $0",
"9": "Bombchu - $0",
"10": "Hookshot",
"11": "Longshot",
"12": "Ice Arrow",
"13": "Farore's Wind",
"14": "Boomerang",
"15": "Lens of Truth",
"16": "Magic Beans $0",
"16": "Magic Beans - $0",
"17": "Megaton Hammer",
"18": "Light Arrow",
"19": "Nayru's Love",
@ -115,8 +126,8 @@
"110": "Zora Sapphire",
"111": "Stone of Agony",
"112": "Gerudo's Card",
"113": "Skulltula Token $0",
"114": "Heart Container $0",
"113": "Skulltula Token - $0",
"114": "Piece of Heart - $0",
"115": "Piece of Heart",
"116": "Boss Key",
"117": "Compass",
@ -124,7 +135,7 @@
"119": "Small Key",
"120": "MAGIC SMALL",
"121": "MAGIC LARGE",
"122": "PIECE OF HEART 2",
"122": "Biggoron's Sword",
"123": "INVALID 1",
"124": "INVALID 2",
"125": "INVALID 3",

View File

@ -1,24 +1,35 @@
{
"health": "vie $0",
"magic": "magie $0",
"rupees": "rubis $0",
"0": "Bâton Mojo $0",
"1": "Noix Mojo $0",
"2": "Bombes $0",
"3": "Arc des Fées $0",
"health": "Vie - $0 Coeurs",
"magic": "Magie - $0",
"rupees": "Rubis - $0",
"floor": "Étage $0",
"basement": "Sous-sol $0",
"item_menu": "Inventaire",
"map_menu": "Carte - $0",
"quest_menu": "Statut de la quête",
"equip_menu": "Equipment",
"overworld": "Surmonde",
"equipped": "$0 - Équipé",
"save_prompt": "Voulez-vous sauvegarder?",
"game_saved": "Jeu sauvegardé",
"assigned_to": "Assigné au $0",
"0": "Bâton Mojo - $0",
"1": "Noix Mojo - $0",
"2": "Bombes - $0",
"3": "Arc des Fées - $0",
"4": "Flèche de Feu",
"5": "Feu de Din",
"6": "Lance-Pierre des Fées $0",
"6": "Lance-Pierre des Fées - $0",
"7": "Ocarina des Fées",
"8": "Ocarina of Temps",
"9": "Missiles Teigneux $0",
"9": "Missiles Teigneux - $0",
"10": "Grappin",
"11": "Super Grappin",
"12": "Flèche de Glace",
"13": "Vent de Farore",
"14": "Boomerang",
"15": "Monocle de Vérité",
"16": "Haricot Magique $0",
"16": "Haricot Magique - $0",
"17": "Masse des Titans",
"18": "Flèche de Lumière",
"19": "Amour de Nayru",
@ -115,8 +126,8 @@
"110": "Saphir Zora",
"111": "Pierre de Souffrance",
"112": "Carte Gerudo",
"113": "Skulltula d'or $0",
"114": "Coeur d'Énergie $0",
"113": "Skulltula d'or - $0",
"114": "Quart de Coeur - $0",
"115": "Quart de Coeur",
"116": "Clé d'or",
"117": "Boussole",
@ -124,7 +135,7 @@
"119": "Petite Clé",
"120": "PETITE BOUTEILLE DE MAGIE",
"121": "GRANDE BOUTEILLE DE MAGIE",
"122": "QUART DE COEUR 2",
"122": "Épée de Biggoron",
"123": "INVALIDE 1",
"124": "INVALIDE 2",
"125": "INVALIDE 3",

View File

@ -1,24 +1,35 @@
{
"health": "Energie $0",
"magic": "Magie $0",
"rupees": "Rubine $0",
"0": "Deku-Stab $0",
"1": "Deku-Nuß $0",
"2": "Bombe $0",
"3": "Feen-Bogen $0",
"health": "Energie - $0 Herzen",
"magic": "Magie - $0",
"rupees": "Rubine - $0",
"floor": "Etage $0",
"basement": "Keller $0",
"item_menu": "Gegenstände",
"map_menu": "Karte - $0",
"quest_menu": "Quest Status",
"equip_menu": "Ausrüstung",
"overworld": "Überwelt",
"equipped": "$0 - Ausgerüstet",
"save_prompt": "Spielstand sichern?",
"game_saved": "Spielstand gesichert",
"assigned_to": "$0 zugeordnet",
"0": "Deku-Stab - $0",
"1": "Deku-Nuß - $0",
"2": "Bombe - $0",
"3": "Feen-Bogen - $0",
"4": "Feuer-Pfeil",
"5": "Dins Feuerinferno",
"6": "Feen-Schleuder $0",
"6": "Feen-Schleuder - $0",
"7": "Feen-Okarina",
"8": "Okarina der Zeit",
"9": "Krabbelmine $0",
"9": "Krabbelmine - $0",
"10": "Fanghaken",
"11": "Enterhaken",
"12": "Eis-Pfeil",
"13": "Farores Donnersturm",
"14": "Bumerang",
"15": "Auge der Wahrheit",
"16": "Wundererbsen $0",
"16": "Wundererbsen - $0",
"17": "Stahlhammer",
"18": "Licht-Pfeil",
"19": "Nayrus Umarmung",
@ -115,8 +126,8 @@
"110": "Zora-Saphir",
"111": "Stein des Wissens",
"112": "Gerudo-Paß",
"113": "Skulltula-Symbol $0",
"114": "Herzcontainer $0",
"113": "Skulltula-Symbol - $0",
"114": "Herzteil - $0",
"115": "Herzteil",
"116": "Master-Schlüssel",
"117": "Kompaß",
@ -124,7 +135,7 @@
"119": "Kleiner Schlüssel",
"120": "MAGIE KLEIN",
"121": "MAGIE GROß",
"122": "HERZTEIL 2",
"122": "Biggoron-Schwert",
"123": "UNGÜLTIG 1",
"124": "UNGÜLTIG 2",
"125": "UNGÜLTIG 3",

View File

@ -14,5 +14,11 @@
"input_button_c_left": "C Left",
"input_button_c_right": "C Right",
"input_analog_stick": "the Analog Stick",
"input_d_pad": "the D-Pad"
"input_d_pad": "the D-Pad",
"input_d_pad_up": "D-Pad Up",
"input_d_pad_down": "D-Pad Down",
"input_d_pad_left": "D-Pad Left",
"input_d_pad_right": "D-Pad Right",
"yes": "Yes",
"no": "No"
}

View File

@ -14,5 +14,11 @@
"input_button_c_left": "C Gauche",
"input_button_c_right": "C Droit",
"input_analog_stick": "le Stick Analogique",
"input_d_pad": "D-Pad"
"input_d_pad": "D-Pad",
"input_d_pad_up": "D-Pad Haut",
"input_d_pad_down": "D-Pad Bas",
"input_d_pad_left": "D-Pad Gauche",
"input_d_pad_right": "D-Pad Droit",
"yes": "Oui",
"no": "Non"
}

View File

@ -14,5 +14,11 @@
"input_button_c_left": "C Links",
"input_button_c_right": "C Rechts",
"input_analog_stick": "den Analog-Stick",
"input_d_pad": "das Steuerkreuz"
"input_d_pad": "das Steuerkreuz",
"input_d_pad_up": "Steuerkreuz Oben",
"input_d_pad_down": "Steuerkreuz Unten",
"input_d_pad_left": "Steuerkreuz Links",
"input_d_pad_right": "Steuerkreuz Rechts",
"yes": "Ja",
"no": "Nein"
}

View File

@ -12,6 +12,7 @@
#include "soh/Enhancements/boss-rush/BossRush.h"
extern "C" {
extern MapData* gMapData;
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
@ -191,27 +192,108 @@ void RegisterOnKaleidoscopeUpdateHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnKaleidoscopeUpdate>([](int16_t inDungeonScene) {
if (!CVarGetInteger("gA11yTTS", 0)) return;
static uint16_t prevCursorIndex = 0;
static int16_t prevCursorIndex = 0;
static uint16_t prevCursorSpecialPos = 0;
static uint16_t prevCursorPoint[5] = { 0 };
static int16_t prevPromptChoice = -1;
static int16_t prevSubState = -1;
static int16_t prevState = -1;
PauseContext* pauseCtx = &gPlayState->pauseCtx;
Input* input = &gPlayState->state.input[0];
// Save game prompt
if (pauseCtx->state == 7) {
if (pauseCtx->unk_1EC == 1) {
// prompt
if (prevPromptChoice != pauseCtx->promptChoice) {
auto prompt = GetParameritizedText(pauseCtx->promptChoice == 0 ? "yes" : "no", TEXT_BANK_MISC, nullptr);
if (prevPromptChoice == -1) {
auto translation = GetParameritizedText("save_prompt", TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak((translation + " - " + prompt).c_str(), GetLanguageCode());
} else {
SpeechSynthesizer::Instance->Speak(prompt.c_str(), GetLanguageCode());
}
prevPromptChoice = pauseCtx->promptChoice;
}
} else if (pauseCtx->unk_1EC == 4 && prevSubState != 4) {
// Saved
auto translation = GetParameritizedText("game_saved", TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
}
prevSubState = pauseCtx->unk_1EC;
prevState = pauseCtx->state;
return;
}
// Announce page when
// Kaleido pages are rotating and page halfway rotated
// Or Kaleido was just opened
if ((pauseCtx->unk_1E4 == 1 && pauseCtx->unk_1EA == 32) || (pauseCtx->state == 4 && prevState != 4)) {
uint16_t modeNextPageMap[] = {
PAUSE_MAP, PAUSE_EQUIP, PAUSE_QUEST, PAUSE_ITEM, PAUSE_EQUIP, PAUSE_MAP, PAUSE_ITEM, PAUSE_QUEST,
};
uint16_t nextPage = modeNextPageMap[pauseCtx->mode];
switch (nextPage) {
case PAUSE_ITEM: {
auto translation = GetParameritizedText("item_menu", TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
break;
}
case PAUSE_MAP: {
std::string map;
if (inDungeonScene) {
std::string key = std::to_string(gSaveContext.mapIndex);
map = GetParameritizedText(key, TEXT_BANK_SCENES, nullptr);
} else {
map = GetParameritizedText("overworld", TEXT_BANK_KALEIDO, nullptr);
}
auto translation = GetParameritizedText("map_menu", TEXT_BANK_KALEIDO, map.c_str());
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
break;
}
case PAUSE_QUEST: {
auto translation = GetParameritizedText("quest_menu", TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
break;
}
case PAUSE_EQUIP: {
auto translation = GetParameritizedText("equip_menu", TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
break;
}
}
prevState = pauseCtx->state;
return;
}
prevState = pauseCtx->state;
if (pauseCtx->state != 6) {
//reset cursor index to so it is announced when pause is reopened
// Reset cursor index and values so it is announced when pause is reopened
prevCursorIndex = -1;
prevPromptChoice = -1;
prevSubState = -1;
return;
}
if ((pauseCtx->debugState != 1) && (pauseCtx->debugState != 2)) {
char arg[8];
if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) {
snprintf(arg, sizeof(arg), "%d", gSaveContext.health);
// Normalize hearts to fractional count similar to z_lifemeter
int curHeartFraction = gSaveContext.health % 16;
int fullHearts = gSaveContext.health / 16;
float fraction = ceilf((float)curHeartFraction / 5) * 0.25;
float health = (float)fullHearts + fraction;
snprintf(arg, sizeof(arg), "%g", health);
auto translation = GetParameritizedText("health", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
} else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) {
snprintf(arg, sizeof(arg), "%d", gSaveContext.magic);
} else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT) && gSaveContext.magicCapacity != 0) {
// Normalize magic to percentage
float magicLevel = ((float)gSaveContext.magic / gSaveContext.magicCapacity) * 100;
snprintf(arg, sizeof(arg), "%.0f%%", magicLevel);
auto translation = GetParameritizedText("magic", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
} else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) {
@ -236,6 +318,17 @@ void RegisterOnKaleidoscopeUpdateHook() {
return;
}
std::string buttonNames[] = {
"input_button_c_left",
"input_button_c_down",
"input_button_c_right",
"input_d_pad_up",
"input_d_pad_down",
"input_d_pad_left",
"input_d_pad_right",
};
int8_t assignedTo = -1;
switch (pauseCtx->pageIndex) {
case PAUSE_ITEM:
{
@ -247,36 +340,71 @@ void RegisterOnKaleidoscopeUpdateHook() {
case ITEM_BOMBCHU:
case ITEM_SLINGSHOT:
case ITEM_BOW:
snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM]));
break;
case ITEM_BEAN:
snprintf(arg, sizeof(arg), "%d", 0);
snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM]));
break;
default:
arg[0] = '\0';
}
if (pauseCtx->cursorItem[PAUSE_ITEM] == 999) {
if (pauseCtx->cursorItem[PAUSE_ITEM] == PAUSE_ITEM_NONE ||
pauseCtx->cursorItem[PAUSE_ITEM] == ITEM_NONE) {
prevCursorIndex = -1;
return;
}
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_ITEM]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
std::string itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg);
// Check if item is assigned to a button
for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) {
if (gSaveContext.equips.buttonItems[i + 1] == pauseCtx->cursorItem[PAUSE_ITEM]) {
assignedTo = i;
break;
}
}
if (assignedTo != -1) {
auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr);
auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str());
SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode());
} else {
SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode());
}
break;
}
case PAUSE_MAP:
if (inDungeonScene) {
// Dungeon map items
if (pauseCtx->cursorItem[PAUSE_MAP] != PAUSE_ITEM_NONE) {
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_MAP]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
} else {
// Dungeon map floor numbers
char arg[8];
int cursorPoint = pauseCtx->cursorPoint[PAUSE_MAP];
// Cursor is on a dungeon floor position
if (cursorPoint >= 3 && cursorPoint < 11) {
int floorID = gMapData->floorID[gPlayState->interfaceCtx.unk_25A][pauseCtx->dungeonMapSlot - 3];
// Normalize so F1 == 0, and negative numbers are basement levels
int normalizedFloor = (floorID * -1) + 8;
if (normalizedFloor >= 0) {
snprintf(arg, sizeof(arg), "%d", normalizedFloor + 1);
auto translation = GetParameritizedText("floor", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
} else {
snprintf(arg, sizeof(arg), "%d", normalizedFloor * -1);
auto translation = GetParameritizedText("basement", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
}
}
}
} else {
std::string key = std::to_string(0x0100 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
SPDLOG_INFO("Item: {}", key);
}
break;
case PAUSE_QUEST:
@ -287,13 +415,14 @@ void RegisterOnKaleidoscopeUpdateHook() {
snprintf(arg, sizeof(arg), "%d", gSaveContext.inventory.gsTokens);
break;
case ITEM_HEART_CONTAINER:
snprintf(arg, sizeof(arg), "%d", ((gSaveContext.inventory.questItems & 0xF) & 0xF) >> 0x1C);
snprintf(arg, sizeof(arg), "%d", (gSaveContext.inventory.questItems & 0xF0000000) >> 0x1C);
break;
default:
arg[0] = '\0';
}
if (pauseCtx->cursorItem[PAUSE_QUEST] == 999) {
if (pauseCtx->cursorItem[PAUSE_QUEST] == PAUSE_ITEM_NONE) {
prevCursorIndex = -1;
return;
}
@ -304,9 +433,45 @@ void RegisterOnKaleidoscopeUpdateHook() {
}
case PAUSE_EQUIP:
{
if (pauseCtx->namedItem == PAUSE_ITEM_NONE) {
prevCursorIndex = -1;
return;
}
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_EQUIP]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
auto itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
uint8_t checkEquipItem = pauseCtx->namedItem;
// BGS from kaleido reports as ITEM_HEART_PIECE_2 (122)
// remap BGS and broken knife to be the BGS item for the current equip check
if (checkEquipItem == ITEM_HEART_PIECE_2 || checkEquipItem == ITEM_SWORD_KNIFE) {
checkEquipItem = ITEM_SWORD_BGS;
}
// Check if equipment item is currently equipped or assigned to a button
if (checkEquipItem >= ITEM_SWORD_KOKIRI && checkEquipItem <= ITEM_BOOTS_HOVER) {
uint8_t checkEquipType = (checkEquipItem - ITEM_SWORD_KOKIRI) / 3;
uint8_t checkEquipValue = ((checkEquipItem - ITEM_SWORD_KOKIRI) % 3) + 1;
if (CUR_EQUIP_VALUE(checkEquipType) == checkEquipValue) {
itemTranslation = GetParameritizedText("equipped", TEXT_BANK_KALEIDO, itemTranslation.c_str());
}
for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) {
if (gSaveContext.equips.buttonItems[i + 1] == checkEquipItem) {
assignedTo = i;
break;
}
}
}
if (assignedTo != -1) {
auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr);
auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str());
SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode());
} else {
SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode());
}
break;
}
default: