[Scummvm-git-logs] scummvm master -> a78a3102b84eeac1465089fc635650e7b33bb526
sev-
noreply at scummvm.org
Mon Aug 18 10:27:23 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:
a78a3102b8 AGI: Add text-to-speech (TTS)
Commit: a78a3102b84eeac1465089fc635650e7b33bb526
https://github.com/scummvm/scummvm/commit/a78a3102b84eeac1465089fc635650e7b33bb526
Author: ellm135 (ellm13531 at gmail.com)
Date: 2025-08-18T12:27:20+02:00
Commit Message:
AGI: Add text-to-speech (TTS)
Changed paths:
engines/agi/agi.cpp
engines/agi/agi.h
engines/agi/cycle.cpp
engines/agi/detection.h
engines/agi/detection_tables.h
engines/agi/inv.cpp
engines/agi/menu.cpp
engines/agi/metaengine.cpp
engines/agi/op_cmd.cpp
engines/agi/op_test.cpp
engines/agi/saveload.cpp
engines/agi/systemui.cpp
engines/agi/text.cpp
engines/agi/text.h
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index 7280068fa63..95d4ad4082e 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -470,6 +470,13 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_artificialDelayCurrentRoom = 0;
_artificialDelayCurrentPicture = 0;
+
+#ifdef USE_TTS
+ _previousDisplayRow = -1;
+ _queueNextText = false;
+ _voiceClock = true;
+ _replaceDisplayNewlines = true;
+#endif
}
void AgiEngine::initialize() {
@@ -547,6 +554,26 @@ void AgiEngine::initialize() {
// finally set up actual VM opcodes, because we should now have figured out the right AGI version
setupOpCodes(getVersion());
+
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan) {
+ ttsMan->enable(ConfMan.getBool("tts_enabled"));
+ ttsMan->setLanguage(ConfMan.get("language"));
+ }
+
+ switch (getLanguage()) {
+ case Common::HE_ISR:
+ _ttsEncoding = Common::CodePage::kWindows1255;
+ break;
+ case Common::RU_RUS:
+ _ttsEncoding = Common::CodePage::kDos866;
+ break;
+ default:
+ _ttsEncoding = Common::CodePage::kDos850;
+ break;
+ }
+#endif
}
bool AgiEngine::promptIsEnabled() {
@@ -615,6 +642,41 @@ void AgiEngine::syncSoundSettings() {
applyVolumeToMixer();
}
+#ifdef USE_TTS
+
+void AgiEngine::sayText(const Common::String &text, Common::TextToSpeechManager::Action action, bool checkPreviousSaid) {
+ if (text.empty()) {
+ return;
+ }
+
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ConfMan.getBool("tts_enabled") && (!checkPreviousSaid || _previousSaid != text)) {
+ Common::String ttsMessage = text;
+
+ if (_replaceDisplayNewlines) {
+ ttsMessage.replace('\n', ' ');
+ }
+
+ ttsMessage.replace('<', ' ');
+ ttsMessage.replace('=', ' ');
+ ttsMan->say(ttsMessage, action, _ttsEncoding);
+ _previousSaid = text;
+ }
+}
+
+void AgiEngine::stopTextToSpeech(bool clearPreviousSaid) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
+ ttsMan->stop();
+
+ if (clearPreviousSaid) {
+ _previousSaid.clear();
+ }
+ }
+}
+
+#endif
+
// WORKAROUND:
// Sometimes Sierra printed some text on the screen and did a room change immediately afterwards expecting the
// interpreter to load the data for a bit of time. This of course doesn't happen in our AGI, so we try to
@@ -734,6 +796,20 @@ void AgiEngine::artificialDelayTrigger_NewRoom(int16 newRoomNr) {
}
}
+#ifdef USE_TTS
+ // Delay room changes until TTS is done speaking, which helps TTS keep up with the text on screen
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while (ttsMan && ttsMan->isSpeaking()) {
+ int key = doPollKeyboard();
+
+ if (key != 0) {
+ break;
+ }
+
+ wait(10);
+ }
+#endif
+
_artificialDelayCurrentRoom = newRoomNr;
}
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index ee1f87a24bc..7a8e7ddb151 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -32,6 +32,7 @@
#include "common/stack.h"
#include "common/str.h"
#include "common/system.h"
+#include "common/text-to-speech.h"
#include "engines/engine.h"
@@ -755,6 +756,16 @@ public:
Common::Stack<ImageStackElement> _imageStack;
+#ifdef USE_TTS
+ int16 _previousDisplayRow;
+ Common::String _combinedText;
+ Common::String _previousSaid;
+ bool _queueNextText;
+ bool _voiceClock;
+ bool _replaceDisplayNewlines;
+ Common::CodePage _ttsEncoding;
+#endif
+
void clearImageStack() override;
void recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) override;
@@ -786,6 +797,12 @@ private:
public:
void syncSoundSettings() override;
+#ifdef USE_TTS
+ void sayText(const Common::String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::QUEUE,
+ bool checkPreviousSaid = true);
+ void stopTextToSpeech(bool clearPreviousSaid = true);
+#endif
+
public:
void decrypt(uint8 *mem, int len);
uint16 processAGIEvents();
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 3f49e6dcc68..81421b47a43 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -195,7 +195,7 @@ void AgiEngine::interpretCycle() {
screenObjEgo->direction = getVar(VM_VAR_EGO_DIRECTION);
if (getVar(VM_VAR_SCORE) != oldScore || getFlag(VM_FLAG_SOUND_ON) != oldSound)
- _game._vm->_text->statusDraw();
+ _game._vm->_text->statusDraw(getVar(VM_VAR_SCORE) != oldScore, getFlag(VM_FLAG_SOUND_ON) != oldSound);
setVar(VM_VAR_BORDER_TOUCH_OBJECT, 0);
setVar(VM_VAR_BORDER_CODE, 0);
diff --git a/engines/agi/detection.h b/engines/agi/detection.h
index d9c26bdbd36..4afab227a08 100644
--- a/engines/agi/detection.h
+++ b/engines/agi/detection.h
@@ -46,6 +46,7 @@ struct AGIGameDescription {
#define GAMEOPTION_COPY_PROTECTION GUIO_GAMEOPTIONS7
#define GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE GUIO_GAMEOPTIONS8
#define GAMEOPTION_PCJR_SN76496_16BIT GUIO_GAMEOPTIONS9
+#define GAMEOPTION_TTS GUIO_GAMEOPTIONS10
} // End of namespace Agi
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index 2ccd5d3f81c..05d88d21eba 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -23,15 +23,15 @@
namespace Agi {
-#define GAMEOPTIONS_DEFAULT GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
-#define GAMEOPTIONS_DEFAULT_CP GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_COPY_PROTECTION)
-#define GAMEOPTIONS_AMIGA GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
-#define GAMEOPTIONS_AMIGA_CP GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_COPY_PROTECTION)
-#define GAMEOPTIONS_APPLE2GS GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW, GAMEOPTION_APPLE2GS_ADD_SPEED_MENU)
-#define GAMEOPTIONS_APPLE2GS_CP GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW, GAMEOPTION_APPLE2GS_ADD_SPEED_MENU, GAMEOPTION_COPY_PROTECTION)
-#define GAMEOPTIONS_VGA GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GUIO_RENDERVGA)
-#define GAMEOPTIONS_FANMADE_MOUSE GUIO5(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_PCJR_SN76496_16BIT)
-#define GAMEOPTIONS_FANMADE_MOUSE_VGA GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_PCJR_SN76496_16BIT,GUIO_RENDERVGA)
+#define GAMEOPTIONS_DEFAULT GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_TTS)
+#define GAMEOPTIONS_DEFAULT_CP GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_COPY_PROTECTION,GAMEOPTION_TTS)
+#define GAMEOPTIONS_AMIGA GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_TTS)
+#define GAMEOPTIONS_AMIGA_CP GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_COPY_PROTECTION,GAMEOPTION_TTS)
+#define GAMEOPTIONS_APPLE2GS GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW, GAMEOPTION_APPLE2GS_ADD_SPEED_MENU,GAMEOPTION_TTS)
+#define GAMEOPTIONS_APPLE2GS_CP GUIO8(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW, GAMEOPTION_APPLE2GS_ADD_SPEED_MENU, GAMEOPTION_COPY_PROTECTION,GAMEOPTION_TTS)
+#define GAMEOPTIONS_VGA GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_MOUSE,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GUIO_RENDERVGA,GAMEOPTION_TTS)
+#define GAMEOPTIONS_FANMADE_MOUSE GUIO6(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_PCJR_SN76496_16BIT,GAMEOPTION_TTS)
+#define GAMEOPTIONS_FANMADE_MOUSE_VGA GUIO7(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_ENABLE_PREDICTIVE_FOR_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW,GAMEOPTION_PCJR_SN76496_16BIT,GUIO_RENDERVGA,GAMEOPTION_TTS)
#define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp,guioptions) { \
{ \
diff --git a/engines/agi/inv.cpp b/engines/agi/inv.cpp
index bc336c0309f..c1cbc997a27 100644
--- a/engines/agi/inv.cpp
+++ b/engines/agi/inv.cpp
@@ -109,6 +109,9 @@ void InventoryMgr::drawAll() {
_text->charPos_Set(0, 11);
_text->displayText(_systemUI->getInventoryTextYouAreCarrying());
+#ifdef USE_TTS
+ _vm->sayText(_systemUI->getInventoryTextYouAreCarrying(), Common::TextToSpeechManager::INTERRUPT, false);
+#endif
for (int16 inventoryNr = 0; inventoryNr < inventoryCount; inventoryNr++) {
drawItem(inventoryNr);
@@ -118,8 +121,23 @@ void InventoryMgr::drawAll() {
void InventoryMgr::drawItem(int16 itemNr) {
if (itemNr == _activeItemNr) {
_text->charAttrib_Set(15, 0);
+
+#ifdef USE_TTS
+ if (_vm->_queueNextText) {
+ _vm->sayText(_array[itemNr].name, Common::TextToSpeechManager::QUEUE, false);
+ _vm->_queueNextText = false;
+ } else {
+ _vm->sayText(_array[itemNr].name, Common::TextToSpeechManager::INTERRUPT, false);
+ }
+#endif
} else {
_text->charAttrib_Set(0, 15);
+
+#ifdef USE_TTS
+ if (_activeItemNr == -1) {
+ _vm->sayText(_array[itemNr].name, Common::TextToSpeechManager::QUEUE, false);
+ }
+#endif
}
_text->charPos_Set(_array[itemNr].row, _array[itemNr].column);
@@ -143,15 +161,24 @@ void InventoryMgr::show() {
_activeItemNr = -1; // so that none is shown as active
}
+#ifdef USE_TTS
+ _vm->_queueNextText = true;
+#endif
drawAll();
_text->charAttrib_Set(0, 15);
if (selectItems) {
_text->charPos_Set(24, 2);
_text->displayText(_systemUI->getInventoryTextSelectItems());
+#ifdef USE_TTS
+ _vm->sayText(_systemUI->getInventoryTextSelectItems());
+#endif
} else {
_text->charPos_Set(24, 4);
_text->displayText(_systemUI->getInventoryTextReturnToGame());
+#ifdef USE_TTS
+ _vm->sayText(_systemUI->getInventoryTextReturnToGame());
+#endif
}
if (selectItems) {
@@ -168,6 +195,9 @@ void InventoryMgr::show() {
// nothing was selected
_vm->setVar(VM_VAR_SELECTED_INVENTORY_ITEM, 0xff);
}
+#ifdef USE_TTS
+ _vm->stopTextToSpeech();
+#endif
} else {
// no selection is supposed to be possible, just wait for key and exit
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
index 17f229abb75..922c2b86dd4 100644
--- a/engines/agi/menu.cpp
+++ b/engines/agi/menu.cpp
@@ -355,6 +355,9 @@ void GfxMenu::execute() {
if (_drawnMenuNr >= 0) {
if (viaKeyboard) {
+#ifdef USE_TTS
+ _vm->_queueNextText = true;
+#endif
drawMenu(_drawnMenuNr, _array[_drawnMenuNr]->selectedItemNr);
}
if (viaMouse) {
@@ -410,6 +413,9 @@ void GfxMenu::drawMenuName(int16 menuNr, bool inverted) {
if (!inverted) {
_text->charAttrib_Set(0, _text->calculateTextBackground(15));
} else {
+#ifdef USE_TTS
+ _vm->sayText(menuEntry->text.c_str(), Common::TextToSpeechManager::INTERRUPT, false);
+#endif
_text->charAttrib_Set(15, _text->calculateTextBackground(0));
}
@@ -428,6 +434,15 @@ void GfxMenu::drawItemName(int16 itemNr, bool inverted) {
if (!inverted) {
_text->charAttrib_Set(0, _text->calculateTextBackground(15));
} else {
+#ifdef USE_TTS
+ if (_vm->_queueNextText) {
+ _vm->sayText(itemEntry->text.c_str(), Common::TextToSpeechManager::QUEUE, false);
+ _vm->_queueNextText = false;
+ } else {
+ _vm->sayText(itemEntry->text.c_str(), Common::TextToSpeechManager::INTERRUPT, false);
+ }
+#endif
+
_text->charAttrib_Set(15, _text->calculateTextBackground(0));
}
@@ -549,6 +564,9 @@ void GfxMenu::keyPress(uint16 newKey) {
}
if (newMenuNr != _drawnMenuNr) {
+#ifdef USE_TTS
+ _vm->_queueNextText = true;
+#endif
removeActiveMenu(_drawnMenuNr);
_drawnMenuNr = newMenuNr;
drawMenu(_drawnMenuNr, _array[_drawnMenuNr]->selectedItemNr);
diff --git a/engines/agi/metaengine.cpp b/engines/agi/metaengine.cpp
index e0651f9522a..deecb1c151e 100644
--- a/engines/agi/metaengine.cpp
+++ b/engines/agi/metaengine.cpp
@@ -213,6 +213,20 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+#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/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 2f918270495..464e19709f7 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -668,12 +668,29 @@ void cmdCloseDialogue(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
}
void cmdCloseWindow(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
+#ifdef USE_TTS
+ // Delay closing the text window until TTS is finished
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while (ttsMan && ttsMan->isSpeaking()) {
+ int key = vm->doPollKeyboard();
+
+ if (key != 0) {
+ break;
+ }
+
+ vm->wait(10);
+ }
+#endif
+
vm->_text->closeWindow();
}
void cmdStatusLineOn(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
TextMgr *text = vm->_text;
+#ifdef USE_TTS
+ vm->_voiceClock = true;
+#endif
text->statusEnable();
text->statusDraw();
}
@@ -1826,6 +1843,9 @@ void cmdTextScreen(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
void cmdGraphics(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
debugC(4, kDebugLevelScripts, "switching to graphics mode");
+#ifdef USE_TTS
+ vm->stopTextToSpeech();
+#endif
vm->redrawScreen();
}
@@ -2012,6 +2032,10 @@ void cmdGetString(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
leadInTextPtr = textMgr->stringWordWrap(leadInTextPtr, 40); // ?? not absolutely sure
textMgr->displayText(leadInTextPtr);
+#ifdef USE_TTS
+ vm->sayText(vm->_combinedText, Common::TextToSpeechManager::INTERRUPT);
+ vm->_combinedText.clear();
+#endif
}
vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING);
@@ -2049,6 +2073,10 @@ void cmdGetNum(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
leadInTextPtr = textMgr->stringWordWrap(leadInTextPtr, 40); // ?? not absolutely sure
textMgr->displayText(leadInTextPtr);
+#ifdef USE_TTS
+ vm->sayText(vm->_combinedText, Common::TextToSpeechManager::INTERRUPT);
+ vm->_combinedText.clear();
+#endif
}
textMgr->inputEditOff();
@@ -2436,6 +2464,13 @@ int AgiEngine::runLogic(int16 logicNr) {
debugC(2, kDebugLevelScripts, "%sreturn() // Logic %d", st, logicNr);
debugC(2, kDebugLevelScripts, "=================");
+#ifdef USE_TTS
+ sayText(_combinedText, Common::TextToSpeechManager::QUEUE, true);
+ _replaceDisplayNewlines = true;
+ _combinedText.clear();
+ _previousDisplayRow = -1;
+#endif
+
// if (vm->getVersion() < 0x2000) {
// if (logic_index < state->max_logics) {
// n = state->logic_list[++logic_index];
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
index f290f22cb14..bc843b58b1c 100644
--- a/engines/agi/op_test.cpp
+++ b/engines/agi/op_test.cpp
@@ -122,6 +122,9 @@ void condHaveKey(AgiGame *state, AgiEngine *vm, uint8 *p) {
// Only check for key when there is not already one set by scripts
if (vm->getVar(VM_VAR_KEY)) {
state->testResult = true;
+#ifdef USE_TTS
+ vm->stopTextToSpeech(false);
+#endif
return;
}
// we are not really an inner loop, but we stop processAGIEvents() from doing regular cycle work by setting it up
@@ -132,6 +135,9 @@ void condHaveKey(AgiGame *state, AgiEngine *vm, uint8 *p) {
debugC(5, kDebugLevelInput, "keypress = %02x", key);
vm->setVar(VM_VAR_KEY, key);
state->testResult = true;
+#ifdef USE_TTS
+ vm->stopTextToSpeech(false);
+#endif
return;
}
state->testResult = false;
@@ -278,6 +284,11 @@ bool AgiEngine::testCompareStrings(uint8 s1, uint8 s2) {
}
bool AgiEngine::testController(uint8 cont) {
+#ifdef USE_TTS
+ if (_game.controllerOccurred[cont]) {
+ stopTextToSpeech(false);
+ }
+#endif
return _game.controllerOccurred[cont];
}
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index d5f8a2b1dc9..88f5b468967 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -746,7 +746,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
_sprites->drawAllSpriteLists();
_picture->showPictureWithTransition();
_game.pictureShown = true;
- _text->statusDraw();
+ _text->statusDraw(true, true);
_text->promptRedraw();
// copy everything over (we should probably only copy over the remaining parts of the screen w/o play screen
diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp
index 2b981e21a43..40cd61af338 100644
--- a/engines/agi/systemui.cpp
+++ b/engines/agi/systemui.cpp
@@ -260,6 +260,9 @@ bool SystemUI::askForCommand(Common::String &commandText) {
bool previousEditState = _text->inputGetEditStatus();
byte previousEditCursor = _text->inputGetCursorChar();
+#ifdef USE_TTS
+ _vm->stopTextToSpeech();
+#endif
_text->drawMessageBox(_textEnterCommand, 0, 36, true);
_text->inputEditOn();
@@ -284,7 +287,7 @@ bool SystemUI::askForCommand(Common::String &commandText) {
_text->inputEditOff();
}
- _text->closeWindow();
+ _text->closeWindow(false);
if (!_text->stringWasEntered()) {
// User cancelled? exit now
@@ -454,6 +457,9 @@ int16 SystemUI::askForSavedGameSlot(const char *slotListText) {
_text->drawMessageBox(slotListText, messageBoxHeight, 34, true);
drawSavedGameSlots();
+#ifdef USE_TTS
+ _vm->_queueNextText = true;
+#endif
drawSavedGameSlotSelector(true);
_vm->cycleInnerLoopActive(CYCLE_INNERLOOP_SYSTEMUI_SELECTSAVEDGAMESLOT);
@@ -783,6 +789,15 @@ void SystemUI::drawSavedGameSlotSelector(bool active) {
}
if (active) {
_text->displayTextInsideWindow(arrow, windowRow, column);
+
+#ifdef USE_TTS
+ if (_vm->_queueNextText) {
+ _vm->sayText(_savedGameArray[_savedGameSelectedSlotNr].displayText, Common::TextToSpeechManager::QUEUE);
+ _vm->_queueNextText = false;
+ } else {
+ _vm->sayText(_savedGameArray[_savedGameSelectedSlotNr].displayText, Common::TextToSpeechManager::INTERRUPT);
+ }
+#endif
} else {
_text->displayTextInsideWindow(" ", windowRow, column);
}
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 06773598849..bb047f5e44b 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -248,6 +248,26 @@ void TextMgr::display(int16 textNr, int16 textRow, int16 textColumn) {
logicTextPtr = _vm->_game._curLogic->texts[textNr - 1];
processedTextPtr = stringPrintf(logicTextPtr);
processedTextPtr = stringWordWrap(processedTextPtr, 40);
+
+#ifdef USE_TTS
+ if (!_vm->_game.gfxMode) {
+ _vm->sayText(processedTextPtr);
+ } else if (_vm->_game.curLogicNr != 0) {
+ if (_vm->_previousDisplayRow != -1 && textRow != _vm->_previousDisplayRow + 1) {
+ _vm->_combinedText += "\n";
+ _vm->_replaceDisplayNewlines = false;
+ }
+
+ _vm->_combinedText += " ";
+ _vm->_combinedText += processedTextPtr;
+ } else if (_vm->_voiceClock) {
+ _vm->sayText(processedTextPtr);
+ _vm->_voiceClock = false;
+ }
+
+ _vm->_previousDisplayRow = textRow;
+#endif
+
displayText(processedTextPtr);
// Signal, that non-blocking text is shown at the moment
@@ -399,7 +419,8 @@ bool TextMgr::messageBox(const char *textPtr) {
_vm->inGameTimerUpdate();
if (windowTimer > 0) {
- if (_vm->inGameTimerGetPassedCycles() >= windowTimer) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (_vm->inGameTimerGetPassedCycles() >= windowTimer && (!ttsMan || !ttsMan->isSpeaking())) {
// Timer reached, close automatically
_vm->cycleInnerLoopInactive();
}
@@ -510,6 +531,9 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want
_reset_Column = _messageState.textPos.column;
displayText(processedTextPtr);
+#ifdef USE_TTS
+ _vm->sayText(processedTextPtr);
+#endif
_reset_Column = 0;
charPos_Pop();
@@ -546,7 +570,7 @@ bool TextMgr::isMouseWithinMessageBox() {
return false;
}
-void TextMgr::closeWindow() {
+void TextMgr::closeWindow(bool ttsStopSpeech) {
if (_messageState.window_Active) {
// Close the window by copying the game screen to the display screen.
// Our graphics code was designed with the assumption that the window would
@@ -559,6 +583,13 @@ void TextMgr::closeWindow() {
const int16 y = MAX<int16>(0, _messageState.backgroundPos_y);
_gfx->render_Block(x, y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height);
}
+
+#ifdef USE_TTS
+ if (ttsStopSpeech && (_messageState.dialogue_Open || _messageState.window_Active)) {
+ _vm->stopTextToSpeech();
+ }
+#endif
+
_messageState.dialogue_Open = false;
_messageState.window_Active = false;
}
@@ -580,7 +611,7 @@ bool TextMgr::statusEnabled() {
return _statusEnabled;
}
-void TextMgr::statusDraw() {
+void TextMgr::statusDraw(bool ttsVoiceScore, bool ttsVoiceSound) {
char *statusTextPtr = nullptr;
charAttrib_Push();
@@ -597,6 +628,12 @@ void TextMgr::statusDraw() {
charPos_Set(_statusRow, FONT_COLUMN_CHARACTERS - Common::strnlen(statusTextPtr, FONT_COLUMN_CHARACTERS) - 1);
displayText(statusTextPtr);
+#ifdef USE_TTS
+ if (ttsVoiceScore) {
+ _vm->sayText(statusTextPtr, Common::TextToSpeechManager::INTERRUPT);
+ }
+#endif
+
if (!_vm->isLanguageRTL())
charPos_Set(_statusRow, 30);
else
@@ -607,6 +644,12 @@ void TextMgr::statusDraw() {
statusTextPtr = stringPrintf(_systemUI->getStatusTextSoundOff());
}
displayText(statusTextPtr);
+
+#ifdef USE_TTS
+ if (ttsVoiceSound) {
+ _vm->sayText(statusTextPtr);
+ }
+#endif
}
charPos_Pop();
@@ -632,6 +675,10 @@ void TextMgr::clearBlock(int16 row_Upper, int16 column_Upper, int16 row_Lower, i
charPos_Clip(row_Upper, column_Upper);
charPos_Clip(row_Lower, column_Lower);
+#ifdef USE_TTS
+ _vm->_combinedText.clear();
+#endif
+
int16 x = column_Upper;
int16 y = row_Upper;
int16 width = (column_Lower + 1 - column_Upper);
@@ -774,6 +821,9 @@ void TextMgr::promptKeyPress(uint16 newKey) {
promptRememberForAutoComplete(true);
memcpy(&_promptPrevious, &_prompt, sizeof(_prompt));
+#ifdef USE_TTS
+ _vm->sayText((char *)&_prompt);
+#endif
// parse text
_vm->_words->parseUsingDictionary((char *)&_prompt);
@@ -1010,6 +1060,9 @@ void TextMgr::stringKeyPress(uint16 newKey) {
stringRememberForAutoComplete(true);
_inputStringEntered = true;
+#ifdef USE_TTS
+ _vm->sayText((const char *)_inputString);
+#endif
_vm->cycleInnerLoopInactive(); // exit GetString-loop
break;
diff --git a/engines/agi/text.h b/engines/agi/text.h
index c5b5d724b52..ad94a94e84a 100644
--- a/engines/agi/text.h
+++ b/engines/agi/text.h
@@ -138,7 +138,7 @@ public:
void drawMessageBox(const char *textPtr, int16 forcedHeight = 0, int16 wantedWidth = 0, bool forcedWidth = false);
void getMessageBoxInnerDisplayDimensions(int16 &x, int16 &y, int16 &width, int16 &height);
bool isMouseWithinMessageBox();
- void closeWindow();
+ void closeWindow(bool ttsStopSpeech = true);
void statusRow_Set(int16 row);
int16 statusRow_Get();
@@ -147,7 +147,7 @@ public:
void statusDisable();
bool statusEnabled();
- void statusDraw();
+ void statusDraw(bool ttsVoiceScore = false, bool ttsVoiceSound = false);
void statusClear();
bool _statusEnabled;
More information about the Scummvm-git-logs
mailing list