[Scummvm-git-logs] scummvm master -> 59eeb400cf9da62c890bfa5eb9936dab828fba75
sev-
noreply at scummvm.org
Thu Aug 21 08:26:09 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:
59eeb400cf GOT: Add text-to-speech (TTS)
Commit: 59eeb400cf9da62c890bfa5eb9936dab828fba75
https://github.com/scummvm/scummvm/commit/59eeb400cf9da62c890bfa5eb9936dab828fba75
Author: ellm135 (ellm13531 at gmail.com)
Date: 2025-08-21T10:26:06+02:00
Commit Message:
GOT: Add text-to-speech (TTS)
Changed paths:
engines/got/data/thorinfo.cpp
engines/got/data/thorinfo.h
engines/got/detection.h
engines/got/detection_tables.h
engines/got/events.cpp
engines/got/events.h
engines/got/game/boss1.cpp
engines/got/game/boss2.cpp
engines/got/game/boss3.cpp
engines/got/got.cpp
engines/got/metaengine.cpp
engines/got/views/credits.cpp
engines/got/views/dialogs/high_scores.cpp
engines/got/views/dialogs/say.cpp
engines/got/views/dialogs/select_item.cpp
engines/got/views/dialogs/select_option.cpp
engines/got/views/dialogs/select_option.h
engines/got/views/game_status.cpp
engines/got/views/game_status.h
engines/got/views/part_title.cpp
engines/got/views/story.cpp
engines/got/views/story.h
engines/got/views/view.cpp
engines/got/views/view.h
diff --git a/engines/got/data/thorinfo.cpp b/engines/got/data/thorinfo.cpp
index acca6cd7c13..e0abc2bc0b3 100644
--- a/engines/got/data/thorinfo.cpp
+++ b/engines/got/data/thorinfo.cpp
@@ -49,6 +49,11 @@ void ThorInfo::clear() {
_lastObject = 0;
_lastObjectName = nullptr;
_armor = 0;
+#ifdef USE_TTS
+ _previousJewels = -1;
+ _previousScore = -1;
+ _previousKeys = -1;
+#endif
Common::fill(_filler, _filler + 65, 0);
}
@@ -85,6 +90,12 @@ void ThorInfo::sync(Common::Serializer &s) {
if (s.isLoading()) {
_objectName = (_object == 0) ? nullptr : OBJECT_NAMES[_object - 1];
_lastObjectName = (_lastObject == 0) ? nullptr : OBJECT_NAMES[_lastObject - 1];
+
+#ifdef USE_TTS
+ _previousJewels = -1;
+ _previousScore = -1;
+ _previousKeys = -1;
+#endif
}
}
diff --git a/engines/got/data/thorinfo.h b/engines/got/data/thorinfo.h
index 4faff526bae..2c54c82100b 100644
--- a/engines/got/data/thorinfo.h
+++ b/engines/got/data/thorinfo.h
@@ -51,6 +51,11 @@ struct ThorInfo {
const char *_lastObjectName = nullptr;
byte _armor = 0;
byte _filler[65] = {};
+#ifdef USE_TTS
+ int _previousJewels = -1;
+ int _previousScore = -1;
+ int _previousKeys = -1;
+#endif
void clear();
void sync(Common::Serializer &s);
diff --git a/engines/got/detection.h b/engines/got/detection.h
index 369636106c6..628060a8b34 100644
--- a/engines/got/detection.h
+++ b/engines/got/detection.h
@@ -39,6 +39,7 @@ extern const PlainGameDescriptor gotGames[];
extern const ADGameDescription gameDescriptions[];
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_TTS GUIO_GAMEOPTIONS2
} // End of namespace Got
diff --git a/engines/got/detection_tables.h b/engines/got/detection_tables.h
index 6557ef4cb6e..914a0c42f17 100644
--- a/engines/got/detection_tables.h
+++ b/engines/got/detection_tables.h
@@ -34,7 +34,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING,
- GUIO1(GUIO_NOSPEECH)
+ GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
},
{
"got",
@@ -43,7 +43,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_DEMO,
- GUIO1(GUIO_NOSPEECH)
+ GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
},
{
"got",
@@ -52,7 +52,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING,
- GUIO1(GUIO_NOSPEECH)
+ GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
},
{
"got",
@@ -61,7 +61,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_DEMO,
- GUIO1(GUIO_NOSPEECH)
+ GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
},
AD_TABLE_END_MARKER};
diff --git a/engines/got/events.cpp b/engines/got/events.cpp
index d2256760aa1..0645288d298 100644
--- a/engines/got/events.cpp
+++ b/engines/got/events.cpp
@@ -413,6 +413,9 @@ UIElement *UIElement::findViewGlobally(const Common::String &name) {
void UIElement::close() {
assert(g_events->focusedView() == this);
g_events->popView();
+#ifdef USE_TTS
+ stopTextToSpeech();
+#endif
}
void UIElement::draw() {
@@ -484,4 +487,17 @@ void UIElement::timeout() {
redraw();
}
+#ifdef USE_TTS
+
+void UIElement::stopTextToSpeech() {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
+ ttsMan->stop();
+ }
+
+ _previousSaid.clear();
+}
+
+#endif
+
} // namespace Got
diff --git a/engines/got/events.h b/engines/got/events.h
index 996a5f4e088..2c3c17a5455 100644
--- a/engines/got/events.h
+++ b/engines/got/events.h
@@ -87,6 +87,9 @@ protected:
Bounds _bounds;
bool _needsRedraw = true;
Common::String _name;
+#ifdef USE_TTS
+ Common::String _previousSaid;
+#endif
protected:
/**
@@ -215,6 +218,13 @@ protected:
return false;
}
+#ifdef USE_TTS
+ /**
+ * Stops TTS voicing and clears the previously spoken text
+ */
+ void stopTextToSpeech();
+#endif
+
public:
bool send(const MouseMoveMessage &msg) {
return msgMouseMove(msg);
diff --git a/engines/got/game/boss1.cpp b/engines/got/game/boss1.cpp
index 4990685b18f..6fc45b8bb16 100644
--- a/engines/got/game/boss1.cpp
+++ b/engines/got/game/boss1.cpp
@@ -278,6 +278,10 @@ void boss1ClosingSequence4() {
_G(scrn)._music = 4;
showLevel(BOSS_LEVEL1);
+#ifdef USE_TTS
+ _G(thorInfo)._previousScore = -1;
+#endif
+
playSound(ANGEL, true);
placeTile(18, 6, 148);
placeTile(19, 6, 202);
diff --git a/engines/got/game/boss2.cpp b/engines/got/game/boss2.cpp
index fa9c9e8c7b2..8be341c1d08 100644
--- a/engines/got/game/boss2.cpp
+++ b/engines/got/game/boss2.cpp
@@ -412,6 +412,10 @@ void boss2ClosingSequence4() {
showLevel(BOSS_LEVEL2);
+#ifdef USE_TTS
+ _G(thorInfo)._previousScore = -1;
+#endif
+
playSound(ANGEL, true);
placeTile(18, 10, 152);
placeTile(19, 10, 202);
diff --git a/engines/got/game/boss3.cpp b/engines/got/game/boss3.cpp
index 58cd3ad8b64..9f7f38af0be 100644
--- a/engines/got/game/boss3.cpp
+++ b/engines/got/game/boss3.cpp
@@ -548,6 +548,10 @@ void boss3ClosingSequence3() {
_G(scrn)._music = 6;
showLevel(BOSS_LEVEL3);
+#ifdef USE_TTS
+ _G(thorInfo)._previousScore = -1;
+#endif
+
_G(exitFlag) = 0;
musicPause();
diff --git a/engines/got/got.cpp b/engines/got/got.cpp
index a533eba14d3..7c55cbdc145 100644
--- a/engines/got/got.cpp
+++ b/engines/got/got.cpp
@@ -78,6 +78,14 @@ Common::Error GotEngine::run() {
initGame();
syncSoundSettings();
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan) {
+ ttsMan->enable(ConfMan.getBool("tts_enabled"));
+ ttsMan->setLanguage(ConfMan.get("language"));
+ }
+#endif
+
runGame();
return Common::kNoError;
diff --git a/engines/got/metaengine.cpp b/engines/got/metaengine.cpp
index 2024fa7b045..33048fbebc1 100644
--- a/engines/got/metaengine.cpp
+++ b/engines/got/metaengine.cpp
@@ -40,6 +40,21 @@ static const ADExtraGuiOptionsMap optionsList[] = {
0
}
},
+
+#ifdef USE_TTS
+ {
+ 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
+ }
+ },
+#endif
+
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
diff --git a/engines/got/views/credits.cpp b/engines/got/views/credits.cpp
index 8204c8e565e..ea13a51c3d3 100644
--- a/engines/got/views/credits.cpp
+++ b/engines/got/views/credits.cpp
@@ -25,6 +25,22 @@
namespace Got {
namespace Views {
+#ifdef USE_TTS
+
+static const char *creditsText[] = {
+ "Programming: Ron Davis",
+ "Graphics: Gary Sirois",
+ "Level Design: Adam Pedersen",
+ "Additional Programming: Jason Blochowiak",
+ "Music: Roy Davis",
+ "Title Screen Art: Wayne C. Timmerman",
+ "Additional Level Design: Ron Davis, Doug Howell, Ken Heckbert, Evan Heckbert",
+ "Play Testing: Ken Heckbert, Doug Howell, Tom King",
+ "Play Testing: Kelly Rogers, Michael Smith, Rik Pierce"
+};
+
+#endif
+
#define CREDITS_COUNT 9
#define FADE_FRAMES 15
#define DISPLAY_TIME 15
@@ -68,6 +84,10 @@ void Credits::draw() {
drawCredit(s, gfxNum2, gfxNum4, 16, 40 + 24);
}
+#ifdef USE_TTS
+ sayText(creditsText[creditNum]);
+#endif
+
s.markAllDirty();
}
@@ -111,7 +131,12 @@ bool Credits::tick() {
if (_frameCtr == (CREDIT_TIME * CREDITS_COUNT) + 10) {
replaceView("HighScores", true, true);
} else {
- ++_frameCtr;
+#ifdef USE_TTS
+ // Pause credits progression until TTS is finished
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (_frameCtr % CREDIT_TIME < FADE_FRAMES + DISPLAY_TIME || !ttsMan || !ttsMan->isSpeaking())
+#endif
+ ++_frameCtr;
redraw();
}
}
diff --git a/engines/got/views/dialogs/high_scores.cpp b/engines/got/views/dialogs/high_scores.cpp
index 26154660545..029aecdf65f 100644
--- a/engines/got/views/dialogs/high_scores.cpp
+++ b/engines/got/views/dialogs/high_scores.cpp
@@ -55,6 +55,9 @@ void HighScores::draw() {
const int titleStart = (s.w - title.size() * 8) / 2;
s.print(Common::Point(titleStart, 4), title, 54);
+#ifdef USE_TTS
+ sayText(title);
+#endif
for (int i = 0; i < 7; ++i) {
const HighScore &hs = _G(highScores)._scores[_currentArea - 1][i];
@@ -67,6 +70,11 @@ void HighScores::draw() {
s.print(Common::Point(15, 24 + i * 18), hs._name, 14);
Common::String score = Common::String::format("%d", hs._total);
s.print(Common::Point(275 - (score.size() * 8), 24 + i * 18), score, 14);
+
+#ifdef USE_TTS
+ sayText(Common::String::format("%s: %d", hs._name, hs._total), Common::TextToSpeechManager::QUEUE);
+ _previousSaid.clear();
+#endif
}
}
@@ -125,7 +133,8 @@ void HighScores::goToNextArea() {
}
bool HighScores::tick() {
- if (--_timeoutCtr < 0)
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (--_timeoutCtr < 0 && (!ttsMan || !ttsMan->isSpeaking()))
goToNextArea();
return true;
diff --git a/engines/got/views/dialogs/say.cpp b/engines/got/views/dialogs/say.cpp
index 6c368e9045f..e4afd5c24a1 100644
--- a/engines/got/views/dialogs/say.cpp
+++ b/engines/got/views/dialogs/say.cpp
@@ -69,6 +69,24 @@ void Say::draw() {
const char *endP = _content + _contentLength;
int color = 14;
int x = 20, lc = 0;
+#ifdef USE_TTS
+ // Voice all text immediately, so that TTS syncs better with the text appearing on screen
+ Common::String ttsMessage = _content;
+ int ttsMessageLc = 0;
+ for (uint i = 0; i < ttsMessage.size(); ++i) {
+ if (ttsMessage[i] == '\n') {
+ ttsMessageLc++;
+
+ if (ttsMessageLc > 4) {
+ ttsMessage.erase(i);
+ ttsMessage += "\n\nMore...";
+ break;
+ }
+ }
+ }
+
+ sayText(ttsMessage);
+#endif
while (p < endP) {
if (*p == '~' && Common::isXDigit(*(p + 1))) {
diff --git a/engines/got/views/dialogs/select_item.cpp b/engines/got/views/dialogs/select_item.cpp
index 3dd3c856dc6..25bd8b83e7d 100644
--- a/engines/got/views/dialogs/select_item.cpp
+++ b/engines/got/views/dialogs/select_item.cpp
@@ -45,6 +45,9 @@ void SelectItem::draw() {
if (_G(thorInfo)._inventory == 0) {
s.print(Common::Point(44, 52), "No Items Found", 14);
+#ifdef USE_TTS
+ sayText("No Items Found");
+#endif
return;
}
@@ -66,6 +69,10 @@ void SelectItem::draw() {
else
objName = _G(thorInfo)._objectName;
+#ifdef USE_TTS
+ sayText(objName);
+#endif
+
s.print(Common::Point((s.w - (strlen(objName) * 8)) / 2, 66), objName, 12);
s.frameRect(Common::Rect(26 + (_selectedItem * _HRZSP), 22,
43 + (_selectedItem * _HRZSP), 42),
diff --git a/engines/got/views/dialogs/select_option.cpp b/engines/got/views/dialogs/select_option.cpp
index 6e8ddaa9981..17e3f968ce3 100644
--- a/engines/got/views/dialogs/select_option.cpp
+++ b/engines/got/views/dialogs/select_option.cpp
@@ -77,6 +77,16 @@ void SelectOption::draw() {
for (uint i = 0; i < _options.size(); ++i)
s.print(Common::Point(32, 28 + i * 16), _options[i], 14);
+#ifdef USE_TTS
+ if (!_titleVoiced) {
+ sayText(_title);
+ sayText(_options[_selectedItem], Common::TextToSpeechManager::QUEUE);
+ _titleVoiced = true;
+ } else {
+ sayText(_options[_selectedItem]);
+ }
+#endif
+
// Draw selection pointer
if (_smackCtr > 0) {
// Selecting an item
@@ -91,6 +101,11 @@ void SelectOption::draw() {
bool SelectOption::msgFocus(const FocusMessage &msg) {
_selectedItem = 0;
_smackCtr = 0;
+
+#ifdef USE_TTS
+ _titleVoiced = false;
+#endif
+
return true;
}
diff --git a/engines/got/views/dialogs/select_option.h b/engines/got/views/dialogs/select_option.h
index ee3d75309b5..8134bb70e47 100644
--- a/engines/got/views/dialogs/select_option.h
+++ b/engines/got/views/dialogs/select_option.h
@@ -39,6 +39,10 @@ private:
int _hammerFrame = 0;
int _smackCtr = 0;
+#ifdef USE_TTS
+ bool _titleVoiced = false;
+#endif
+
protected:
int _selectedItem = 0;
diff --git a/engines/got/views/game_status.cpp b/engines/got/views/game_status.cpp
index 5a14c25af36..67b38362d36 100644
--- a/engines/got/views/game_status.cpp
+++ b/engines/got/views/game_status.cpp
@@ -61,6 +61,20 @@ void GameStatus::displayMagic(GfxSurface &s) {
void GameStatus::displayJewels(GfxSurface &s) {
const Common::String str = Common::String::format("%d", _G(thorInfo)._jewels);
+
+#ifdef USE_TTS
+ if (_G(thorInfo)._previousJewels != _G(thorInfo)._jewels && _G(gameMode) != MODE_SCORE_INV) {
+ if (_lastStatusSpoken == kJewels) {
+ stopTextToSpeech();
+ }
+
+ sayText("Jewels: " + str, Common::TextToSpeechManager::QUEUE);
+
+ _G(thorInfo)._previousJewels = _G(thorInfo)._jewels;
+ _lastStatusSpoken = kJewels;
+ }
+#endif
+
int x;
if (str.size() == 1)
x = 70;
@@ -77,6 +91,19 @@ void GameStatus::displayScore(GfxSurface &s) {
const Common::String str = Common::String::format("%ld", _G(thorInfo)._score);
const int x = 276 - (str.size() * 8);
+#ifdef USE_TTS
+ if (_G(thorInfo)._previousScore != _G(thorInfo)._score && _G(gameMode) != MODE_SCORE_INV && _scoreCountdown == 0) {
+ if (_lastStatusSpoken == kScore) {
+ stopTextToSpeech();
+ }
+
+ sayText("Score: " + str, Common::TextToSpeechManager::QUEUE);
+
+ _G(thorInfo)._previousScore = _G(thorInfo)._score;
+ _lastStatusSpoken = kScore;
+ }
+#endif
+
s.fillRect(Common::Rect(223, 32, 279, 42), STAT_COLOR);
s.print(Common::Point(x, 32), str, 14);
}
@@ -84,6 +111,19 @@ void GameStatus::displayScore(GfxSurface &s) {
void GameStatus::displayKeys(GfxSurface &s) {
const Common::String str = Common::String::format("%d", _G(thorInfo)._keys);
+#ifdef USE_TTS
+ if (_G(thorInfo)._previousKeys != _G(thorInfo)._keys && _G(gameMode) != MODE_SCORE_INV) {
+ if (_lastStatusSpoken == kKeys) {
+ stopTextToSpeech();
+ }
+
+ sayText("Keys: " + str, Common::TextToSpeechManager::QUEUE);
+
+ _G(thorInfo)._previousKeys = _G(thorInfo)._keys;
+ _lastStatusSpoken = kKeys;
+ }
+#endif
+
int x;
if (str.size() == 1)
x = 150;
diff --git a/engines/got/views/game_status.h b/engines/got/views/game_status.h
index d48c2095c97..d94a7ed2fa7 100644
--- a/engines/got/views/game_status.h
+++ b/engines/got/views/game_status.h
@@ -27,6 +27,17 @@
namespace Got {
namespace Views {
+#ifdef USE_TTS
+
+enum LastStatusSpoken {
+ kNone = 0,
+ kJewels = 1,
+ kScore = 2,
+ kKeys = 3
+};
+
+#endif
+
class GameStatus : public View {
private:
int _scoreCountdown = 0;
@@ -39,6 +50,10 @@ private:
void displayKeys(GfxSurface &s);
void displayItem(GfxSurface &s);
+#ifdef USE_TTS
+ LastStatusSpoken _lastStatusSpoken = kNone;
+#endif
+
public:
GameStatus() : View("GameStatus") {}
virtual ~GameStatus() {}
diff --git a/engines/got/views/part_title.cpp b/engines/got/views/part_title.cpp
index d63952c8e01..d34485f35c1 100644
--- a/engines/got/views/part_title.cpp
+++ b/engines/got/views/part_title.cpp
@@ -30,20 +30,35 @@ void PartTitle::draw() {
GfxSurface s = getSurface();
s.clear();
s.print(Common::Point(13 * 8, 13 * 8), "God of Thunder", 14);
+#ifdef USE_TTS
+ Common::String ttsMessage = "God of Thunder: ";
+#endif
switch (_G(area)) {
case 1:
s.print(Common::Point(8 * 8, 15 * 8), "Part I: Serpent Surprise", 32);
+#ifdef USE_TTS
+ ttsMessage += "Part 1: Serpent Surprise";
+#endif
break;
case 2:
s.print(Common::Point(7 * 8, 15 * 8), "Part II: Non-Stick Nognir", 32);
+#ifdef USE_TTS
+ ttsMessage += "Part 2: Non-Stick Nognir";
+#endif
break;
case 3:
s.print(Common::Point(7 * 8, 15 * 8), "Part III: Lookin' for Loki", 32);
+#ifdef USE_TTS
+ ttsMessage += "Part 3: Lookin' for Loki";
+#endif
break;
default:
break;
}
+#ifdef USE_TTS
+ sayText(ttsMessage);
+#endif
}
bool PartTitle::msgAction(const ActionMessage &msg) {
@@ -54,7 +69,8 @@ bool PartTitle::msgAction(const ActionMessage &msg) {
}
bool PartTitle::tick() {
- if (++_timeoutCtr == 80) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (++_timeoutCtr >= 80 && (!ttsMan || !ttsMan->isSpeaking())) {
_timeoutCtr = 0;
done();
}
@@ -63,6 +79,9 @@ bool PartTitle::tick() {
}
void PartTitle::done() {
+#ifdef USE_TTS
+ stopTextToSpeech();
+#endif
replaceView("Game", true, true);
}
diff --git a/engines/got/views/story.cpp b/engines/got/views/story.cpp
index f4d7205c072..38f2fdd3e12 100644
--- a/engines/got/views/story.cpp
+++ b/engines/got/views/story.cpp
@@ -60,6 +60,10 @@ bool Story::msgFocus(const FocusMessage &msg) {
const char *p = (const char *)_G(tmpBuff);
+#ifdef USE_TTS
+ _ttsMessage.clear();
+#endif
+
while (i < 46) {
if (*p == '\n') {
x = 8;
@@ -69,7 +73,13 @@ bool Story::msgFocus(const FocusMessage &msg) {
if (i == 23) {
// Move to start of of "second page" of the surface
y = 240 + 2;
+#ifdef USE_TTS
+ _secondPageIndex = _ttsMessage.size() - 1;
+#endif
}
+#ifdef USE_TTS
+ _ttsMessage += '\n';
+#endif
} else if (*p == '/' && *(p + 4) == '/') {
p++;
s[0] = *p++;
@@ -88,11 +98,19 @@ bool Story::msgFocus(const FocusMessage &msg) {
_surface.rawPrintChar(*p, x + 1, y, 255);
_surface.rawPrintChar(*p, x, y, color);
x += 8;
+
+#ifdef USE_TTS
+ _ttsMessage += *p;
+#endif
}
p++;
}
+#ifdef USE_TTS
+ sayText(_ttsMessage.substr(0, _secondPageIndex));
+#endif
+
// Final two glyphs
Gfx::Pics glyphs("STORYPIC", 262);
@@ -130,9 +148,12 @@ void Story::draw() {
bool Story::msgAction(const ActionMessage &msg) {
if (msg._action == KEYBIND_ESCAPE || _yp == MAX_Y)
done();
- else if (!_scrolling)
+ else if (!_scrolling) {
+#ifdef USE_TTS
+ sayText(_ttsMessage.substr(_secondPageIndex));
+#endif
_scrolling = true;
- else
+ } else
_yp = MAX_Y;
return true;
diff --git a/engines/got/views/story.h b/engines/got/views/story.h
index e528ef0ec08..7d0f302b2e8 100644
--- a/engines/got/views/story.h
+++ b/engines/got/views/story.h
@@ -35,6 +35,11 @@ private:
void done();
+#ifdef USE_TTS
+ Common::String _ttsMessage;
+ uint _secondPageIndex = 0;
+#endif
+
public:
Story() : View("Story") {}
virtual ~Story() {}
diff --git a/engines/got/views/view.cpp b/engines/got/views/view.cpp
index df75a31e293..3d519781b93 100644
--- a/engines/got/views/view.cpp
+++ b/engines/got/views/view.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/config-manager.h"
#include "got/views/view.h"
#include "got/gfx/palette.h"
#include "got/vars.h"
@@ -120,5 +121,52 @@ void View::fadeIn(const byte *pal) {
Gfx::fadeIn(pal);
}
+#ifdef USE_TTS
+
+void View::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 options in selection menus are voiced. 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 && ConfMan.getBool("tts_enabled") && _previousSaid != text) {
+ Common::String ttsMessage;
+
+ for (uint i = 0; i < text.size(); ++i) {
+ // Some text is enclosed in < and >, which causes the text to not be voiced by TTS if they aren't replaced
+ if (text[i] == '<' || text[i] == '>') {
+ ttsMessage += ", ";
+ continue;
+ }
+
+ // Ignore color changing characters
+ if (text[i] == '~' && i < text.size() - 1 && Common::isXDigit(text[i + 1])) {
+ i++;
+ continue;
+ }
+
+ // Replace single newlines with spaces to make voicing of dialog smoother. If there are two or more newlines in a row,
+ // then the pieces of text are most likely supposed to be voiced as separate sentences, so keep the newlines in
+ // that case
+ if (text[i] == '\n') {
+ if (i < text.size() - 1 && text[i + 1] == '\n') {
+ i++;
+ } else {
+ ttsMessage += ' ';
+ continue;
+ }
+ }
+
+ ttsMessage += text[i];
+ }
+
+ ttsMan->say(ttsMessage, action, Common::CodePage::kDos850);
+ _previousSaid = text;
+ }
+}
+
+#endif
+
} // namespace Views
} // namespace Got
diff --git a/engines/got/views/view.h b/engines/got/views/view.h
index b0e213f63db..d70e73967df 100644
--- a/engines/got/views/view.h
+++ b/engines/got/views/view.h
@@ -22,6 +22,7 @@
#ifndef GOT_VIEWS_VIEW_H
#define GOT_VIEWS_VIEW_H
+#include "common/text-to-speech.h"
#include "got/events.h"
#include "got/gfx/gfx_chunks.h"
@@ -67,6 +68,10 @@ protected:
void fadeOut();
void fadeIn(const byte *pal = nullptr);
+#ifdef USE_TTS
+ void sayText(const Common::String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT);
+#endif
+
public:
View(const Common::String &name, UIElement *uiParent) : UIElement(name, uiParent) {}
View(const Common::String &name) : UIElement(name) {}
More information about the Scummvm-git-logs
mailing list