[Scummvm-git-logs] scummvm master -> a95eb645bac9e852270db00ba7b5ac0c4c2b2154
sev-
noreply at scummvm.org
Sat Aug 9 19:09:39 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:
a95eb645ba EFH: Add text-to-speech (TTS)
Commit: a95eb645bac9e852270db00ba7b5ac0c4c2b2154
https://github.com/scummvm/scummvm/commit/a95eb645bac9e852270db00ba7b5ac0c4c2b2154
Author: ellm135 (ellm13531 at gmail.com)
Date: 2025-08-09T21:09:36+02:00
Commit Message:
EFH: Add text-to-speech (TTS)
Changed paths:
A engines/efh/detection.h
engines/efh/detection.cpp
engines/efh/efh.cpp
engines/efh/efh.h
engines/efh/fight.cpp
engines/efh/init.cpp
engines/efh/menu.cpp
engines/efh/metaengine.cpp
engines/efh/script.cpp
engines/efh/utils.cpp
diff --git a/engines/efh/detection.cpp b/engines/efh/detection.cpp
index 12b092989f3..8a1d569f389 100644
--- a/engines/efh/detection.cpp
+++ b/engines/efh/detection.cpp
@@ -22,6 +22,7 @@
#include "base/plugins.h"
#include "engines/advancedDetector.h"
+#include "efh/detection.h"
#include "efh/efh.h"
namespace Efh {
@@ -40,7 +41,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
// Escape From Hell English
{
@@ -48,7 +49,7 @@ static const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
AD_TABLE_END_MARKER
};
diff --git a/engines/efh/detection.h b/engines/efh/detection.h
new file mode 100644
index 00000000000..c7c3fc7e23e
--- /dev/null
+++ b/engines/efh/detection.h
@@ -0,0 +1,27 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef EFH_DETECTION_H
+#define EFH_DETECTION_H
+
+#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
+
+#endif // EFH_DETECTION_H
diff --git a/engines/efh/efh.cpp b/engines/efh/efh.cpp
index 7d820e7a498..e0064d2a593 100644
--- a/engines/efh/efh.cpp
+++ b/engines/efh/efh.cpp
@@ -71,6 +71,12 @@ Common::Error EfhEngine::run() {
// Setup mixer
syncSoundSettings();
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan != nullptr) {
+ ttsMan->enable(ConfMan.getBool("tts_enabled"));
+ ttsMan->setLanguage(ConfMan.get("language"));
+ }
+
// Sometimes a ghost key event stops the intro, so we ass a short delay and purge the keyboard events
_system->delayMillis(100);
_system->getEventManager()->purgeKeyboardEvents();
@@ -149,7 +155,7 @@ Common::Error EfhEngine::run() {
}
void EfhEngine::handleEvents() {
- Common::KeyCode retVal = getLastCharAfterAnimCount(4);
+ Common::KeyCode retVal = getLastCharAfterAnimCount(4, false);
switch (_customAction) {
case kActionSave:
@@ -226,6 +232,8 @@ void EfhEngine::handleEvents() {
}
void EfhEngine::handleActionSave() {
+ sayText("Are you sure you want to save?", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+
for (uint counter = 0; counter < 2; ++counter) {
clearBottomTextZone(0);
displayCenteredString("Are You Sure You Want To Save?", 24, 296, 160);
@@ -235,10 +243,12 @@ void EfhEngine::handleActionSave() {
Common::KeyCode input = waitForKey();
if (input == Common::KEYCODE_y) {
displayMenuAnswerString("-> Yes <-", 24, 296, 169);
+ sayText("Yes", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
getInput(2);
saveGameDialog();
} else {
displayMenuAnswerString("-> No!!! <-", 24, 296, 169);
+ sayText("No", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
getInput(2);
}
clearBottomTextZone_2(0);
@@ -246,6 +256,8 @@ void EfhEngine::handleActionSave() {
}
void EfhEngine::handleActionLoad() {
+ sayText("Are you sure you want to load?", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+
for (uint counter = 0; counter < 2; ++counter) {
clearBottomTextZone(0);
displayCenteredString("Are You Sure You Want To Load?", 24, 296, 160);
@@ -255,10 +267,12 @@ void EfhEngine::handleActionLoad() {
Common::KeyCode input = waitForKey();
if (input == Common::KEYCODE_y) {
displayMenuAnswerString("-> Yes <-", 24, 296, 169);
+ sayText("Yes", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
getInput(2);
loadGameDialog();
} else {
displayMenuAnswerString("-> No!!! <-", 24, 296, 169);
+ sayText("No", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
getInput(2);
}
clearBottomTextZone_2(0);
@@ -411,8 +425,12 @@ void EfhEngine::initEngine() {
readFileToBuffer(fileName, _titleSong);
Common::KeyCode lastInput = Common::KEYCODE_INVALID;
- if (_loadSaveSlot == -1)
+ if (_loadSaveSlot == -1) {
+ sayText("Richard and Alan's Escape from Hell\nCopyright 1990 Electronic Arts\n"
+ "By Richard L. Seaborne with Alan J. Murphy", kNoRestriction);
lastInput = playSong(_titleSong);
+ stopTextToSpeech();
+ }
if (lastInput != Common::KEYCODE_ESCAPE && _loadSaveSlot == -1)
playIntro();
@@ -427,6 +445,7 @@ void EfhEngine::initEngine() {
loadEfhGame();
resetGame();
} else {
+ _sayLowStatusScreen = true;
loadGameState(_loadSaveSlot);
_loadSaveSlot = -1;
}
@@ -680,15 +699,20 @@ void EfhEngine::displayLowStatusScreen(bool flag) {
Common::String buffer = _npcBuf[charId]._name;
setTextPos(16, textPosY);
displayStringAtTextPos(buffer);
+ sayText("Name: " + buffer, kLowStatusMenu);
buffer = Common::String::format("%d", getEquipmentDefense(charId));
displayCenteredString(buffer, 104, 128, textPosY);
+ sayText("Defense: " + buffer, kLowStatusMenu);
buffer = Common::String::format("%d", _npcBuf[charId]._hitPoints);
displayCenteredString(buffer, 144, 176, textPosY);
+ sayText("HP: " + buffer, kLowStatusMenu);
buffer = Common::String::format("%d", _npcBuf[charId]._maxHP);
displayCenteredString(buffer, 192, 224, textPosY);
+ sayText("Max HP: " + buffer, kLowStatusMenu);
if (_npcBuf[charId]._hitPoints <= 0) {
displayCenteredString("* DEAD *", 225, 302, textPosY);
+ sayText("Dead", kLowStatusMenu);
continue;
}
@@ -713,11 +737,19 @@ void EfhEngine::displayLowStatusScreen(bool flag) {
}
displayCenteredString(_nameBuffer, 225, 302, textPosY);
+
+ if (_teamChar[i]._status._type == kEfhStatusNormal) {
+ sayText("Weapon: " + _nameBuffer, kLowStatusMenu);
+ } else {
+ sayText(_nameBuffer, kLowStatusMenu);
+ }
}
}
if (counter == 0 && flag)
displayFctFullScreen();
+
+ _sayLowStatusScreen = false;
}
}
@@ -808,6 +840,8 @@ void EfhEngine::handleWinSequence() {
loadImageSet(64, winSeqBuf3, winSeqSubFilesArray1, decompBuffer);
loadImageSet(65, winSeqBuf4, winSeqSubFilesArray2, decompBuffer);
+ sayText("Come back soon", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+
for (uint counter = 0; counter < 2; ++counter) {
displayRawDataAtPos(winSeqSubFilesArray1[0], 0, 0);
displayRawDataAtPos(winSeqSubFilesArray2[0], 136, 48);
@@ -931,6 +965,7 @@ int16 EfhEngine::handleCharacterJoining() {
if (counter == 0)
displayFctFullScreen();
}
+ sayText("Replace Who?", kNoRestriction);
int16 charId = chooseCharacterToReplace();
for (uint counter = 0; counter < 2; ++counter) {
@@ -942,6 +977,7 @@ int16 EfhEngine::handleCharacterJoining() {
if (charId == 0x1B) // Escape Keycode
return -1;
+ sayText(_npcBuf[_teamChar[charId]._id]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
removeCharacterFromTeam(charId);
return 2;
}
@@ -972,6 +1008,8 @@ void EfhEngine::drawText(uint8 *srcPtr, int16 posX, int16 posY, int16 maxX, int1
}
}
+ sayText(_messageToBePrinted, kNoRestriction, Common::TextToSpeechManager::QUEUE_NO_REPEAT);
+
script_parse(_messageToBePrinted, posX, posY, maxX, maxY, flag);
}
@@ -985,6 +1023,7 @@ void EfhEngine::displayMiddleLeftTempText(uint8 *impArray, bool flag) {
if (impArray != nullptr) {
_tempTextDelay = 4;
_tempTextPtr = impArray;
+ stopTextToSpeech();
drawText(impArray, 17, 115, 110, 133, false);
}
if (counter == 0 && flag)
@@ -1027,7 +1066,7 @@ void EfhEngine::transitionMap(int16 centerX, int16 centerY) {
drawScreen();
}
- getLastCharAfterAnimCount(3);
+ getLastCharAfterAnimCount(3, false);
_drawHeroOnMapFl = true;
}
@@ -1221,6 +1260,8 @@ void EfhEngine::goSouthWest() {
void EfhEngine::showCharacterStatus(uint8 character) {
if (_teamChar[character]._id != -1) {
+ stopTextToSpeech();
+ _sayMenu = true;
handleStatusMenu(1, _teamChar[character]._id);
_tempTextPtr = nullptr;
drawGameScreenAndTempText(true);
@@ -1704,8 +1745,10 @@ void EfhEngine::handleMapMonsterMoves() {
} while (!monsterMovedFl && retryCounter > 0 && !shouldQuit());
}
- if (attackMonsterId != -1)
+ if (attackMonsterId != -1) {
+ stopTextToSpeech();
handleFight(attackMonsterId);
+ }
}
bool EfhEngine::checkMapMonsterAvailability(int16 monsterId) {
@@ -1870,6 +1913,7 @@ bool EfhEngine::handleTalk(int16 monsterId, int16 arg2, int16 itemId) {
_enemyNamePt2 = _npcBuf[npcId]._name;
_characterNamePt2 = _npcBuf[_teamChar[charId]._id]._name;
Common::String buffer = Common::String::format("%s asks that %s leave your party.", _enemyNamePt2.c_str(), _characterNamePt2.c_str());
+ sayText(buffer, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
for (uint i = 0; i < 2; ++i) {
clearBottomTextZone(0);
_textColor = 0xE;
@@ -1879,6 +1923,7 @@ bool EfhEngine::handleTalk(int16 monsterId, int16 arg2, int16 itemId) {
if (i == 0)
displayFctFullScreen();
}
+ sayText("Will you do this?", kNoRestriction);
setTextColorRed();
Common::KeyCode input = waitForKey();
if (input == Common::KEYCODE_y) {
@@ -2017,6 +2062,10 @@ void EfhEngine::displayImp1Text(int16 textId) {
displayFctFullScreen();
}
+ if (!_initiatedTalkByMenu) {
+ stopTextToSpeech();
+ }
+
nextTextId = displayBoxWithText(_messageToBePrinted, 1, 1, true);
if (nextTextId != 0xFF)
curTextId = nextTextId;
@@ -2031,7 +2080,15 @@ void EfhEngine::displayImp1Text(int16 textId) {
if (counter == 0)
displayFctFullScreen();
}
+
+ if (textComplete) {
+ sayText("Done", kNoRestriction);
+ } else {
+ sayText("More", kNoRestriction);
+ }
waitForKey();
+ stopTextToSpeech();
+ _initiatedTalkByMenu = false;
}
}
if (nextTextId != 0xFF)
@@ -2115,6 +2172,7 @@ bool EfhEngine::handleInteractionText(int16 mapPosX, int16 mapPosY, int16 charId
// Note: The 2 checks on var6 are useless, as [0x78..0xEF] - 0x78 => [0x00..0x77]
// Note: In the data,all resulting values are between 2 and 14, so it's working
if (var6 >= 0 && var6 <= 0x8B && var6 == itemId && _mapSpecialTiles[_techId][tileId]._triggerValue <= _npcBuf[charId]._activeScore[itemId]) {
+ _initiatedTalkByMenu = true;
displayImp1Text(_mapSpecialTiles[_techId][tileId]._field5_textId);
return true;
}
@@ -2347,6 +2405,7 @@ bool EfhEngine::checkMonsterCollision() {
++mobsterCount;
}
+ _sayMenu = true;
bool endLoop = false;
Common::String buffer;
do {
@@ -2375,50 +2434,62 @@ bool EfhEngine::checkMonsterCollision() {
_textColor = 0xE;
displayCenteredString("Interaction", 24, 296, 152);
displayCenteredString(buffer, 24, 296, 161);
+ sayText("Interaction " + buffer, kMenu, Common::TextToSpeechManager::INTERRUPT);
setTextPos(24, 169);
setTextColorWhite();
displayStringAtTextPos("T");
setTextColorRed();
buffer = Common::String("alk to the ") + dest;
displayStringAtTextPos(buffer);
+ sayText("Talk to the " + dest, kMenu);
setTextPos(24, 178);
setTextColorWhite();
displayStringAtTextPos("A");
setTextColorRed();
buffer = Common::String("ttack the ") + dest;
displayStringAtTextPos(buffer);
+ sayText("Attack the " + dest, kMenu);
setTextPos(198, 169);
setTextColorWhite();
displayStringAtTextPos("S");
setTextColorRed();
displayStringAtTextPos("tatus");
+ sayText("Status", kMenu);
setTextPos(198, 178);
setTextColorWhite();
displayStringAtTextPos("L");
setTextColorRed();
displayStringAtTextPos("eave");
+ sayText("Leave", kMenu);
if (displayCounter == 0)
displayFctFullScreen();
+ _sayMenu = false;
}
Common::KeyCode input = waitForKey();
switch (input) {
case Common::KEYCODE_a: // Attack
+ sayText("Attack", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
handleFight(monsterId);
endLoop = true;
break;
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_l: // Leave
+ sayText("Leave", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
endLoop = true;
break;
case Common::KEYCODE_s: // Status
+ sayText("Status", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ _sayMenu = true;
handleStatusMenu(1, _teamChar[0]._id);
endLoop = true;
_tempTextPtr = nullptr;
drawGameScreenAndTempText(true);
break;
case Common::KEYCODE_t: // Talk
+ sayText("Talk", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ _initiatedTalkByMenu = true;
startTalkMenu(monsterId);
endLoop = true;
break;
@@ -2520,6 +2591,8 @@ void EfhEngine::loadEfhGame() {
_lastMainPlaceId = 0xFFFF;
loadPlacesFile(_fullPlaceId, true);
+
+ _sayLowStatusScreen = true;
}
uint8 EfhEngine::getMapTileInfo(int16 mapPosX, int16 mapPosY) {
@@ -2576,4 +2649,20 @@ void EfhEngine::copyCurrentPlaceToBuffer(int16 id) {
}
}
+void EfhEngine::sayText(const Common::String &text, TTSMenuRestriction menuRestriction, Common::TextToSpeechManager::Action action) const {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ bool speak = menuRestriction == kNoRestriction || (menuRestriction == kLowStatusMenu && _sayLowStatusScreen) || (menuRestriction == kMenu && _sayMenu);
+ if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && speak) {
+ ttsMan->say(text, action, Common::CodePage::kDos850);
+ }
+}
+
+void EfhEngine::stopTextToSpeech() const {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
+ ttsMan->stop();
+ }
+}
+
+
} // End of namespace Efh
diff --git a/engines/efh/efh.h b/engines/efh/efh.h
index 448d856960f..f2ca873538d 100644
--- a/engines/efh/efh.h
+++ b/engines/efh/efh.h
@@ -28,6 +28,7 @@
#include "common/rect.h"
#include "common/events.h"
#include "common/serializer.h"
+#include "common/text-to-speech.h"
#include "engines/advancedDetector.h"
#include "engines/engine.h"
@@ -257,6 +258,12 @@ struct TeamMonster {
void init();
};
+enum TTSMenuRestriction {
+ kNoRestriction,
+ kLowStatusMenu,
+ kMenu
+};
+
enum EFHAction {
kActionNone,
kActionExit,
@@ -480,7 +487,7 @@ private:
void displayColoredMenuBox(int16 minX, int16 minY, int16 maxX, int16 maxY, int16 color);
// Menu
- int16 displayBoxWithText(const Common::String &str, int16 menuType, int16 displayOption, bool displayTeamWindowFl);
+ int16 displayBoxWithText(const Common::String &str, int16 menuType, int16 displayOption, bool displayTeamWindowFl, bool voiceText = true);
bool handleDeathMenu();
void displayCombatMenu(int16 charId);
void displayMenuItemString(int16 menuBoxId, int16 thisBoxId, int16 minX, int16 maxX, int16 minY, const char *str);
@@ -516,13 +523,15 @@ private:
void generateSound5(int repeat);
void generateSound(int16 soundType);
void genericGenerateSound(int16 soundType, int16 repeatCount);
+ void sayText(const Common::String &text, TTSMenuRestriction menuRestriction, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::QUEUE) const;
+ void stopTextToSpeech() const;
// Utils
void decryptImpFile(bool techMapFl);
void loadImageSet(int16 imageSetId, uint8 *buffer, uint8 **subFilesArray, uint8 *destBuffer);
uint32 uncompressBuffer(uint8 *compressedBuf, uint8 *destBuf);
int16 getRandom(int16 maxVal);
- Common::KeyCode getLastCharAfterAnimCount(int16 delay);
+ Common::KeyCode getLastCharAfterAnimCount(int16 delay, bool waitForTTS = true);
Common::KeyCode getInput(int16 delay);
Common::KeyCode waitForKey();
Common::KeyCode handleAndMapInput(bool animFl);
@@ -632,6 +641,10 @@ private:
int16 _menuStatItemArr[15];
int16 _menuDepth;
int16 _menuItemCounter;
+ int16 _selectedMenuBox;
+ bool _sayMenu;
+ bool _sayLowStatusScreen;
+ bool _initiatedTalkByMenu;
TeamChar _teamChar[3];
TeamMonster _teamMonster[5];
diff --git a/engines/efh/fight.cpp b/engines/efh/fight.cpp
index bc1d6bb4fe9..26c219cb7cc 100644
--- a/engines/efh/fight.cpp
+++ b/engines/efh/fight.cpp
@@ -92,6 +92,7 @@ bool EfhEngine::handleFight(int16 monsterId) {
return true;
}
+ _sayMenu = false;
drawCombatScreen(0, false, true);
for (bool mainLoopCond = false; !mainLoopCond && !shouldQuit();) {
@@ -129,7 +130,7 @@ bool EfhEngine::handleFight(int16 monsterId) {
}
computeInitiatives();
- displayBoxWithText("", 2, 1, false);
+ displayBoxWithText("", 2, 1, false, false);
for (uint counter = 0; counter < 8; ++counter) {
int16 monsterGroupIdOrMonsterId = _initiatives[counter]._id;
@@ -1000,10 +1001,15 @@ int16 EfhEngine::determineTeamTarget(int16 charId, int16 unkFied18Val, bool chec
}
do {
+ if (_teamMonster[1]._id != -1) {
+ _sayMenu = true;
+ sayText("Select Monster Group:", kMenu);
+ }
+
for (uint counter = 0; counter < 2; ++counter) {
drawCombatScreen(charId, true, false);
if (_teamMonster[1]._id != -1)
- displayBoxWithText("Select Monster Group:", 3, 0, false);
+ displayBoxWithText("Select Monster Group:", 3, 0, false, false);
if (counter == 0)
displayFctFullScreen();
@@ -1034,7 +1040,9 @@ bool EfhEngine::getTeamAttackRoundPlans() {
debugC(3, kDebugFight, "getTeamAttackRoundPlans");
bool retVal = false;
+ _sayLowStatusScreen = true;
for (int charId = 0; charId < _teamSize; ++charId) {
+ _sayMenu = true;
_teamChar[charId]._lastAction = 0;
if (!isTeamMemberStatusNormal(charId))
continue;
@@ -1044,23 +1052,29 @@ bool EfhEngine::getTeamAttackRoundPlans() {
drawCombatScreen(_teamChar[charId]._id, false, true);
switch (handleAndMapInput(true)) {
case Common::KEYCODE_a: // Attack
+ sayText("Attack", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
_teamChar[charId]._lastAction = 'A';
_teamChar[charId]._nextAttack = determineTeamTarget(_teamChar[charId]._id, 9, true);
if (_teamChar[charId]._nextAttack == -1)
_teamChar[charId]._lastAction = 0;
break;
case Common::KEYCODE_d: // Defend
+ sayText("Defend", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
_teamChar[charId]._lastAction = 'D';
break;
case Common::KEYCODE_h: // Hide
+ sayText("Hide", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
_teamChar[charId]._lastAction = 'H';
break;
case Common::KEYCODE_r: // Run
+ sayText("Run", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
for (int counter2 = 0; counter2 < _teamSize; ++counter2) {
_teamChar[counter2]._lastAction = 'R';
}
return true;
case Common::KEYCODE_s: { // Status
+ sayText("Status", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ _sayMenu = true;
int16 lastInvId = handleStatusMenu(2, _teamChar[charId]._id);
redrawCombatScreenWithTempText(_teamChar[charId]._id);
if (lastInvId >= 999) {
@@ -1122,7 +1136,9 @@ bool EfhEngine::getTeamAttackRoundPlans() {
} break;
case Common::KEYCODE_t: // Terrain
redrawScreenForced();
+ sayText("Terrain", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
waitForKey();
+ _sayMenu = true;
drawCombatScreen(_teamChar[charId]._id, false, true);
break;
default:
@@ -1143,10 +1159,19 @@ void EfhEngine::drawCombatScreen(int16 charId, bool whiteFl, bool drawFl) {
displayCenteredString("Combat", 128, 303, 9);
drawColoredRect(200, 112, 278, 132, 0);
displayCenteredString("'T' for Terrain", 128, 303, 117);
- displayBoxWithText("", 1, 0, false);
+ displayBoxWithText("", 1, 0, false, false);
+ if (!whiteFl) {
+ sayText(_npcBuf[charId]._name, kMenu);
+ }
displayEncounterInfo(whiteFl);
+ if (whiteFl) {
+ _sayMenu = false;
+ } else {
+ sayText("'T' for Terrain", kMenu);
+ }
displayCombatMenu(charId);
displayLowStatusScreen(false);
+ _sayMenu = false;
}
if (counter == 0 && drawFl)
@@ -1334,46 +1359,58 @@ void EfhEngine::displayEncounterInfo(bool whiteFl) {
setTextPos(129, textPosY);
Common::String buffer = Common::String::format("%c)", 'A' + counter);
+ Common::String ttsMessage = buffer + " ";
displayStringAtTextPos(buffer);
setTextColorRed();
int16 var1 = _mapMonsters[_techId][_teamMonster[counter]._id]._possessivePronounSHL6 & 0x3F;
if (var1 <= 0x3D) {
buffer = Common::String::format("%d %s", mobsterCount, kEncounters[_mapMonsters[_techId][_teamMonster[counter]._id]._monsterRef]._name);
+ ttsMessage += buffer;
displayStringAtTextPos(buffer);
- if (mobsterCount > 1)
+ if (mobsterCount > 1) {
displayStringAtTextPos("s");
+ ttsMessage += "s";
+ }
} else if (var1 == 0x3E) {
displayStringAtTextPos("(NOT DEFINED)");
} else if (var1 == 0x3F) {
Common::String stringToDisplay = _npcBuf[_mapMonsters[_techId][_teamMonster[counter]._id]._npcId]._name;
displayStringAtTextPos(stringToDisplay);
+ ttsMessage += stringToDisplay;
}
setTextPos(228, textPosY);
if (checkMonsterMovementType(counter, true)) {
_textColor = 0xE;
displayStringAtTextPos("Hostile");
+ ttsMessage += ", Hostile, ";
} else {
_textColor = 0x2;
displayStringAtTextPos("Friendly");
+ ttsMessage += ", Friendly, ";
}
setTextColorRed();
switch (monsterDistance) {
case 1:
displayCenteredString("S", 290, 302, textPosY);
+ ttsMessage += "S";
break;
case 2:
displayCenteredString("M", 290, 302, textPosY);
+ ttsMessage += "M";
break;
case 3:
displayCenteredString("L", 290, 302, textPosY);
+ ttsMessage += "L";
break;
default:
displayCenteredString("?", 290, 302, textPosY);
+ ttsMessage += "?";
break;
}
+ sayText(ttsMessage, kMenu);
textPosY += 9;
}
}
@@ -1682,6 +1719,12 @@ int16 EfhEngine::selectMonsterGroup() {
}
}
+ _sayMenu = true;
+ stopTextToSpeech();
+ if (retVal != -1 && retVal != 27) {
+ sayText(Common::String::format("%c", retVal + Common::KEYCODE_a), kNoRestriction);
+ }
+
return retVal;
}
diff --git a/engines/efh/init.cpp b/engines/efh/init.cpp
index 9ef80f076c0..e6e6a7bde9c 100644
--- a/engines/efh/init.cpp
+++ b/engines/efh/init.cpp
@@ -352,6 +352,10 @@ EfhEngine::EfhEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst),
_statusMenuActive = false;
_menuDepth = 0;
_menuItemCounter = 0;
+ _selectedMenuBox = -1;
+ _sayMenu = true;
+ _sayLowStatusScreen = false;
+ _initiatedTalkByMenu = false;
for (int i = 0; i < 15; ++i)
_menuStatItemArr[i] = 0;
diff --git a/engines/efh/menu.cpp b/engines/efh/menu.cpp
index 95adc30493c..08e789dfea1 100644
--- a/engines/efh/menu.cpp
+++ b/engines/efh/menu.cpp
@@ -23,7 +23,7 @@
namespace Efh {
-int16 EfhEngine::displayBoxWithText(const Common::String &str, int16 menuType, int16 displayOption, bool displayTeamWindowFl) {
+int16 EfhEngine::displayBoxWithText(const Common::String &str, int16 menuType, int16 displayOption, bool displayTeamWindowFl, bool voiceText) {
debugC(3, kDebugEngine, "displayBoxWithText %s %d %d %s", str.c_str(), menuType, displayOption, displayTeamWindowFl ? "True" : "False");
int16 retVal = 0xFF;
@@ -61,6 +61,20 @@ int16 EfhEngine::displayBoxWithText(const Common::String &str, int16 menuType, i
break;
}
+ if (voiceText) {
+ Common::String ttsMessage(str);
+
+ // Some messages have a ^ followed by a few numbers at the end
+ uint32 caretPosition = ttsMessage.find('^');
+ if (caretPosition != Common::String::npos) {
+ ttsMessage.erase(caretPosition, Common::String::npos);
+ }
+
+ ttsMessage.replace('|', '\n');
+
+ sayText(ttsMessage, kNoRestriction);
+ }
+
drawColoredRect(minX, minY, maxX, maxY, 0);
if (!str.empty())
retVal = script_parse(str, minX, minY, maxX, maxY, true);
@@ -100,6 +114,11 @@ bool EfhEngine::handleDeathMenu() {
_imageSetSubFilesIdx = 213;
drawScreen();
+ sayText("Darkness prevails... death has taken you!", kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ sayText("Load last saved game", kNoRestriction);
+ sayText("Restart from beginning", kNoRestriction);
+ sayText("Quit for now", kNoRestriction);
+
for (uint counter = 0; counter < 2; ++counter) {
clearBottomTextZone(0);
displayCenteredString("Darkness Prevails...Death Has Taken You!", 24, 296, 153);
@@ -165,26 +184,31 @@ void EfhEngine::displayCombatMenu(int16 charId) {
displayStringAtTextPos("A");
setTextColorRed();
displayStringAtTextPos("ttack");
+ sayText("Attack", kMenu);
setTextPos(195, 79);
setTextColorWhite();
displayStringAtTextPos("H");
setTextColorRed();
displayStringAtTextPos("ide");
+ sayText("Hide", kMenu);
setTextPos(152, 88);
setTextColorWhite();
displayStringAtTextPos("D");
setTextColorRed();
displayStringAtTextPos("efend");
+ sayText("Defend", kMenu);
setTextPos(195, 88);
setTextColorWhite();
displayStringAtTextPos("R");
setTextColorRed();
displayStringAtTextPos("un");
+ sayText("Run", kMenu);
setTextPos(152, 97);
setTextColorWhite();
displayStringAtTextPos("S");
setTextColorRed();
displayStringAtTextPos("tatus");
+ sayText("Status", kMenu);
}
void EfhEngine::displayMenuItemString(int16 menuBoxId, int16 thisBoxId, int16 minX, int16 maxX, int16 minY, const char *str) {
@@ -196,6 +220,11 @@ void EfhEngine::displayMenuItemString(int16 menuBoxId, int16 thisBoxId, int16 mi
else
setTextColorGrey();
+ if (_selectedMenuBox != thisBoxId) {
+ sayText(str, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ _selectedMenuBox = thisBoxId;
+ }
+
Common::String buffer = Common::String::format("> %s <", str);
displayCenteredString(buffer, minX, maxX, minY);
setTextColorRed();
@@ -275,33 +304,47 @@ void EfhEngine::displayCharacterSummary(int16 curMenuLine, int16 npcId) {
setTextColorRed();
Common::String buffer1 = _npcBuf[npcId]._name;
setTextPos(146, 27);
- displayStringAtTextPos("Name: ");
+ buffer1 = "Name: " + buffer1;
+ Common::String ttsMessage = buffer1;
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("Level: %d", getXPLevel(_npcBuf[npcId]._xp));
+ ttsMessage += '\n' + buffer1;
setTextPos(146, 36);
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("XP: %u", _npcBuf[npcId]._xp);
+ ttsMessage += '\n' + buffer1;
setTextPos(227, 36);
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("Speed: %d", _npcBuf[npcId]._speed);
+ ttsMessage += '\n' + buffer1;
setTextPos(146, 45);
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("Defense: %d", getEquipmentDefense(npcId));
+ ttsMessage += '\n' + buffer1;
setTextPos(146, 54);
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("Hit Points: %d", _npcBuf[npcId]._hitPoints);
+ ttsMessage += '\n' + buffer1;
setTextPos(146, 63);
displayStringAtTextPos(buffer1);
buffer1 = Common::String::format("Max HP: %d", _npcBuf[npcId]._maxHP);
+ ttsMessage += '\n' + buffer1;
setTextPos(227, 63);
displayStringAtTextPos(buffer1);
+
+ if (curMenuLine == -1) {
+ sayText(ttsMessage, kMenu);
+ }
+
displayCenteredString("Inventory", 144, 310, 72);
+ sayText("Inventory", kMenu);
if (_menuItemCounter == 0) {
if (curMenuLine != -1)
setTextColorWhite();
displayCenteredString("Nothing Carried", 144, 310, 117);
+ sayText("Nothing Carried", kMenu);
setTextColorRed();
return;
}
@@ -319,6 +362,7 @@ void EfhEngine::displayCharacterSummary(int16 curMenuLine, int16 npcId) {
if (_npcBuf[npcId]._inventory[_menuStatItemArr[counter]].isEquipped()) {
setTextPos(146, textPosY);
displayCharAtTextPos('E');
+ sayText("E", kMenu);
}
}
@@ -329,12 +373,14 @@ void EfhEngine::displayCharacterSummary(int16 curMenuLine, int16 npcId) {
buffer1 = Common::String::format("%c)", 'A' + counter);
}
displayStringAtTextPos(buffer1);
+ sayText(buffer1, kMenu);
if (itemId != 0x7FFF) {
setTextPos(168, textPosY);
buffer1 = Common::String::format(" %s", _items[itemId]._name);
displayStringAtTextPos(buffer1);
setTextPos(262, textPosY);
+ sayText(buffer1, kMenu);
if (_items[itemId]._defense > 0) {
int16 curHitPoints = _npcBuf[npcId]._inventory[_menuStatItemArr[counter]]._curHitPoints;
@@ -343,6 +389,7 @@ void EfhEngine::displayCharacterSummary(int16 curMenuLine, int16 npcId) {
displayStringAtTextPos(buffer1);
setTextPos(286, textPosY);
displayStringAtTextPos("Def");
+ sayText(buffer1 + " Defense", kMenu);
}
} else if (_items[itemId]._uses != 0x7F) {
int16 stat1 = _npcBuf[npcId]._inventory[_menuStatItemArr[counter]].getUsesLeft();
@@ -350,10 +397,13 @@ void EfhEngine::displayCharacterSummary(int16 curMenuLine, int16 npcId) {
buffer1 = Common::String::format("%d", stat1);
displayStringAtTextPos(buffer1);
setTextPos(286, textPosY);
- if (stat1 == 1)
+ if (stat1 == 1) {
displayStringAtTextPos("Use");
- else
+ sayText(buffer1 + " Use", kMenu);
+ } else {
displayStringAtTextPos("Uses");
+ sayText(buffer1 + " Uses", kMenu);
+ }
}
}
}
@@ -366,14 +416,20 @@ void EfhEngine::displayCharacterInformationOrSkills(int16 curMenuLine, int16 cha
setTextColorRed();
Common::String buffer = _npcBuf[charId]._name;
+ buffer = "Name: " + buffer;
setTextPos(146, 27);
- displayStringAtTextPos("Name: ");
displayStringAtTextPos(buffer);
+
+ sayText(buffer, kMenu);
+
if (_menuItemCounter <= 0) {
if (curMenuLine != -1)
setTextColorWhite();
displayCenteredString("No Skills To Select", 144, 310, 96);
setTextColorRed();
+
+ sayText("No Skills To Select", kMenu);
+
return;
}
@@ -389,9 +445,11 @@ void EfhEngine::displayCharacterInformationOrSkills(int16 curMenuLine, int16 cha
}
displayStringAtTextPos(buffer);
+ sayText(buffer, kMenu);
setTextPos(163, textPosY);
int16 scoreId = _menuStatItemArr[counter];
displayStringAtTextPos(kSkillArray[scoreId]);
+ sayText(kSkillArray[scoreId], kMenu);
if (scoreId < 15)
buffer = Common::String::format("%d", _npcBuf[charId]._activeScore[_menuStatItemArr[counter]]);
else if (scoreId < 26)
@@ -400,6 +458,7 @@ void EfhEngine::displayCharacterInformationOrSkills(int16 curMenuLine, int16 cha
buffer = Common::String::format("%d", _npcBuf[charId]._infoScore[_menuStatItemArr[counter] - 26]);
setTextPos(278, textPosY);
displayStringAtTextPos(buffer);
+ sayText(buffer, kMenu);
setTextColorRed();
}
}
@@ -413,44 +472,56 @@ void EfhEngine::displayStatusMenuActions(int16 menuId, int16 curMenuLine, int16
switch (menuId) {
case kEfhMenuEquip:
displayCenteredString("Select Item to Equip", 144, 310, 15);
+ sayText("Select Item to Equip", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
case kEfhMenuUse:
displayCenteredString("Select Item to Use", 144, 310, 15);
+ sayText("Select Item to Use", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
case kEfhMenuGive:
displayCenteredString("Select Item to Give", 144, 310, 15);
+ sayText("Select Item to Give", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
case kEfhMenuTrade:
displayCenteredString("Select Item to Trade", 144, 310, 15);
+ sayText("Select Item to Trade", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
case kEfhMenuDrop:
displayCenteredString("Select Item to Drop", 144, 310, 15);
+ sayText("Select Item to Drop", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
case kEfhMenuInfo:
displayCenteredString("Character Information", 144, 310, 15);
+ sayText("Character Information", kMenu);
displayCharacterInformationOrSkills(curMenuLine, npcId);
break;
case kEfhMenuPassive:
displayCenteredString("Passive Skills", 144, 310, 15);
+ sayText("Passive Skills", kMenu);
displayCharacterInformationOrSkills(curMenuLine, npcId);
break;
case kEfhMenuActive:
displayCenteredString("Active Skills", 144, 310, 15);
+ sayText("Active Skills", kMenu);
displayCharacterInformationOrSkills(curMenuLine, npcId);
break;
case kEfhMenuLeave:
case kEfhMenuInvalid:
displayCenteredString("Character Summary", 144, 310, 15);
+ sayText("Character Summary", kMenu);
displayCharacterSummary(curMenuLine, npcId);
break;
default:
break;
}
+
+ sayText("Escape Aborts", kMenu);
+ _sayMenu = false;
}
void EfhEngine::prepareStatusMenu(int16 windowId, int16 menuId, int16 curMenuLine, int16 charId, bool refreshFl) {
@@ -480,6 +551,7 @@ void EfhEngine::displayWindowAndStatusMenu(int16 charId, int16 windowId, int16 m
int16 EfhEngine::displayStringInSmallWindowWithBorder(const Common::String &str, bool delayFl, int16 charId, int16 windowId, int16 menuId, int16 curMenuLine) {
debugC(3, kDebugEngine, "displayStringInSmallWindowWithBorder %s %s %d %d %d %d", str.c_str(), delayFl ? "True" : "False", charId, windowId, menuId, curMenuLine);
+ sayText(str, kNoRestriction);
int16 retVal = 0;
for (uint counter = 0; counter < 2; ++counter) {
@@ -514,6 +586,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
int16 curMenuLine = -1;
bool selectionDoneFl = false;
bool var2 = false;
+ _selectedMenuBox = 0;
saveAnimImageSetId();
@@ -554,6 +627,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
break;
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_l:
+ stopTextToSpeech();
windowId = kEfhMenuLeave;
input = Common::KEYCODE_RETURN;
break;
@@ -573,6 +647,10 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
debugC(9, kDebugEngine, "handleStatusMenu - unhandled keys");
break;
}
+
+ if (input == Common::KEYCODE_RETURN) {
+ _selectedMenuBox = -1;
+ }
} else if (_menuDepth == 1) {
// in the sub-menus, only a list of selectable items is displayed
if (input >= Common::KEYCODE_a && input <= Common::KEYCODE_z) {
@@ -592,6 +670,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
if (menuId >= kEfhMenuLeave)
selectionDoneFl = true;
else {
+ _sayMenu = true;
_menuDepth = 1;
curMenuLine = 0;
}
@@ -600,6 +679,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
_menuDepth = 0;
curMenuLine = -1;
menuId = kEfhMenuInvalid;
+ _sayMenu = true;
prepareStatusMenu(windowId, menuId, curMenuLine, charId, true);
} else {
selectedLine = curMenuLine;
@@ -611,6 +691,8 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
_menuDepth = 0;
curMenuLine = -1;
menuId = kEfhMenuInvalid;
+ stopTextToSpeech();
+ _sayMenu = true;
prepareStatusMenu(windowId, menuId, curMenuLine, charId, true);
break;
case Common::KEYCODE_2:
@@ -667,6 +749,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuEquip:
objectId = _menuStatItemArr[selectedLine];
itemId = _npcBuf[charId]._inventory[objectId]._ref; // CHECKME: Useless?
+ sayText(_items[itemId]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
tryToggleEquipped(charId, objectId, windowId, menuId, curMenuLine);
if (gameMode == 2) {
restoreAnimImageSetId();
@@ -677,6 +760,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuUse:
objectId = _menuStatItemArr[selectedLine];
itemId = _npcBuf[charId]._inventory[objectId]._ref;
+ sayText(_items[itemId]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
if (gameMode == 2) {
restoreAnimImageSetId();
_statusMenuActive = false;
@@ -693,6 +777,8 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuGive:
objectId = _menuStatItemArr[selectedLine];
itemId = _npcBuf[charId]._inventory[objectId]._ref;
+ sayText(_items[itemId]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
+ _initiatedTalkByMenu = true;
if (hasObjectEquipped(charId, objectId) && isItemCursed(itemId)) {
displayStringInSmallWindowWithBorder("The item is cursed! IT IS EVIL!!!!!!!!", true, charId, windowId, menuId, curMenuLine);
} else {
@@ -700,6 +786,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
displayStringInSmallWindowWithBorder("Item is Equipped! Give anyway?", false, charId, windowId, menuId, curMenuLine);
if (!getValidationFromUser())
validationFl = false;
+ stopTextToSpeech();
displayWindowAndStatusMenu(charId, windowId, menuId, curMenuLine);
}
if (validationFl) {
@@ -719,6 +806,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuTrade:
objectId = _menuStatItemArr[selectedLine];
itemId = _npcBuf[charId]._inventory[objectId]._ref;
+ sayText(_items[itemId]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
if (hasObjectEquipped(charId, objectId) && isItemCursed(itemId)) {
displayStringInSmallWindowWithBorder("The item is cursed! IT IS EVIL!!!!!!!!", true, charId, windowId, menuId, curMenuLine);
break;
@@ -728,6 +816,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
displayStringInSmallWindowWithBorder("Item is Equipped! Trade anyway?", false, charId, windowId, menuId, curMenuLine);
if (!getValidationFromUser())
validationFl = false;
+ stopTextToSpeech();
displayWindowAndStatusMenu(charId, windowId, menuId, curMenuLine);
}
@@ -738,6 +827,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
if (_teamChar[2]._id != -1) {
displayStringInSmallWindowWithBorder("Who will you give the item to?", false, charId, windowId, menuId, curMenuLine);
destCharId = selectOtherCharFromTeam();
+ stopTextToSpeech();
var2 = false;
} else if (_teamChar[1]._id == -1) {
destCharId = 0x1A;
@@ -752,6 +842,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
if (destCharId != 0x1A && destCharId != 0x1B) {
givenFl = giveItemTo(_teamChar[destCharId]._id, objectId, charId);
+ sayText(_npcBuf[_teamChar[destCharId]._id]._name, kNoRestriction);
if (!givenFl) {
displayStringInSmallWindowWithBorder("That character cannot carry anymore!", false, charId, windowId, menuId, curMenuLine);
getLastCharAfterAnimCount(_guessAnimationAmount);
@@ -781,6 +872,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuDrop:
objectId = _menuStatItemArr[selectedLine];
itemId = _npcBuf[charId]._inventory[objectId]._ref;
+ sayText(_items[itemId]._name, kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
if (hasObjectEquipped(charId, objectId) && isItemCursed(itemId)) {
displayStringInSmallWindowWithBorder("The item is cursed! IT IS EVIL!!!!!!!!", true, charId, windowId, menuId, curMenuLine);
} else {
@@ -791,6 +883,8 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
displayWindowAndStatusMenu(charId, windowId, menuId, curMenuLine);
}
if (validationFl) {
+ stopTextToSpeech();
+ _sayMenu = true;
removeObject(charId, objectId);
if (gameMode == 2) {
restoreAnimImageSetId();
@@ -808,7 +902,9 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
case kEfhMenuInfo:
case kEfhMenuPassive:
case kEfhMenuActive:
+ stopTextToSpeech();
objectId = _menuStatItemArr[selectedLine];
+ sayText(kSkillArray[objectId], kNoRestriction, Common::TextToSpeechManager::INTERRUPT);
if (gameMode == 2) {
displayStringInSmallWindowWithBorder("Not a Combat Option!", true, charId, windowId, menuId, curMenuLine);
} else if (handleInteractionText(_mapPosX, _mapPosY, charId, objectId, 4, -1)) {
@@ -823,6 +919,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
if (menuId != kEfhMenuLeave) {
selectionDoneFl = false;
_menuDepth = 0;
+ _sayMenu = true;
menuId = kEfhMenuInvalid;
selectedLine = -1;
curMenuLine = -1;
@@ -831,6 +928,7 @@ int16 EfhEngine::handleStatusMenu(int16 gameMode, int16 charId) {
if (menuId == kEfhMenuLeave) {
restoreAnimImageSetId();
_statusMenuActive = false;
+ _sayMenu = true;
return 0x7FFF;
}
diff --git a/engines/efh/metaengine.cpp b/engines/efh/metaengine.cpp
index 44811a59f34..14e9da986a5 100644
--- a/engines/efh/metaengine.cpp
+++ b/engines/efh/metaengine.cpp
@@ -30,10 +30,31 @@
#include "graphics/thumbnail.h"
#include "graphics/surface.h"
+#include "efh/detection.h"
#include "efh/efh.h"
namespace Efh {
+#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
+
uint32 EfhEngine::getFeatures() const {
return _gameDescription->flags;
}
@@ -67,6 +88,12 @@ public:
return "efh";
}
+#ifdef USE_TTS
+ const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+ return Efh::optionsList;
+ }
+#endif
+
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const override;
bool hasFeature(MetaEngineFeature f) const override;
diff --git a/engines/efh/script.cpp b/engines/efh/script.cpp
index 895e620267c..ea22d99a137 100644
--- a/engines/efh/script.cpp
+++ b/engines/efh/script.cpp
@@ -483,6 +483,7 @@ int16 EfhEngine::script_parse(Common::String stringBuffer, int16 posX, int16 pos
_teamChar[teamSlot]._id = joiningNpcId;
}
refreshTeamSize();
+ _sayLowStatusScreen = true;
}
return retVal;
diff --git a/engines/efh/utils.cpp b/engines/efh/utils.cpp
index 33ed35bc7ac..78dd45b68d6 100644
--- a/engines/efh/utils.cpp
+++ b/engines/efh/utils.cpp
@@ -134,7 +134,7 @@ int16 EfhEngine::getRandom(int16 maxVal) {
return 1 + _rnd->getRandomNumber(maxVal - 1);
}
-Common::KeyCode EfhEngine::getLastCharAfterAnimCount(int16 delay) {
+Common::KeyCode EfhEngine::getLastCharAfterAnimCount(int16 delay, bool waitForTTS) {
debugC(1, kDebugUtils, "getLastCharAfterAnimCount %d", delay);
if (delay == 0)
return Common::KEYCODE_INVALID;
@@ -143,7 +143,8 @@ Common::KeyCode EfhEngine::getLastCharAfterAnimCount(int16 delay) {
_customAction = kActionNone;
uint32 lastMs = _system->getMillis();
- while (delay > 0 && lastChar == Common::KEYCODE_INVALID && _customAction == kActionNone && !shouldQuit()) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while ((delay > 0 || (waitForTTS && ttsMan && ttsMan->isSpeaking())) && lastChar == Common::KEYCODE_INVALID && _customAction == kActionNone && !shouldQuit()) {
_system->delayMillis(20);
uint32 newMs = _system->getMillis();
@@ -156,6 +157,10 @@ Common::KeyCode EfhEngine::getLastCharAfterAnimCount(int16 delay) {
lastChar = handleAndMapInput(false);
}
+ if (waitForTTS) {
+ stopTextToSpeech();
+ }
+
return lastChar;
}
@@ -168,7 +173,8 @@ Common::KeyCode EfhEngine::getInput(int16 delay) {
Common::KeyCode retVal = Common::KEYCODE_INVALID;
uint32 lastMs = _system->getMillis();
- while (delay > 0 && !shouldQuit()) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while ((delay > 0 || (ttsMan && ttsMan->isSpeaking())) && !shouldQuit()) {
_system->delayMillis(20);
uint32 newMs = _system->getMillis();
More information about the Scummvm-git-logs
mailing list