diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 24fda1ade..d52644c8b 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -23,6 +23,7 @@ #include "SohHooks.h" #include "SohConsole.h" #include +#include extern "C" { struct OSMesgQueue; @@ -232,6 +233,8 @@ extern "C" { extern "C" GfxWindowManagerAPI gfx_sdl; void SetWindowManager(GfxWindowManagerAPI** WmApi, GfxRenderingAPI** RenderingApi, const std::string& gfx_backend); +ISpVoice* pVoice = NULL; + namespace Ship { std::map>> Window::Controllers; int32_t Window::lastScancode; @@ -262,11 +265,27 @@ namespace Ship { const std::string& gfx_backend = Conf["WINDOW"]["GFX BACKEND"]; SetWindowManager(&WmApi, &RenderingApi, gfx_backend); + //Initialize Text To Speech + HRESULT hr; + HRESULT a = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + HRESULT CoInitializeEx(LPVOID pvReserved, DWORD dwCoInit); + hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice); + gfx_init(WmApi, RenderingApi, GetContext()->GetName().c_str(), bIsFullscreen); WmApi->set_fullscreen_changed_callback(Window::OnFullscreenChanged); WmApi->set_keyboard_callbacks(Window::KeyDown, Window::KeyUp, Window::AllKeysUp); } + void Window::ReadText(const char textToRead[]) + { + wchar_t wtext[sizeof(textToRead)]; + + mbstowcs(wtext, textToRead, sizeof(textToRead)); + const LPWSTR ptr = wtext; + + pVoice->Speak(ptr, SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); + } + void Window::RunCommands(Gfx* Commands) { gfx_start_frame(); gfx_run(Commands); diff --git a/libultraship/libultraship/Window.h b/libultraship/libultraship/Window.h index 6046ca4ae..5594bc721 100644 --- a/libultraship/libultraship/Window.h +++ b/libultraship/libultraship/Window.h @@ -24,6 +24,7 @@ namespace Ship { void ToggleFullscreen(); void SetFullscreen(bool bIsFullscreen); void ShowCursor(bool hide); + void ReadText(const char textToRead[]); bool IsFullscreen() { return bIsFullscreen; } uint32_t GetCurrentWidth(); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 0732a80e1..b4b3b9be4 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -689,6 +689,10 @@ extern "C" uint32_t OTRGetCurrentHeight() { return OTRGlobals::Instance->context->GetWindow()->GetCurrentHeight(); } +extern "C" void OTRTextToSpeechCallback(char* text) { + OTRGlobals::Instance->context->GetWindow()->ReadText(text); +} + extern "C" void OTRControllerCallback(ControllerCallback* controller) { auto controllers = OTRGlobals::Instance->context->GetWindow()->Controllers; for (int i = 0; i < controllers.size(); i++) { diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 92702f866..976ec0386 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -516,6 +516,44 @@ void func_8002C7BC(TargetContext* targetCtx, Player* player, Actor* actorArg, Gl targetCtx->arrowPointedActor = unkActor; targetCtx->activeCategory = actorCategory; targetCtx->unk_40 = 1.0f; + + if (CVar_GetS32("gBlindMode", 0)) { + u16 targetSound; + + if (targetCtx->arrowPointedActor != NULL) { + switch (targetCtx->activeCategory) { + case ACTORCAT_PROP: + targetSound = NA_SE_VO_NA_HELLO_1; + break; + case ACTORCAT_EXPLOSIVE: + targetSound = NA_SE_VO_NA_HELLO_3; + break; + case ACTORCAT_DOOR: + targetSound = NA_SE_VO_NA_HELLO_2; + break; + case ACTORCAT_CHEST: + targetSound = NA_SE_VO_NA_HELLO_1; + break; + case ACTORCAT_SWITCH: + targetSound = NA_SE_VO_NA_HELLO_1; + break; + case ACTORCAT_NPC: + targetSound = NA_SE_VO_NA_HELLO_3; + break; + case ACTORCAT_ENEMY: + targetSound = NA_SE_VO_NA_HELLO_0; + break; + case ACTORCAT_BOSS: + targetSound = NA_SE_VO_NA_HELLO_0; + break; + default: + targetSound = NA_SE_VO_NA_HELLO_2; + break; + } + + Audio_PlaySoundGeneral(targetSound, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } } if (unkActor == NULL) { diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index ca5154567..36169bba3 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -166,6 +166,8 @@ void FileChoose_FinishFadeIn(GameState* thisx) { } } +uint16_t lastButtonIndex; + /** * Update the cursor and wait for the player to select a button to change menus accordingly. * If an empty file is selected, enter the name entry config mode. @@ -276,6 +278,29 @@ void FileChoose_UpdateMainMenu(GameState* thisx) { } else { this->warningLabel = FS_WARNING_NONE; } + + if (lastButtonIndex != this->buttonIndex) { + switch (this->buttonIndex) { + case FS_BTN_MAIN_FILE_1: + case FS_BTN_MAIN_FILE_2: + case FS_BTN_MAIN_FILE_3: + Audio_PlaySoundGeneral(NA_SE_VO_NA_HELLO_3, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + case FS_BTN_MAIN_OPTIONS: + Audio_PlaySoundGeneral(NA_SE_VO_NA_HELLO_1, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + case FS_BTN_MAIN_COPY: + Audio_PlaySoundGeneral(NA_SE_VO_NA_HELLO_2, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + case FS_BTN_MAIN_ERASE: + Audio_PlaySoundGeneral(NA_SE_VO_NA_HELLO_0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + default: + break; + } + + lastButtonIndex = this->buttonIndex; + } } }