592 lines
17 KiB
C++
592 lines
17 KiB
C++
/* 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/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This code is based on original Hugo Trilogy source code
|
|
*
|
|
* Copyright (c) 1989-1995 David P. Gray
|
|
*
|
|
*/
|
|
|
|
#include "common/events.h"
|
|
#include "common/textconsole.h"
|
|
#include "gui/debugger.h"
|
|
|
|
#include "hugo/hugo.h"
|
|
#include "hugo/display.h"
|
|
#include "hugo/parser.h"
|
|
#include "hugo/file.h"
|
|
#include "hugo/util.h"
|
|
#include "hugo/route.h"
|
|
#include "hugo/sound.h"
|
|
#include "hugo/object.h"
|
|
#include "hugo/text.h"
|
|
#include "hugo/inventory.h"
|
|
#include "hugo/mouse.h"
|
|
|
|
namespace Hugo {
|
|
|
|
Parser::Parser(HugoEngine *vm) : _vm(vm), _putIndex(0), _getIndex(0) {
|
|
_catchallList = nullptr;
|
|
_arrayReqs = nullptr;
|
|
|
|
_backgroundObjects = nullptr;
|
|
_backgroundObjectsSize = 0;
|
|
_cmdList = nullptr;
|
|
_cmdListSize = 0;
|
|
|
|
_cmdLineIndex = 0;
|
|
_cmdLineTick = 0;
|
|
_cmdLineCursor = '_';
|
|
_cmdLine[0] = '\0';
|
|
_checkDoubleF1Fl = false;
|
|
}
|
|
|
|
Parser::~Parser() {
|
|
}
|
|
|
|
uint16 Parser::getCmdDefaultVerbIdx(const uint16 index) const {
|
|
return _cmdList[index][0]._verbIndex;
|
|
}
|
|
|
|
/**
|
|
* Read a cmd structure from Hugo.dat
|
|
*/
|
|
void Parser::readCmd(Common::ReadStream &in, cmd &curCmd) {
|
|
curCmd._verbIndex = in.readUint16BE();
|
|
curCmd._reqIndex = in.readUint16BE();
|
|
curCmd._textDataNoCarryIndex = in.readUint16BE();
|
|
curCmd._reqState = in.readByte();
|
|
curCmd._newState = in.readByte();
|
|
curCmd._textDataWrongIndex = in.readUint16BE();
|
|
curCmd._textDataDoneIndex = in.readUint16BE();
|
|
curCmd._actIndex = in.readUint16BE();
|
|
}
|
|
|
|
/**
|
|
* Load _cmdList from Hugo.dat
|
|
*/
|
|
void Parser::loadCmdList(Common::ReadStream &in) {
|
|
cmd tmpCmd;
|
|
memset(&tmpCmd, 0, sizeof(tmpCmd));
|
|
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
|
|
uint16 numElem = in.readUint16BE();
|
|
if (varnt == _vm->_gameVariant) {
|
|
_cmdListSize = numElem;
|
|
_cmdList = (cmd **)malloc(sizeof(cmd *) * _cmdListSize);
|
|
}
|
|
|
|
for (int16 i = 0; i < numElem; i++) {
|
|
uint16 numSubElem = in.readUint16BE();
|
|
if (varnt == _vm->_gameVariant)
|
|
_cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem);
|
|
for (int16 j = 0; j < numSubElem; j++)
|
|
readCmd(in, (varnt == _vm->_gameVariant) ? _cmdList[i][j] : tmpCmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Parser::readBG(Common::ReadStream &in, Background &curBG) {
|
|
curBG._verbIndex = in.readUint16BE();
|
|
curBG._nounIndex = in.readUint16BE();
|
|
curBG._commentIndex = in.readSint16BE();
|
|
curBG._matchFl = (in.readByte() != 0);
|
|
curBG._roomState = in.readByte();
|
|
curBG._bonusIndex = in.readByte();
|
|
}
|
|
|
|
/**
|
|
* Read _backgrounObjects from Hugo.dat
|
|
*/
|
|
void Parser::loadBackgroundObjects(Common::ReadStream &in) {
|
|
Background tmpBG;
|
|
memset(&tmpBG, 0, sizeof(tmpBG));
|
|
|
|
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
|
|
uint16 numElem = in.readUint16BE();
|
|
|
|
if (varnt == _vm->_gameVariant) {
|
|
_backgroundObjectsSize = numElem;
|
|
_backgroundObjects = (Background **)malloc(sizeof(Background *) * numElem);
|
|
}
|
|
|
|
for (int i = 0; i < numElem; i++) {
|
|
uint16 numSubElem = in.readUint16BE();
|
|
if (varnt == _vm->_gameVariant)
|
|
_backgroundObjects[i] = (Background *)malloc(sizeof(Background) * numSubElem);
|
|
|
|
for (int j = 0; j < numSubElem; j++)
|
|
readBG(in, (varnt == _vm->_gameVariant) ? _backgroundObjects[i][j] : tmpBG);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read _catchallList from Hugo.dat
|
|
*/
|
|
void Parser::loadCatchallList(Common::ReadStream &in) {
|
|
Background *wrkCatchallList = nullptr;
|
|
Background tmpBG;
|
|
memset(&tmpBG, 0, sizeof(tmpBG));
|
|
|
|
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
|
|
uint16 numElem = in.readUint16BE();
|
|
|
|
if (varnt == _vm->_gameVariant)
|
|
_catchallList = wrkCatchallList = (Background *)malloc(sizeof(Background) * numElem);
|
|
|
|
for (int i = 0; i < numElem; i++)
|
|
readBG(in, (varnt == _vm->_gameVariant) ? wrkCatchallList[i] : tmpBG);
|
|
}
|
|
}
|
|
|
|
void Parser::loadArrayReqs(Common::SeekableReadStream &in) {
|
|
_arrayReqs = _vm->loadLongArray(in);
|
|
}
|
|
|
|
/**
|
|
* Search background command list for this screen for supplied object.
|
|
* Return first associated verb (not "look") or 0 if none found.
|
|
*/
|
|
const char *Parser::useBG(const char *name) {
|
|
debugC(1, kDebugEngine, "useBG(%s)", name);
|
|
|
|
ObjectList p = _backgroundObjects[*_vm->_screenPtr];
|
|
for (int i = 0; p[i]._verbIndex != 0; i++) {
|
|
if ((name == _vm->_text->getNoun(p[i]._nounIndex, 0) &&
|
|
p[i]._verbIndex != _vm->_look) &&
|
|
((p[i]._roomState == kStateDontCare) || (p[i]._roomState == _vm->_screenStates[*_vm->_screenPtr])))
|
|
return _vm->_text->getVerb(p[i]._verbIndex, 0);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Parser::freeParser() {
|
|
if (_arrayReqs) {
|
|
for (int i = 0; _arrayReqs[i] != nullptr; i++)
|
|
free(_arrayReqs[i]);
|
|
free(_arrayReqs);
|
|
_arrayReqs = nullptr;
|
|
}
|
|
|
|
free(_catchallList);
|
|
_catchallList = nullptr;
|
|
|
|
if (_backgroundObjects) {
|
|
for (int i = 0; i < _backgroundObjectsSize; i++)
|
|
free(_backgroundObjects[i]);
|
|
free(_backgroundObjects);
|
|
_backgroundObjects = nullptr;
|
|
}
|
|
|
|
if (_cmdList) {
|
|
for (int i = 0; i < _cmdListSize; i++)
|
|
free(_cmdList[i]);
|
|
free(_cmdList);
|
|
_cmdList = nullptr;
|
|
}
|
|
}
|
|
|
|
void Parser::switchTurbo() {
|
|
_vm->_config._turboFl = !_vm->_config._turboFl;
|
|
|
|
#ifdef USE_TTS
|
|
if (_vm->_config._turboFl) {
|
|
_vm->sayText("T");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Add any new chars to line buffer and display them.
|
|
* If CR pressed, pass line to LineHandler()
|
|
*/
|
|
void Parser::charHandler() {
|
|
debugC(4, kDebugParser, "charHandler");
|
|
|
|
Status &gameStatus = _vm->getGameStatus();
|
|
|
|
// Check for one or more characters in ring buffer
|
|
while (_getIndex != _putIndex) {
|
|
char c = _ringBuffer[_getIndex++];
|
|
if (_getIndex >= sizeof(_ringBuffer))
|
|
_getIndex = 0;
|
|
|
|
switch (c) {
|
|
case Common::KEYCODE_BACKSPACE: // Rubout key
|
|
if (_cmdLineIndex)
|
|
_cmdLine[--_cmdLineIndex] = '\0';
|
|
break;
|
|
case Common::KEYCODE_RETURN: // EOL, pass line to line handler
|
|
if (_cmdLineIndex && (_vm->_hero->_pathType != kPathQuiet)) {
|
|
// Remove inventory bar if active
|
|
if (_vm->_inventory->getInventoryState() == kInventoryActive)
|
|
_vm->_inventory->setInventoryState(kInventoryUp);
|
|
#ifdef USE_TTS
|
|
_vm->sayText(_cmdLine);
|
|
_vm->_previousSaid.clear();
|
|
#endif
|
|
// Call Line handler and reset line
|
|
command(_cmdLine);
|
|
_cmdLine[_cmdLineIndex = 0] = '\0';
|
|
}
|
|
break;
|
|
default: // Normal text key, add to line
|
|
if (_cmdLineIndex >= kMaxLineSize) {
|
|
//MessageBeep(MB_ICONASTERISK);
|
|
warning("STUB: MessageBeep() - Command line too long");
|
|
} else if (Common::isPrint(c)) {
|
|
_cmdLine[_cmdLineIndex++] = c;
|
|
_cmdLine[_cmdLineIndex] = '\0';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// See if time to blink cursor, set cursor character
|
|
if (_vm->useWindowsInterface()) {
|
|
if ((_cmdLineTick++ % (_vm->getTPS() / kBlinksPerSec)) == 0)
|
|
_cmdLineCursor = (_cmdLineCursor == '_') ? ' ' : '_';
|
|
} else {
|
|
// DOS: No blinking cursor
|
|
_cmdLineCursor = ' ';
|
|
}
|
|
|
|
// See if recall button pressed
|
|
if (gameStatus._recallFl) {
|
|
// Copy previous line to current cmdline
|
|
gameStatus._recallFl = false;
|
|
Common::strcpy_s(_cmdLine, _vm->_line);
|
|
_cmdLineIndex = strlen(_cmdLine);
|
|
}
|
|
|
|
_vm->_screen->updateStatusText();
|
|
_vm->_screen->updatePromptText(_cmdLine, _cmdLineCursor);
|
|
|
|
#ifdef USE_TTS
|
|
if (_vm->_previousScore != _vm->getScore()) {
|
|
_vm->sayText(Common::String::format("Score: %d of %d", _vm->getScore(), _vm->getMaxScore()));
|
|
_vm->_previousScore = _vm->getScore();
|
|
}
|
|
|
|
if (_vm->_voiceScoreLine) {
|
|
_vm->sayText(Common::String::format("F1: Help\n%s\nScore: %d of %d\nSound %s", (_vm->_config._turboFl) ? "T" : " ", _vm->getScore(), _vm->getMaxScore(), (_vm->_config._soundFl) ? "On" : "Off"));
|
|
_vm->_voiceScoreLine = false;
|
|
}
|
|
|
|
if (_vm->_voiceSoundSetting) {
|
|
_vm->sayText(Common::String::format("Sound %s", (_vm->_config._soundFl) ? "On" : "Off"));
|
|
|
|
// If the mouse is in the top menu range, the top menu will close and reopen after the sound setting is changed,
|
|
// causing the new sound setting voicing to be interrupted. Therefore, keep trying to voice the new sound setting
|
|
// as long as the top menu can be opened
|
|
int mouseY = _vm->_mouse->getMouseY();
|
|
if (mouseY <= 0 || mouseY >= 5) {
|
|
_vm->_voiceSoundSetting = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// See if "look" button pressed
|
|
if (gameStatus._lookFl) {
|
|
command("look around");
|
|
gameStatus._lookFl = false;
|
|
}
|
|
}
|
|
|
|
void Parser::keyHandler(Common::Event event) {
|
|
debugC(1, kDebugParser, "keyHandler(%d)", event.kbd.keycode);
|
|
|
|
Status &gameStatus = _vm->getGameStatus();
|
|
|
|
if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_SCRL))
|
|
return;
|
|
|
|
// Process key down input - called from OnKeyDown()
|
|
if (!gameStatus._storyModeFl) { // Keyboard disabled
|
|
// Add printable keys to ring buffer
|
|
uint16 bnext = _putIndex + 1;
|
|
if (bnext >= sizeof(_ringBuffer))
|
|
bnext = 0;
|
|
if (bnext != _getIndex) {
|
|
_ringBuffer[_putIndex] = event.kbd.ascii;
|
|
_putIndex = bnext;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Parser::actionHandler(Common::Event event) {
|
|
debugC(1, kDebugParser, "ActionHandler(%d)", event.customType);
|
|
|
|
Status &gameStatus = _vm->getGameStatus();
|
|
|
|
switch (event.customType) {
|
|
case kActionUserHelp:
|
|
if (_vm->useWindowsInterface()) {
|
|
// Windows: Track double-F1 with a flag
|
|
if (_checkDoubleF1Fl)
|
|
gameStatus._helpFl = true;
|
|
else
|
|
_vm->_screen->userHelp();
|
|
_checkDoubleF1Fl = !_checkDoubleF1Fl;
|
|
} else {
|
|
// DOS: userHelp() handles double-F1
|
|
_vm->_screen->userHelp();
|
|
}
|
|
break;
|
|
case kActionToggleSound:
|
|
_vm->_sound->toggleSound();
|
|
_vm->_sound->toggleMusic();
|
|
break;
|
|
case kActionRepeatLine:
|
|
gameStatus._recallFl = true;
|
|
break;
|
|
case kActionSaveGame:
|
|
if (gameStatus._viewState == kViewPlay) {
|
|
if (gameStatus._gameOverFl)
|
|
_vm->gameOverMsg();
|
|
else
|
|
_vm->_file->saveGame(-1, Common::String());
|
|
}
|
|
break;
|
|
case kActionRestoreGame:
|
|
_vm->_file->restoreGame(-1);
|
|
break;
|
|
case kActionNewGame: {
|
|
// DOS requires shorter text for message boxes
|
|
const char *message = _vm->useWindowsInterface() ?
|
|
"Are you sure you want to start a new game?" :
|
|
"Are you sure you want to RESTART?";
|
|
if (_vm->yesNoBox(message, true))
|
|
_vm->_file->restoreGame(99);
|
|
break;
|
|
}
|
|
case kActionInventory:
|
|
showInventory();
|
|
break;
|
|
case kActionToggleTurbo:
|
|
switchTurbo();
|
|
break;
|
|
case kActionEscape: // Escape key, may want to QUIT
|
|
if (gameStatus._viewState == kViewIntro) {
|
|
gameStatus._skipIntroFl = true;
|
|
} else if (gameStatus._viewState == kViewPlay) {
|
|
endGamePrompt();
|
|
} else {
|
|
if (_vm->_inventory->getInventoryState() == kInventoryActive) // Remove inventory, if displayed
|
|
_vm->_inventory->setInventoryState(kInventoryUp);
|
|
_vm->_screen->resetInventoryObjId();
|
|
}
|
|
break;
|
|
case kActionMoveTop:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_UP); // Direction of hero travel
|
|
break;
|
|
case kActionMoveBottom:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_DOWN); // Direction of hero travel
|
|
break;
|
|
case kActionMoveLeft:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_LEFT); // Direction of hero travel
|
|
break;
|
|
case kActionMoveRight:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_RIGHT); // Direction of hero travel
|
|
break;
|
|
case kActionMoveTopLeft:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_KP7); // Direction of hero travel
|
|
break;
|
|
case kActionMoveTopRight:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_KP9); // Direction of hero travel
|
|
break;
|
|
case kActionMoveBottomLeft:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_KP1); // Direction of hero travel
|
|
break;
|
|
case kActionMoveBottomRight:
|
|
_vm->_route->resetRoute(); // Stop any automatic route
|
|
_vm->_route->setWalk(Common::KEYCODE_KP3); // Direction of hero travel
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (_checkDoubleF1Fl && (event.customType != kActionUserHelp))
|
|
_checkDoubleF1Fl = false;
|
|
}
|
|
|
|
/**
|
|
* Perform an immediate command. Takes parameters a la sprintf
|
|
* Assumes final string will not overrun line[] length
|
|
*/
|
|
void Parser::command(const char *format, ...) {
|
|
debugC(1, kDebugParser, "Command(%s, ...)", format);
|
|
|
|
va_list marker;
|
|
va_start(marker, format);
|
|
Common::vsprintf_s(_vm->_line, format, marker);
|
|
va_end(marker);
|
|
|
|
lineHandler();
|
|
}
|
|
|
|
void Parser::resetCommandLine() {
|
|
_cmdLine[_cmdLineIndex = 0] = '\0';
|
|
}
|
|
|
|
/**
|
|
* Locate any member of object name list appearing in command line
|
|
*/
|
|
bool Parser::isWordPresent(char **wordArr) const {
|
|
if (wordArr != nullptr) {
|
|
debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]);
|
|
|
|
for (int i = 0; strlen(wordArr[i]); i++) {
|
|
if (strstr(_vm->_line, wordArr[i]))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Locate word in list of nouns and return ptr to first string in noun list
|
|
*/
|
|
const char *Parser::findNoun() const {
|
|
debugC(1, kDebugParser, "findNoun()");
|
|
|
|
for (int i = 0; _vm->_text->getNounArray(i); i++) {
|
|
for (int j = 0; strlen(_vm->_text->getNoun(i, j)); j++) {
|
|
if (strstr(_vm->_line, _vm->_text->getNoun(i, j)))
|
|
return _vm->_text->getNoun(i, 0);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Locate word in list of verbs and return ptr to first string in verb list
|
|
*/
|
|
const char *Parser::findVerb() const {
|
|
debugC(1, kDebugParser, "findVerb()");
|
|
|
|
for (int i = 0; _vm->_text->getVerbArray(i); i++) {
|
|
for (int j = 0; strlen(_vm->_text->getVerb(i, j)); j++) {
|
|
if (strstr(_vm->_line, _vm->_text->getVerb(i, j)))
|
|
return _vm->_text->getVerb(i, 0);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Show user all objects being carried in a variable width 2 column format
|
|
*/
|
|
void Parser::showDosInventory() const {
|
|
debugC(1, kDebugParser, "showDosInventory()");
|
|
static const char *const blanks = " ";
|
|
uint16 index = 0, len1 = 0, len2 = 0;
|
|
const char *intro = _vm->_text->getTextParser(kTBIntro);
|
|
const char *outro = _vm->_text->getTextParser(kTBOutro);
|
|
if (_vm->getGameType() == kGameTypeHugo3) {
|
|
outro = "\nPress any key to continue";
|
|
}
|
|
const int nounIndex2 = (_vm->getGameType() <= kGameTypeHugo2) ? 0 : 1;
|
|
|
|
for (int i = 0; i < _vm->_object->_numObj; i++) { // Find widths of 2 columns
|
|
if (_vm->_object->isCarried(i)) {
|
|
uint16 len = strlen(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, nounIndex2));
|
|
if (index++ & 1) // Right hand column
|
|
len2 = (len > len2) ? len : len2;
|
|
else
|
|
len1 = (len > len1) ? len : len1;
|
|
}
|
|
}
|
|
|
|
Common::String buffer;
|
|
if (_vm->getGameType() <= kGameTypeHugo2) {
|
|
len1 += 1; // For gap between columns
|
|
if (len1 + len2 < (uint16)strlen(outro)) {
|
|
len1 = strlen(outro);
|
|
}
|
|
assert(len1 + len2 - strlen(intro) / 2 < strlen(blanks));
|
|
buffer = Common::String(blanks, (len1 + len2 - strlen(intro)) / 2);
|
|
} else {
|
|
len1 += 4; // For gap between columns
|
|
if (len1 + len2 > (uint16)strlen(intro)) {
|
|
assert(len1 + len2 - strlen(intro) / 2 < strlen(blanks));
|
|
buffer = Common::String(blanks, (len1 + len2 - strlen(intro)) / 2);
|
|
}
|
|
}
|
|
|
|
buffer += intro;
|
|
buffer += '\n';
|
|
index = 0;
|
|
for (int i = 0; i < _vm->_object->_numObj; i++) { // Assign strings
|
|
if (_vm->_object->isCarried(i)) {
|
|
const char *objectName = _vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, nounIndex2);
|
|
buffer += objectName;
|
|
if (index++ & 1) {
|
|
buffer += '\n';
|
|
} else {
|
|
buffer += Common::String(blanks, len1 - strlen(objectName));
|
|
}
|
|
}
|
|
}
|
|
if (index & 1)
|
|
buffer += '\n';
|
|
buffer += outro;
|
|
|
|
#ifdef USE_TTS
|
|
sayInventory(intro, outro, nounIndex2);
|
|
#endif
|
|
|
|
_vm->notifyBox(buffer, false, kTtsNoSpeech);
|
|
}
|
|
|
|
#ifdef USE_TTS
|
|
void Parser::sayInventory(const char *intro, const char *outro, int nounIndex2) const {
|
|
Common::String text = intro;
|
|
for (int i = 0; i < _vm->_object->_numObj; i++) {
|
|
if (_vm->_object->isCarried(i)) {
|
|
text += '\n';
|
|
text += _vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, nounIndex2);
|
|
}
|
|
}
|
|
text += '\n';
|
|
text += outro;
|
|
_vm->sayText(text, Common::TextToSpeechManager::INTERRUPT);
|
|
}
|
|
#endif
|
|
|
|
void Parser::endGamePrompt() {
|
|
if (_vm->yesNoBox(_vm->_text->getTextParser(kTBExit_1d), true)) {
|
|
_vm->endGame();
|
|
}
|
|
}
|
|
|
|
} // End of namespace Hugo
|