[Scummvm-git-logs] scummvm master -> cef1dd6d844e1402dbf2bb2ec2789b3c0f0df84c
sev-
noreply at scummvm.org
Thu Jun 19 22:25:26 UTC 2025
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
cef1dd6d84 CRUISE: Add text-to-speech (TTS)
Commit: cef1dd6d844e1402dbf2bb2ec2789b3c0f0df84c
https://github.com/scummvm/scummvm/commit/cef1dd6d844e1402dbf2bb2ec2789b3c0f0df84c
Author: ellm135 (ellm13531 at gmail.com)
Date: 2025-06-20T00:25:23+02:00
Commit Message:
CRUISE: Add text-to-speech (TTS)
Changed paths:
engines/cruise/backgroundIncrust.cpp
engines/cruise/cell.cpp
engines/cruise/cruise.cpp
engines/cruise/cruise.h
engines/cruise/cruise_main.cpp
engines/cruise/detection.cpp
engines/cruise/detection.h
engines/cruise/font.cpp
engines/cruise/function.cpp
engines/cruise/menu.cpp
engines/cruise/metaengine.cpp
engines/cruise/overlay.cpp
diff --git a/engines/cruise/backgroundIncrust.cpp b/engines/cruise/backgroundIncrust.cpp
index ec8e4a4c871..0760ff462b7 100644
--- a/engines/cruise/backgroundIncrust.cpp
+++ b/engines/cruise/backgroundIncrust.cpp
@@ -24,6 +24,22 @@
namespace Cruise {
+static const char *endTexts[] = {
+ "The End", // English
+ "Fin", // French
+ "Ende", // German
+ "El Fin", // Spanish
+ "Fine" // Italian
+};
+
+enum EndTextIndex {
+ kEnglish = 0,
+ kFrench = 1,
+ kGerman = 2,
+ kSpanish = 3,
+ kItalian = 4
+};
+
backgroundIncrustStruct backgroundIncrustHead;
void resetBackgroundIncrustList(backgroundIncrustStruct *pHead) {
@@ -140,6 +156,42 @@ backgroundIncrustStruct *addBackgroundIncrust(int16 overlayIdx, int16 objectIdx,
newElement->ptr = nullptr;
Common::strcpy_s(newElement->name, filesDatabase[params.fileIdx].subData.name);
+ if (_vm->getLanguage() != Common::RU_RUS && !strcmp(newElement->name, "FIN14.SET")) { // "The End" (nothing for Russian version)
+ const char *text;
+
+ switch (_vm->getLanguage()) {
+ case Common::EN_GRB:
+ case Common::EN_ANY:
+ text = endTexts[kEnglish];
+ break;
+ case Common::FR_FRA:
+ text = endTexts[kFrench];
+ break;
+ case Common::DE_DEU:
+ text = endTexts[kGerman];
+ break;
+ case Common::ES_ESP:
+ text = endTexts[kSpanish];
+ break;
+ case Common::IT_ITA:
+ text = endTexts[kItalian];
+ break;
+ default:
+ text = endTexts[kEnglish];
+ }
+
+ _vm->sayText(text, Common::TextToSpeechManager::QUEUE);
+ } else if (!strcmp(newElement->name, "CFACSP02.SET")) { // Title
+ if (_vm->getLanguage() == Common::FR_FRA) {
+ _vm->sayText("Croisi\212re pour un Cadavre", Common::TextToSpeechManager::QUEUE);
+ } else if (_vm->getLanguage() == Common::RU_RUS) {
+ // "ÐÑÑиз Ð´Ð»Ñ Ð¼ÐµÑÑвеÑа"
+ _vm->sayText("\x8a\xe0\x93\xa8\xa7 \xa4\xab\xef \xac\xa5\xe0\xe2\xa2\xa5\xe6\xa0", Common::TextToSpeechManager::QUEUE);
+ } else {
+ _vm->sayText("Cruise for a Corpse", Common::TextToSpeechManager::QUEUE);
+ }
+ }
+
if (filesDatabase[params.fileIdx].subData.resourceType == OBJ_TYPE_SPRITE) {
// sprite
int width = filesDatabase[params.fileIdx].width;
diff --git a/engines/cruise/cell.cpp b/engines/cruise/cell.cpp
index 5d2990a64d5..82181008dd3 100644
--- a/engines/cruise/cell.cpp
+++ b/engines/cruise/cell.cpp
@@ -22,6 +22,7 @@
#include "cruise/cruise_main.h"
#include "common/file.h"
#include "cruise/cell.h"
+#include "cruise/cruise.h"
namespace Cruise {
@@ -159,15 +160,34 @@ void createTextObject(cellStruct *pObject, int overlayIdx, int messageIdx, int x
cx->prev = pNewElement;
ax = getText(messageIdx, overlayIdx);
+ Common::String ttsMessage;
if (ax) {
pNewElement->gfxPtr = renderText(width, ax);
+ ttsMessage = ax;
+ ttsMessage.replace('|', '\n');
}
- // WORKAROUND: This is needed for the new dirty rect handling so as to properly refresh the screen
- // when the copy protection screen is being shown
- if ((messageIdx == 0) && !strcmp(overlayTable[overlayIdx].overlayName, "XX2"))
- backgroundChanged[0] = true;
+ if (!strcmp(overlayTable[overlayIdx].overlayName, "XX2")) {
+ // The English DOS and Russian versions automatically skip past the copy protection screen, so don't voice
+ // any of its text
+ if ((_vm->getLanguage() != Common::EN_ANY && _vm->getLanguage() != Common::RU_RUS && _vm->getLanguage() != Common::EN_GRB) ||
+ _vm->getPlatform() != Common::kPlatformDOS) {
+ // Don't voice the "OK" button (index 32) here
+ if (messageIdx != 32) {
+ _vm->sayText(ttsMessage, Common::TextToSpeechManager::QUEUE);
+ }
+ }
+
+ // WORKAROUND: This is needed for the new dirty rect handling so as to properly refresh the screen
+ // when the copy protection screen is being shown
+ if (messageIdx == 0)
+ backgroundChanged[0] = true;
+ } else {
+ // Sometimes this text shows up on screen later, so queue it up to be spoken
+ _vm->_toSpeak = ttsMessage;
+ _vm->_previousSaid.clear();
+ }
}
void removeCell(cellStruct *objPtr, int ovlNumber, int objectIdx, int objType, int backgroundPlane) {
diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp
index 13cfaa42ff1..e09c6578acb 100644
--- a/engines/cruise/cruise.cpp
+++ b/engines/cruise/cruise.cpp
@@ -22,6 +22,7 @@
#include "common/file.h"
#include "common/debug-channels.h"
#include "common/textconsole.h"
+#include "common/system.h"
#include "engines/util.h"
@@ -55,6 +56,9 @@ CruiseEngine::CruiseEngine(OSystem * syst, const CRUISEGameDescription *gameDesc
_polyStructs = nullptr;
_polyStruct = nullptr;
+ _mouseButtonDown = false;
+ _menuJustOpened = false;
+
// Setup mixer
syncSoundSettings();
}
@@ -86,6 +90,18 @@ Common::Error CruiseEngine::run() {
return Common::kUnknownError; // for compilers that don't support NORETURN
}
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan != nullptr) {
+ ttsMan->enable(ConfMan.getBool("tts_enabled"));
+ ttsMan->setLanguage(ConfMan.get("language"));
+
+ if (getLanguage() == Common::RU_RUS) {
+ _ttsTextEncoding = Common::CodePage::kDos866;
+ } else {
+ _ttsTextEncoding = Common::CodePage::kDos850;
+ }
+ }
+
initialize();
Cruise::changeCursor(Cruise::CURSOR_NORMAL);
@@ -194,11 +210,38 @@ void CruiseEngine::pauseEngine(bool pause) {
processAnimation();
flipScreen();
changeCursor(_savedCursor);
+
+ _vm->stopTextToSpeech();
}
gfxModuleData_addDirtyRect(Common::Rect(64, 100, 256, 117));
}
+void CruiseEngine::sayText(const Common::String &text, Common::TextToSpeechManager::Action action) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ // _previousSaid is used to prevent the TTS from looping when sayText is called inside a loop,
+ // for example when the cursor stays on a menu item. Without it when the text ends it would speak
+ // the same text again.
+ // _previousSaid is cleared when appropriate to allow for repeat requests
+ if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && _previousSaid != text) {
+ ttsMan->say(text, action, _ttsTextEncoding);
+ _previousSaid = text;
+ }
+}
+
+void CruiseEngine::sayQueuedText(Common::TextToSpeechManager::Action action) {
+ sayText(_toSpeak, action);
+ _toSpeak.clear();
+}
+
+void CruiseEngine::stopTextToSpeech() {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
+ ttsMan->stop();
+ _previousSaid.clear();
+ }
+}
+
Common::Error CruiseEngine::loadGameState(int slot) {
return loadSavegameData(slot);
}
diff --git a/engines/cruise/cruise.h b/engines/cruise/cruise.h
index ae18dbe1b23..d85016f5fb8 100644
--- a/engines/cruise/cruise.h
+++ b/engines/cruise/cruise.h
@@ -25,6 +25,7 @@
#include "common/scummsys.h"
#include "common/util.h"
#include "common/random.h"
+#include "common/text-to-speech.h"
#include "engines/engine.h"
@@ -78,6 +79,8 @@ private:
bool _speedFlag;
PauseToken _gamePauseToken;
+ Common::CodePage _ttsTextEncoding;
+
void initialize();
void deinitialize();
bool loadLanguageStrings();
@@ -104,6 +107,9 @@ public:
PCSound &sound() { return *_sound; }
virtual void pauseEngine(bool pause);
const char *langString(LangStringId langId) { return _langStrings[(int)langId].c_str(); }
+ void sayText(const Common::String &text, Common::TextToSpeechManager::Action action);
+ void sayQueuedText(Common::TextToSpeechManager::Action action);
+ void stopTextToSpeech();
static const char *getSavegameFile(int saveGameIdx);
Common::Error loadGameState(int slot) override;
@@ -141,6 +147,11 @@ public:
Common::Array<CtStruct> *_polyStruct;
Common::File _PAL_file;
+
+ Common::String _toSpeak;
+ Common::String _previousSaid;
+ bool _mouseButtonDown;
+ bool _menuJustOpened;
};
extern CruiseEngine *_vm;
diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp
index 4c8a25c56f1..78d0523b447 100644
--- a/engines/cruise/cruise_main.cpp
+++ b/engines/cruise/cruise_main.cpp
@@ -712,7 +712,16 @@ int findObject(int mouseX, int mouseY, int *outObjOvl, int *outObjIdx) {
*outObjOvl = linkedObjOvl;
*outObjIdx = linkedObjIdx;
+ // "Ok" button on the copy protection screen
+ if (!strcmp(overlayTable[currentObject->overlay].overlayName, "XX2") &&
+ (currentObject->idx == 1 || currentObject->idx == 0)) {
+ _vm->sayText("OK", Common::TextToSpeechManager::INTERRUPT);
+ }
+
return (currentObject->type);
+ } else if (!strcmp(overlayTable[currentObject->overlay].overlayName, "XX2") &&
+ (currentObject->idx == 1 || currentObject->idx == 0)) {
+ _vm->_previousSaid.clear();
}
} else {
// int numBitPlanes = filesDatabase[j].resType;
@@ -810,6 +819,8 @@ void buildInventory(int X, int Y) {
if (numObjectInInventory == 0) {
freeMenu(menuTable[1]);
menuTable[1] = nullptr;
+ } else {
+ _vm->sayText(_vm->langString(ID_INVENTORY), Common::TextToSpeechManager::INTERRUPT);
}
}
@@ -835,6 +846,7 @@ menuElementSubStruct *getSelectedEntryInMenu(menuStruct *pMenu) {
currentMenuElementX = pMenuElement->x;
currentMenuElementY = pMenuElement->y;
currentMenuElement = pMenuElement;
+ _vm->stopTextToSpeech();
return pMenuElement->ptrSub;
}
@@ -957,6 +969,8 @@ bool findRelation(int objOvl, int objIdx, int x, int y) {
getSingleObjectParam(objOvl, objIdx, 5, &objectState);
+ Common::String ttsMessage;
+
for (int j = 1; j < numOfLoadedOverlay; j++) {
if (overlayTable[j].alreadyLoaded) {
int idHeader = overlayTable[j].ovlData->numMsgRelHeader;
@@ -1009,6 +1023,7 @@ bool findRelation(int objOvl, int objIdx, int x, int y) {
const char *ptrName = getObjectName(ptrHead->obj1Number, ovl3->arrayNameObj);
menuTable[0] = createMenu(x, y, ptrName);
+ ttsMessage = ptrName;
first = false;
}
}
@@ -1037,6 +1052,10 @@ bool findRelation(int objOvl, int objIdx, int x, int y) {
}
}
+ if (found) {
+ _vm->sayText(ttsMessage, Common::TextToSpeechManager::INTERRUPT);
+ }
+
return found;
}
@@ -1191,6 +1210,8 @@ void callSubRelation(menuElementSubStruct *pMenuElement, int nOvl, int nObj) {
userEnabled = 0;
freezeCell(&cellHead, ovlIdx, pHeader->id, 5, -1, 0, 9998);
}
+ } else {
+ _vm->sayQueuedText(Common::TextToSpeechManager::QUEUE);
}
}
}
@@ -1339,6 +1360,8 @@ void callRelation(menuElementSubStruct *pMenuElement, int nObj2) {
userEnabled = 0;
freezeCell(&cellHead, ovlIdx, pHeader->id, 5, -1, 0, 9998);
}
+ } else {
+ _vm->sayQueuedText(Common::TextToSpeechManager::QUEUE);
}
}
} else {
@@ -1451,8 +1474,10 @@ int CruiseEngine::processInput() {
if (userWait) {
// Check for left mouse button click or Space to end user waiting
- if ((action == kActionEndUserWaiting) || (button == CRS_MB_LEFT))
+ if ((action == kActionEndUserWaiting) || (button == CRS_MB_LEFT)) {
userWait = false;
+ stopTextToSpeech();
+ }
action = kActionNone;
return 0;
@@ -1493,6 +1518,8 @@ int CruiseEngine::processInput() {
if (menuTable[0]) {
if (dialogFound) {
+ sayText(menuTable[0]->stringPtr, Common::TextToSpeechManager::INTERRUPT);
+
currentActiveMenu = 0;
} else {
freeMenu(menuTable[0]);
@@ -1621,6 +1648,11 @@ int CruiseEngine::processInput() {
Common::strcat_s(text, ":");
Common::strcat_s(text, currentMenuElement->string);
linkedMsgList = renderText(320, (const char *)text);
+
+ Common::String ttsMessage = text;
+ Common::replace(ttsMessage, ":", ": ");
+ sayText(ttsMessage, Common::TextToSpeechManager::INTERRUPT);
+
changeCursor(CURSOR_CROSS);
}
}
@@ -1686,6 +1718,7 @@ bool manageEvents() {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
currentMouseButton |= CRS_MB_LEFT;
+ _vm->_mouseButtonDown = true;
break;
case Common::EVENT_LBUTTONUP:
currentMouseButton &= ~CRS_MB_LEFT;
@@ -1693,6 +1726,7 @@ bool manageEvents() {
break;
case Common::EVENT_RBUTTONDOWN:
currentMouseButton |= CRS_MB_RIGHT;
+ _vm->_mouseButtonDown = true;
break;
case Common::EVENT_RBUTTONUP:
currentMouseButton &= ~CRS_MB_RIGHT;
@@ -1942,6 +1976,7 @@ void CruiseEngine::mainLoop() {
if (isAnimFinished(narratorOvl, narratorIdx, &actorHead, ATP_MOUSE)) {
if (autoMsg != -1) {
freezeCell(&cellHead, autoOvl, autoMsg, 5, -1, 9998, 0);
+ sayQueuedText(Common::TextToSpeechManager::QUEUE);
char* pText = getText(autoMsg, autoOvl);
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index ef6be115897..97524ad319a 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -47,7 +47,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::EN_GRB,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -58,7 +58,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -69,7 +69,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -80,7 +80,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -91,7 +91,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -102,7 +102,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -113,7 +113,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -124,7 +124,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Fanmade translation by old-games.ru
@@ -135,7 +135,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{
@@ -146,7 +146,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga English US GOLD edition.
@@ -157,7 +157,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga English US GOLD edition (Delphine Collection).
@@ -168,7 +168,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga Italian US GOLD edition.
@@ -179,7 +179,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga Spanish edition.
@@ -190,7 +190,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga French edition.
@@ -201,7 +201,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga French edition (alternate).
@@ -212,7 +212,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // Amiga Italian (Fanmade translation 1.0).
@@ -223,7 +223,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // AtariST English KixxXL edition.
@@ -234,7 +234,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{ // AtariST French edition. Bugreport #12824
@@ -245,7 +245,7 @@ static const CRUISEGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
},
{AD_TABLE_END_MARKER}
diff --git a/engines/cruise/detection.h b/engines/cruise/detection.h
index 3e82dc2eeb1..f45889a9100 100644
--- a/engines/cruise/detection.h
+++ b/engines/cruise/detection.h
@@ -30,6 +30,8 @@ struct CRUISEGameDescription {
ADGameDescription desc;
};
+#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
+
} // End of namespace Cruise
#endif // CRUISE_DETECTION_H
diff --git a/engines/cruise/font.cpp b/engines/cruise/font.cpp
index 5c8fdc9542b..cd776e2e40a 100644
--- a/engines/cruise/font.cpp
+++ b/engines/cruise/font.cpp
@@ -270,6 +270,8 @@ void drawString(int32 x, int32 y, const char *string, uint8 *buffer, uint8 fontC
// Draw the message
drawMessage(s, x, y, rightBorder_X - x, fontColor, buffer);
+ _vm->sayText(string, Common::TextToSpeechManager::INTERRUPT);
+
// Free the data
delete s->imagePtr;
free(s);
diff --git a/engines/cruise/function.cpp b/engines/cruise/function.cpp
index 48d6d087d57..ba0f36978e0 100644
--- a/engines/cruise/function.cpp
+++ b/engines/cruise/function.cpp
@@ -713,6 +713,15 @@ int16 Op_GetMouseButton() {
getMouseStatus(&dummy, &mouseX, &mouseButton, &mouseY);
+ // Stop TTS when progressing a dialog or switching screens with user input
+ // _mouseButtonDown is used to prevent cases where the mouse input in this function is repeated several times
+ // after a click, which sometimes immediately ends TTS for a message that just appeared on screen
+ if ((mouseButton == 1 || mouseButton == 2) && !userEnabled && _vm->_mouseButtonDown &&
+ strcmp(overlayTable[currentScriptPtr->overlayNumber].overlayName, "SHIP")) {
+ _vm->stopTextToSpeech();
+ _vm->_mouseButtonDown = false;
+ }
+
return mouseButton;
}
@@ -819,6 +828,13 @@ int16 Op_AddMessage() {
}
createTextObject(&cellHead, overlayIdx, var_8, var_6, var_4, var_2, color, masterScreen, currentScriptPtr->overlayNumber, currentScriptPtr->scriptNumber);
+
+ // Interrupt for location names on the map to avoid a long queue if the user clicks a lot of locations
+ if (!strcmp(overlayTable[currentScriptPtr->overlayNumber].overlayName, "SHIP")) {
+ _vm->sayQueuedText(Common::TextToSpeechManager::INTERRUPT);
+ } else {
+ _vm->sayQueuedText(Common::TextToSpeechManager::QUEUE);
+ }
return 0;
}
diff --git a/engines/cruise/menu.cpp b/engines/cruise/menu.cpp
index 45acc239a56..9c8260f5065 100644
--- a/engines/cruise/menu.cpp
+++ b/engines/cruise/menu.cpp
@@ -45,6 +45,7 @@ menuStruct *createMenu(int X, int Y, const char *menuName) {
entry->numElements = 0;
entry->ptrNextElement = nullptr;
entry->gfx = renderText(160, menuName);
+ _vm->_menuJustOpened = true;
return entry;
}
@@ -124,6 +125,8 @@ void addSelectableMenuEntry(int ovlIdx, int headerIdx, menuStruct *pMenu, int pa
}
void updateMenuMouse(int mouseX, int mouseY, menuStruct *pMenu) {
+ bool menuItemSelected = false;
+
if (pMenu) {
if (pMenu->gfx) {
int height = pMenu->gfx->height; // rustine
@@ -138,6 +141,18 @@ void updateMenuMouse(int mouseX, int mouseY, menuStruct *pMenu) {
if ((mouseY > pCurrentEntry->y) && ((pCurrentEntry->y + height) >= mouseY)) {
var_2 = 1;
pCurrentEntry->selected = true;
+
+ // Queue up the first selected item after opening the menu
+ // This allows the name of the menu to be always voiced, in cases where something
+ // is selected the instant the menu opens
+ if (_vm->_menuJustOpened) {
+ _vm->sayText(pCurrentEntry->string, Common::TextToSpeechManager::QUEUE);
+ _vm->_menuJustOpened = false;
+ } else {
+ _vm->sayText(pCurrentEntry->string, Common::TextToSpeechManager::INTERRUPT);
+ }
+
+ menuItemSelected = true;
}
}
}
@@ -146,6 +161,10 @@ void updateMenuMouse(int mouseX, int mouseY, menuStruct *pMenu) {
}
}
}
+
+ if (!menuItemSelected) {
+ _vm->_previousSaid.clear();
+ }
}
bool manageEvents();
@@ -248,6 +267,7 @@ int playerMenu(int menuX, int menuY) {
menuTable[0] = createMenu(menuX, menuY, _vm->langString(ID_PLAYER_MENU));
assert(menuTable[0]);
+ _vm->sayText(_vm->langString(ID_PLAYER_MENU), Common::TextToSpeechManager::INTERRUPT);
//addSelectableMenuEntry(0, 3, menuTable[0], 1, -1, "Save game disk");
if (userEnabled) {
diff --git a/engines/cruise/metaengine.cpp b/engines/cruise/metaengine.cpp
index bc05fc31738..8a27045dc72 100644
--- a/engines/cruise/metaengine.cpp
+++ b/engines/cruise/metaengine.cpp
@@ -36,6 +36,26 @@
namespace Cruise {
+#ifdef USE_TTS
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_TTS,
+ {
+ _s("Enable Text to Speech"),
+ _s("Use TTS to read text in the game (if TTS is available)"),
+ "tts_enabled",
+ false,
+ 0,
+ 0
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+#endif
+
const char *CruiseEngine::getGameId() const {
return _gameDescription->desc.gameId;
}
@@ -55,6 +75,12 @@ public:
return "cruise";
}
+#ifdef USE_TTS
+ const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+ return Cruise::optionsList;
+ }
+#endif
+
bool hasFeature(MetaEngineFeature f) const override;
int getMaximumSaveSlot() const override { return 99; }
diff --git a/engines/cruise/overlay.cpp b/engines/cruise/overlay.cpp
index 016b6d7c26c..27889958401 100644
--- a/engines/cruise/overlay.cpp
+++ b/engines/cruise/overlay.cpp
@@ -143,6 +143,10 @@ int loadOverlay(const char *scriptName) {
scriptNotLoadedBefore = true;
}
+ if (!strcmp(scriptName, "LOGO")) { // Logo when game is first booted up (or restarted)
+ _vm->sayText("Delphine Software\nCinematique", Common::TextToSpeechManager::QUEUE);
+ }
+
if (overlayTable[scriptIdx].alreadyLoaded) {
return (scriptIdx);
}
More information about the Scummvm-git-logs
mailing list