diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index ecd970b31..e70c0d33e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -22,6 +22,7 @@ DEFINE_HOOK(OnFlagSet, (int16_t flagType, int16_t flag)); DEFINE_HOOK(OnFlagUnset, (int16_t flagType, int16_t flag)); DEFINE_HOOK(OnSceneSpawnActors, ()); DEFINE_HOOK(OnPlayerUpdate, ()); +DEFINE_HOOK(OnSetDoAction, (uint16_t action)); DEFINE_HOOK(OnOcarinaSongAction, ()); DEFINE_HOOK(OnCuccoOrChickenHatch, ()); DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 5e52bed71..08f2660dd 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -85,6 +85,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnSetDoAction(uint16_t action) { + GameInteractor::Instance->ExecuteHooks(action); +} + void GameInteractor_ExecuteOnOcarinaSongAction() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index b1f9195b4..cd8e7962e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -25,6 +25,7 @@ void GameInteractor_ExecuteOnFlagSet(int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneSpawnActors(); void GameInteractor_ExecuteOnPlayerUpdate(); +void GameInteractor_ExecuteOnSetDoAction(uint16_t action); void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnCuccoOrChickenHatch(); void GameInteractor_ExecuteOnActorInit(void* actor); diff --git a/soh/soh/Enhancements/tts/tts.cpp b/soh/soh/Enhancements/tts/tts.cpp index 77df9983d..efc8a3808 100644 --- a/soh/soh/Enhancements/tts/tts.cpp +++ b/soh/soh/Enhancements/tts/tts.cpp @@ -1,6 +1,5 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h" - #include #include #include @@ -11,6 +10,8 @@ #include "soh/OTRGlobals.h" #include "message_data_static.h" #include "overlays/gamestates/ovl_file_choose/file_choose.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "soh/ActorDB.h" #include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/resource/type/SohResourceType.h" @@ -178,6 +179,71 @@ void RegisterOnInterfaceUpdateHook() { if (!GameInteractor::IsSaveLoaded(true)) return; + if (CHECK_BTN_ALL(gPlayState->state.input->press.button, BTN_CUSTOM_MODIFIER1)) { + Player* player = GET_PLAYER(gPlayState); + if (player != NULL) { + u16 angle = (u16)player->actor.world.rot.y; + const char* ttsAnnounce = (angle > 0xf000 || angle < 0x1000) ? "south" + : angle < 0x3000 ? "southeast" + : angle < 0x5000 ? "east" + : angle < 0x7000 ? "northeast" + : angle < 0x9000 ? "north" + : angle < 0xB000 ? "northwest" + : angle < 0xD000 ? "west" + : "southwest"; + SpeechSynthesizer::Instance->Speak(ttsAnnounce, "en-US"); + } + } + + if (CHECK_BTN_ALL(gPlayState->state.input->press.button, BTN_CUSTOM_MODIFIER2)) { + int minDist = 1000000; + Actor* readOut = NULL; + Player* player = GET_PLAYER(gPlayState); + if (player != NULL) { + if (player->focusActor != NULL) { + readOut = player->focusActor; + } else { + for (int i = 0; i < ACTORCAT_MAX; i++) { + if (i == ACTORCAT_PLAYER) + continue; + for (Actor* actor = gPlayState->actorCtx.actorLists[i].head; actor != NULL; + actor = actor->next) { + if ((actor->id == ACTOR_EN_ELF && actor->params == FAIRY_NAVI) || + actor->id == ACTOR_SHOT_SUN) + continue; + if (actor->xzDistToPlayer < 40 && (player->stateFlags1 & PLAYER_STATE1_IN_WATER) && + player->currentBoots != PLAYER_BOOTS_IRON && + actor->world.pos.z < player->actor.world.pos.z) { + if (actor->id == ACTOR_EN_ITEM00) { + SpeechSynthesizer::Instance->Speak("item below", "en-US"); + goto spoke; + } else if (actor->id == ACTOR_OBJ_SWITCH) { + SpeechSynthesizer::Instance->Speak("switch below", "en-US"); + goto spoke; + } + } + u16 reverseYaw = (actor->yawTowardsPlayer + 0x8000) - player->actor.world.rot.y; + if ((reverseYaw < 0x2000 || reverseYaw > 0xE000) && actor->xyzDistToPlayerSq < minDist) { + readOut = actor; + minDist = actor->xyzDistToPlayerSq; + } + } + } + } + if (readOut != NULL) { + auto entry = ActorDB::Instance->RetrieveEntry(readOut->id); + if (entry.name.empty()) { + char ttsAnnounceBuf[8]; + int annouceBuf = snprintf(ttsAnnounceBuf, sizeof(ttsAnnounceBuf), "%d", readOut->id); + SpeechSynthesizer::Instance->Speak(ttsAnnounceBuf, "en-US"); + } else { + SpeechSynthesizer::Instance->Speak(entry.name.c_str(), "en-US"); + } + } + } + } + spoke: + static int16_t lostHealth = 0; static int16_t prevHealth = 0; @@ -1148,6 +1214,46 @@ void RegisterOnSetGameLanguageHook() { GameInteractor::Instance->RegisterGameHook([]() { InitTTSBank(); }); } +void RegisterOnSetDoAction() { + GameInteractor::Instance->RegisterGameHook([](uint16_t action) { + if (CVarGetInteger(CVAR_SETTING("A11yTTS"), 0) && action < DO_ACTION_1) { + const char* text; + switch (action) { + case DO_ACTION_CHECK: + text = "act check"; + break; + case DO_ACTION_ENTER: + text = "act enter"; + break; + case DO_ACTION_OPEN: + text = "act open"; + break; + case DO_ACTION_THROW: + text = "act throw"; + break; + case DO_ACTION_CLIMB: + text = "act climb"; + break; + case DO_ACTION_DROP: + text = "act drop"; + break; + case DO_ACTION_SPEAK: + text = "act speak"; + break; + case DO_ACTION_GRAB: + text = "act grab"; + break; + case DO_ACTION_PUTAWAY: + text = "act putaway"; + break; + default: + return; + } + SpeechSynthesizer::Instance->Speak(text, "en-US"); + } + }); +} + void RegisterTTSModHooks() { RegisterOnSetGameLanguageHook(); RegisterOnDialogMessageHook(); @@ -1156,6 +1262,7 @@ void RegisterTTSModHooks() { RegisterOnInterfaceUpdateHook(); RegisterOnKaleidoscopeUpdateHook(); RegisterOnUpdateMainMenuSelection(); + RegisterOnSetDoAction(); } void RegisterTTS() { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 72030657e..68018b052 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1420,13 +1420,11 @@ extern "C" void Graph_StartFrame() { break; } -#if defined(_WIN32) || defined(__APPLE__) case KbScancode::LUS_KB_F9: { // Toggle TTS CVarSetInteger(CVAR_SETTING("A11yTTS"), !CVarGetInteger(CVAR_SETTING("A11yTTS"), 0)); break; } -#endif case KbScancode::LUS_KB_TAB: { CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0)); break; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 4ead0d268..3423855b6 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2791,6 +2791,7 @@ void Interface_SetDoAction(PlayState* play, u16 action) { PauseContext* pauseCtx = &play->pauseCtx; if (interfaceCtx->unk_1F0 != action) { + GameInteractor_ExecuteOnSetDoAction(action); interfaceCtx->unk_1F0 = action; interfaceCtx->unk_1EC = 1; interfaceCtx->unk_1F4 = 0.0f;