/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ // Game interface module #include "saga/saga.h" #include "saga/gfx.h" #include "saga/actor.h" #include "saga/console.h" #include "saga/displayinfo.h" #include "saga/events.h" #include "saga/font.h" #include "saga/objectmap.h" #include "saga/isomap.h" #include "saga/itedata.h" #include "saga/music.h" #include "saga/puzzle.h" #include "saga/render.h" #include "saga/scene.h" #include "saga/script.h" #include "saga/sound.h" #include "saga/sprite.h" #include "saga/resource.h" #include "saga/interface.h" #include "common/config-manager.h" #include "common/system.h" #include "common/timer.h" namespace Saga { static const int verbToTextIdITE[] = { kTextWalkTo, kTextLookAt, kTextPickUp, kTextTalkTo, kTextOpen, kTextClose, kTextUse, kTextGive }; // This maps the internally used string ITE IDs to the LUT strings loaded in IHNM // i.e. id 12 (quit game button) maps to string 14 (Quit game) // The comments are what the actual IHNM string is // For the text string IDs, refer to saga.h, enum TextStringIds static const int IHNMTextStringIdsLUT[56] = { -1, // (Empty) -1, // (Empty) 4, // Take 6, // Talk to -1, -1, 5, // Use 8, // Give 10, // Options 11, // Test 12, // Demo 13, // Help 14, // Quit Game 16, // Fast 18, // Slow 20, // On 21, // Off 15, // Continue Playing 22, // Load 23, // Save 24, // Game Options 25, // Reading Speed 26, // Music 27, // Sound 32, // Cancel 33, // Quit 34, // OK 17, // Mid 19, // Click 36, // 10% 37, // 20% 38, // 30% 39, // 40% 40, // 50% 41, // 60% 42, // 70% 43, // 80% 44, // 90% 45, // Max -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, // Voices 29, // Text 30, // Audio 31 // Both }; #define buttonRes0 0x42544E00 #define buttonRes1 0x42544E01 Interface::Interface(SagaEngine *vm) : _vm(vm) { ByteArray resourceData; int i; #if 0 // FTA2 related test code // TODO: this will probably have to be moved to a new class // It's left here for now till the code differences are figured out if (_vm->getGameId() == GID_FTA2) { _interfaceContext = _vm->_resource->getContext(GAME_IMAGEFILE); _vm->_resource->loadResource(_interfaceContext, 22, resource, resourceLength); // Julian's portrait _vm->decodeBGImage(resource, resourceLength, &_mainPanel.image, &_mainPanel.imageLength, &_mainPanel.imageWidth, &_mainPanel.imageHeight); free(resource); return; } #endif // Load interface module resource file context _interfaceContext = _vm->_resource->getContext(GAME_RESOURCEFILE); if (_interfaceContext == NULL) { error("Interface::Interface() resource context not found"); } // Do nothing for SAGA2 games for now if (_vm->isSaga2()) { return; } // Main panel _mainPanel.buttons = _vm->getDisplayInfo().mainPanelButtons; _mainPanel.buttonsCount = _vm->getDisplayInfo().mainPanelButtonsCount; for (i = 0; i < kVerbTypeIdsMax; i++) { _verbTypeToPanelButton[i] = NULL; } for (i = 0; i < _mainPanel.buttonsCount; i++) { if (_mainPanel.buttons[i].type == kPanelButtonVerb) { _verbTypeToPanelButton[_mainPanel.buttons[i].id] = &_mainPanel.buttons[i]; } } _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _mainPanel.image, &_mainPanel.imageWidth, &_mainPanel.imageHeight); // Converse panel _conversePanel.buttons = _vm->getDisplayInfo().conversePanelButtons; _conversePanel.buttonsCount = _vm->getDisplayInfo().conversePanelButtonsCount; _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _conversePanel.image, &_conversePanel.imageWidth, &_conversePanel.imageHeight); // Option panel if (!_vm->_script->isNonInteractiveDemo()) { _optionPanel.buttons = _vm->getDisplayInfo().optionPanelButtons; _optionPanel.buttonsCount = _vm->getDisplayInfo().optionPanelButtonsCount; _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _optionPanel.image, &_optionPanel.imageWidth, &_optionPanel.imageHeight); } else { _optionPanel.buttons = NULL; _optionPanel.buttonsCount = 0; _optionPanel.sprites.clear(); } #ifdef ENABLE_IHNM // Quit panel if (_vm->getGameId() == GID_IHNM) { _quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons; _quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount; _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _quitPanel.image, &_quitPanel.imageWidth, &_quitPanel.imageHeight); } // Save panel if (_vm->getGameId() == GID_IHNM) { _savePanel.buttons = _vm->getDisplayInfo().savePanelButtons; _savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount; _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _savePanel.image, &_savePanel.imageWidth, &_savePanel.imageHeight); } // Load panel if (_vm->getGameId() == GID_IHNM) { _loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons; _loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount; _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData); _vm->decodeBGImage(resourceData, _loadPanel.image, &_loadPanel.imageWidth, &_loadPanel.imageHeight); } #endif // Main panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->mainPanelSpritesResourceId, _mainPanel.sprites); if (!_vm->_script->isNonInteractiveDemo()) { // Option panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->optionPanelSpritesResourceId, _optionPanel.sprites); // Save panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _savePanel.sprites); // Load panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _loadPanel.sprites); // Quit panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _quitPanel.sprites); } if (_vm->getGameId() == GID_ITE) { _vm->_sprite->loadList(_vm->getResourceDescription()->defaultPortraitsResourceId, _defPortraits); } setPortraitBgColor(0, 0, 0); _mainPanel.x = _vm->getDisplayInfo().mainPanelXOffset; _mainPanel.y = _vm->getDisplayInfo().mainPanelYOffset; _mainPanel.currentButton = NULL; _inventoryUpButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryUpButtonIndex); _inventoryDownButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryDownButtonIndex); _conversePanel.x = _vm->getDisplayInfo().conversePanelXOffset; _conversePanel.y = _vm->getDisplayInfo().conversePanelYOffset; _conversePanel.currentButton = NULL; _converseUpButton = _conversePanel.getButton(_vm->getDisplayInfo().converseUpButtonIndex); _converseDownButton = _conversePanel.getButton(_vm->getDisplayInfo().converseDownButtonIndex); _leftPortrait = 0; _rightPortrait = 0; _optionPanel.x = _vm->getDisplayInfo().optionPanelXOffset; _optionPanel.y = _vm->getDisplayInfo().optionPanelYOffset; _optionPanel.currentButton = NULL; _optionSaveFileSlider = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFileSliderIndex); _optionSaveFilePanel = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFilePanelIndex); _quitPanel.x = _vm->getDisplayInfo().quitPanelXOffset; _quitPanel.y = _vm->getDisplayInfo().quitPanelYOffset; _quitPanel.imageWidth = _vm->getDisplayInfo().quitPanelWidth; _quitPanel.imageHeight = _vm->getDisplayInfo().quitPanelHeight; _quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons; _quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount; _quitPanel.currentButton = NULL; _loadPanel.x = _vm->getDisplayInfo().loadPanelXOffset; _loadPanel.y = _vm->getDisplayInfo().loadPanelYOffset; _loadPanel.imageWidth = _vm->getDisplayInfo().loadPanelWidth; _loadPanel.imageHeight = _vm->getDisplayInfo().loadPanelHeight; _loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons; _loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount; _loadPanel.currentButton = NULL; _savePanel.x = _vm->getDisplayInfo().savePanelXOffset; _savePanel.y = _vm->getDisplayInfo().savePanelYOffset; _savePanel.imageWidth = _vm->getDisplayInfo().savePanelWidth; _savePanel.imageHeight = _vm->getDisplayInfo().savePanelHeight; _savePanel.buttons = _vm->getDisplayInfo().savePanelButtons; _savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount; _saveEdit = _savePanel.getButton(_vm->getDisplayInfo().saveEditIndex); _savePanel.currentButton = NULL; _protectPanel.x = _vm->getDisplayInfo().protectPanelXOffset; _protectPanel.y = _vm->getDisplayInfo().protectPanelYOffset; _protectPanel.imageWidth = _vm->getDisplayInfo().protectPanelWidth; _protectPanel.imageHeight = _vm->getDisplayInfo().protectPanelHeight; _protectPanel.buttons = _vm->getDisplayInfo().protectPanelButtons; _protectPanel.buttonsCount = _vm->getDisplayInfo().protectPanelButtonsCount; _protectEdit = _protectPanel.getButton(_vm->getDisplayInfo().protectEditIndex); _protectPanel.currentButton = NULL; _active = true; _panelMode = _lockedMode = kPanelNull; _savedMode = -1; _bossMode = -1; _fadeMode = kNoFade; _inMainMode = false; *_statusText = 0; _statusOnceColor = -1; _inventoryCount = 0; _inventoryPos = 0; _inventoryStart = 0; _inventoryEnd = 0; _inventoryBox = 0; _saveReminderState = 0; _optionSaveFileTop = 0; _optionSaveFileTitleNumber = 0; _inventory.resize(ITE_INVENTORY_SIZE); _textInput = false; _statusTextInput = false; _statusTextInputState = kStatusTextInputFirstRun; _disableAbortSpeeches = false; // set save game reminder alarm _vm->getTimerManager()->installTimerProc(&saveReminderCallback, TIMETOSAVE, this, "sagaSaveReminder"); } Interface::~Interface() { _vm->getTimerManager()->removeTimerProc(&saveReminderCallback); } void Interface::saveReminderCallback(void *refCon) { ((Interface *)refCon)->updateSaveReminder(); } void Interface::updateSaveReminder() { // CHECKME: This is potentially called from a different thread because it is // called from a timer callback. However, it does not seem to take any // precautions to avoid race conditions. if (_active && _panelMode == kPanelMain) { _saveReminderState = _saveReminderState % _vm->getDisplayInfo().saveReminderNumSprites + 1; drawStatusBar(); _vm->getTimerManager()->removeTimerProc(&saveReminderCallback); _vm->getTimerManager()->installTimerProc(&saveReminderCallback, ((_vm->getGameId() == GID_ITE) ? TIMETOBLINK_ITE : TIMETOBLINK_IHNM), this, "sagaSaveReminder"); } } int Interface::activate() { if (!_active) { _active = true; _vm->_script->_skipSpeeches = false; _vm->_actor->_protagonist->_targetObject = ID_NOTHING; unlockMode(); if (_panelMode == kPanelMain || _panelMode == kPanelChapterSelection) { _saveReminderState = 1; } else if (_panelMode == kPanelNull && _vm->isIHNMDemo()) { _saveReminderState = 1; } _vm->_gfx->showCursor(true); draw(); _vm->_render->setFullRefresh(true); } return SUCCESS; } int Interface::deactivate() { if (_active) { _active = false; lockMode(); setMode(kPanelNull); } _vm->_gfx->showCursor(false); return SUCCESS; } void Interface::rememberMode() { debug(1, "rememberMode(%d)", _panelMode); _savedMode = _panelMode; } void Interface::restoreMode(bool draw_) { debug(1, "restoreMode(%d)", _savedMode); // If _savedMode is -1 by a race condition, set it to kPanelMain if (_savedMode == -1) _savedMode = kPanelMain; _panelMode = _savedMode; _savedMode = -1; if (draw_) draw(); } void Interface::setMode(int mode) { debug(1, "Interface::setMode %i", mode); if (mode == kPanelMain) { _inMainMode = true; _saveReminderState = 1; } else if (mode == kPanelChapterSelection) { _saveReminderState = 1; } else if (mode == kPanelNull) { if (_vm->isIHNMDemo()) { _inMainMode = true; _saveReminderState = 1; } } else if (mode == kPanelOption) { // Show the cursor if it's hidden _vm->_gfx->showCursor(true); } else { if (mode == kPanelConverse) { _inMainMode = false; } _saveReminderState = 0; } _panelMode = mode; switch (_panelMode) { case kPanelMain: // FIXME: Implement IHNM differences from ExecuteInventoryPanel for IHNM (though I believe they're already // implemented) _mainPanel.currentButton = NULL; break; case kPanelConverse: _conversePanel.currentButton = NULL; converseDisplayText(); break; case kPanelOption: _optionPanel.currentButton = NULL; _vm->fillSaveList(); calcOptionSaveSlider(); if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) { _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1; } break; case kPanelLoad: _loadPanel.currentButton = NULL; break; case kPanelQuit: _quitPanel.currentButton = NULL; break; case kPanelSave: _savePanel.currentButton = NULL; _textInputMaxWidth = _saveEdit->width - 10; _textInput = true; _textInputStringLength = strlen(_textInputString); _textInputPos = _textInputStringLength + 1; break; case kPanelMap: mapPanelShow(); break; case kPanelSceneSubstitute: _vm->_render->setFlag(RF_DEMO_SUBST); _vm->_gfx->getCurrentPal(_mapSavedPal); break; case kPanelChapterSelection: break; case kPanelBoss: _vm->_render->setFlag(RF_DEMO_SUBST); break; case kPanelProtect: if (_vm->getGameId() == GID_ITE) { // This is used as the copy protection panel in ITE _protectPanel.currentButton = NULL; _textInputMaxWidth = _protectEdit->width - 10; _textInput = true; _textInputString[0] = 0; _textInputStringLength = 0; _textInputPos = _textInputStringLength + 1; } else { // In the IHNM demo, this panel mode is set by the scripts // to flip through the pages of the help system } break; } draw(); _vm->_render->setFullRefresh(true); } bool Interface::processAscii(Common::KeyState keystate) { // TODO: Checking for Esc and Enter below is a bit hackish, and // probably only works with the English version. Maybe we should // add a flag to the button so it can indicate if it's the default // or cancel button? uint16 ascii = keystate.ascii; int i; PanelButton *panelButton; if (_statusTextInput) { processStatusTextInput(keystate); return true; } switch (_panelMode) { case kPanelNull: if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (_vm->_scene->isInIntro()) { _vm->_scene->skipScene(); } else { if (!_disableAbortSpeeches) _vm->_actor->abortAllSpeeches(); } return true; } #ifdef ENABLE_IHNM if (_vm->_scene->isNonInteractiveIHNMDemoPart()) _vm->_scene->showIHNMDemoSpecialScreen(); #endif break; case kPanelCutaway: if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (!_disableAbortSpeeches) _vm->_actor->abortAllSpeeches(); _vm->_scene->cutawaySkip(); return true; } #ifdef ENABLE_INHM if (_vm->_scene->isNonInteractiveIHNMDemoPart()) _vm->_scene->showIHNMDemoSpecialScreen(); #endif break; case kPanelVideo: if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (_vm->_scene->isInIntro()) { _vm->_scene->skipScene(); } else { if (!_disableAbortSpeeches) _vm->_actor->abortAllSpeeches(); } _vm->_scene->cutawaySkip(); return true; } #ifdef ENABLE_IHNM if (_vm->_scene->isNonInteractiveIHNMDemoPart()) _vm->_scene->showIHNMDemoSpecialScreen(); #endif break; case kPanelOption: // TODO: check input dialog keys if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) { // Esc or Enter ascii = 'c'; //continue } for (i = 0; i < _optionPanel.buttonsCount; i++) { panelButton = &_optionPanel.buttons[i]; if (panelButton->type == kPanelButtonOption) { if (panelButton->ascii == ascii) { setOption(panelButton); return true; } } } break; case kPanelSave: if (_textInput && processTextInput(keystate)) { return true; } if (keystate.keycode == Common::KEYCODE_ESCAPE) { ascii = 'c'; // cancel } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter ascii = 's'; // save } for (i = 0; i < _savePanel.buttonsCount; i++) { panelButton = &_savePanel.buttons[i]; if (panelButton->type == kPanelButtonSave) { if (panelButton->ascii == ascii) { setSave(panelButton); return true; } } } break; case kPanelQuit: if (keystate.keycode == Common::KEYCODE_ESCAPE) { ascii = 'c'; // cancel } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter ascii = 'q'; // quit } for (i = 0; i < _quitPanel.buttonsCount; i++) { panelButton = &_quitPanel.buttons[i]; if (panelButton->type == kPanelButtonQuit) { if (panelButton->ascii == ascii) { setQuit(panelButton); return true; } } } break; case kPanelLoad: for (i = 0; i < _loadPanel.buttonsCount; i++) { panelButton = &_loadPanel.buttons[i]; if (panelButton->type == kPanelButtonLoad) { if (panelButton->ascii == ascii) { setLoad(panelButton); return true; } } } break; case kPanelMain: for (i = 0; i < _mainPanel.buttonsCount; i++) { panelButton = &_mainPanel.buttons[i]; if (panelButton->ascii == ascii) { if (panelButton->type == kPanelButtonVerb) { _vm->_script->setVerb(panelButton->id); } if (panelButton->type == kPanelButtonArrow) { inventoryChangePos(panelButton->id); } return true; } } if (keystate.keycode == Common::KEYCODE_o && keystate.hasFlags(Common::KBD_CTRL)) { // ctrl-o if (_saveReminderState > 0) { setMode(kPanelOption); return true; } } break; case kPanelConverse: switch (ascii) { case 'x': setMode(kPanelMain); if (_vm->_scene->isITEPuzzleScene()) _vm->_puzzle->exitPuzzle(); break; case 'u': converseChangePos(-1); break; case 'd': converseChangePos(1); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': converseSetPos(ascii); break; } break; case kPanelMap: mapPanelClean(); break; case kPanelSceneSubstitute: if (keystate.keycode == Common::KEYCODE_RETURN) { _vm->_render->clearFlag(RF_DEMO_SUBST); _vm->_gfx->setPalette(_mapSavedPal); setMode(kPanelMain); _vm->_script->setNoPendingVerb(); } else if (ascii == 'q' || ascii == 'Q') { _vm->quitGame(); } break; case kPanelBoss: _vm->_render->clearFlag(RF_DEMO_SUBST); keyBossExit(); break; case kPanelProtect: if (_vm->getGameId() == GID_ITE) { if (_textInput && processTextInput(keystate)) { return true; } if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) { _vm->_script->wakeUpThreads(kWaitTypeRequest); _vm->_interface->setMode(kPanelMain); _protectHash = 0; for (char *p = _textInputString; *p; p++) _protectHash = (_protectHash << 1) + toupper(*p); } } else { // In the IHNM demo, this panel mode is set by the scripts // to flip through the pages of the help system } break; case kPanelPlacard: #ifdef ENABLE_IHNM if (_vm->getGameId() == GID_IHNM) { // Any keypress here returns the user back to the game if (!_vm->isIHNMDemo()) { _vm->_scene->clearPsychicProfile(); } else { setMode(kPanelConverse); _vm->_scene->_textList.clear(); _vm->_script->wakeUpThreads(kWaitTypeDelay); } } #endif break; } return false; } void Interface::setStatusText(const char *text, int statusColor) { if (_vm->getGameId() == GID_FTA2 || _vm->getGameId() == GID_DINO) { warning("setStatusText not implemented for SAGA2"); return; } if (_vm->getGameId() == GID_IHNM) { // Don't show the status text for the IHNM chapter selection screens (chapter 8), or // scene 0 (IHNM demo introduction) if (_vm->_scene->currentChapterNumber() == 8 || _vm->_scene->currentSceneNumber() == 0) return; } assert(text != NULL); assert(strlen(text) < STATUS_TEXT_LEN); if (_vm->_render->getFlags() & RF_MAP || _vm->_interface->getMode() == kPanelPlacard) return; Common::strlcpy(_statusText, text, STATUS_TEXT_LEN); _statusOnceColor = statusColor; drawStatusBar(); } void Interface::loadScenePortraits(int resourceId) { _scenePortraits.clear(); _vm->_sprite->loadList(resourceId, _scenePortraits); } void Interface::drawVerbPanel(PanelButton* panelButton) { PanelButton * rightButtonVerbPanelButton; PanelButton * currentVerbPanelButton; KnownColor textColor; int spriteNumber; Point point; rightButtonVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getRightButtonVerb()); currentVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getCurrentVerb()); if (panelButton->state) { textColor = kKnownColorVerbTextActive; } else if (panelButton == rightButtonVerbPanelButton) { textColor = kKnownColorVerbTextActive; } else { textColor = kKnownColorVerbText; } if (panelButton == currentVerbPanelButton) { spriteNumber = panelButton->downSpriteNumber; } else { spriteNumber = panelButton->upSpriteNumber; } point.x = _mainPanel.x + panelButton->xOffset; point.y = _mainPanel.y + panelButton->yOffset; _vm->_sprite->draw(_mainPanel.sprites, spriteNumber, point, 256); drawVerbPanelText(panelButton, textColor, kKnownColorVerbTextShadow); } void Interface::draw() { Point leftPortraitPoint; Point rightPortraitPoint; Rect rect; if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut) return; drawStatusBar(); if (_panelMode == kPanelMain || _panelMode == kPanelMap || (_panelMode == kPanelNull && _vm->isIHNMDemo())) { _mainPanel.getRect(rect); _vm->_gfx->drawRegion(rect, _mainPanel.image.getBuffer()); for (int i = 0; i < kVerbTypeIdsMax; i++) { if (_verbTypeToPanelButton[i] != NULL) { drawVerbPanel(_verbTypeToPanelButton[i]); } } } else if (_panelMode == kPanelConverse) { _conversePanel.getRect(rect); _vm->_gfx->drawRegion(rect, _conversePanel.image.getBuffer()); converseDisplayTextLines(); } if (_panelMode == kPanelMain || _panelMode == kPanelConverse || _lockedMode == kPanelMain || _lockedMode == kPanelConverse || (_panelMode == kPanelNull && _vm->isIHNMDemo())) { leftPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().leftPortraitXOffset; leftPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().leftPortraitYOffset; _vm->_sprite->draw(_defPortraits, _leftPortrait, leftPortraitPoint, 256); } if (!_inMainMode && _vm->getDisplayInfo().rightPortraitXOffset >= 0) { //FIXME: should we change !_inMainMode to _panelMode == kPanelConverse ? rightPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().rightPortraitXOffset; rightPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().rightPortraitYOffset; // This looks like hack - particularly since it's only done for // the right-side portrait - and perhaps it is! But as far as I // can tell this is what the original engine does. And it keeps // ITE from crashing when entering the Elk King's court. if (_rightPortrait >= (int)_scenePortraits.size()) _rightPortrait = 0; _vm->_sprite->draw(_scenePortraits, _rightPortrait, rightPortraitPoint, 256); } drawInventory(); } void Interface::calcOptionSaveSlider() { int totalFiles = _vm->getSaveFilesCount(); int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible; int height = _optionSaveFileSlider->height; int sliderHeight = 13; // IHNM's save file list slider has a fixed height int pos; if (totalFiles < visibleFiles) { totalFiles = visibleFiles; } if (_vm->getGameId() == GID_ITE) { // ITE's save file list slider has a dynamically computed height, depending on // the number of save games sliderHeight = visibleFiles * height / totalFiles; } if (sliderHeight < 7) { sliderHeight = 7; } if (totalFiles - visibleFiles <= 0) { pos = 0; } else { pos = _optionSaveFileTop * (height - sliderHeight) / (totalFiles - visibleFiles); } _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, _optionSaveRectTop); _optionSaveRectBottom = _optionSaveRectSlider = _optionSaveRectTop; _optionSaveRectTop.bottom = _optionSaveRectTop.top + pos; _optionSaveRectTop.top++; _optionSaveRectTop.right--; _optionSaveRectSlider.top = _optionSaveRectTop.bottom; _optionSaveRectSlider.bottom = _optionSaveRectSlider.top + sliderHeight; _optionSaveRectBottom.top = _optionSaveRectSlider.bottom; _optionSaveRectBottom.right--; } void Interface::drawPanelText(InterfacePanel *panel, PanelButton *panelButton) { const char *text; int textWidth, textHeight; Rect rect; Point textPoint; KnownColor textShadowKnownColor = kKnownColorVerbTextShadow; KnownFont textFont = kKnownFontMedium; // Button differs for CD version if (panelButton->id == kTextReadingSpeed && _vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY)) return; if (panelButton->id == kTextShowDialog && _vm->getFeatures() & GF_ITE_FLOPPY) return; if (_vm->getGameId() == GID_ITE) { text = _vm->getTextString(panelButton->id); textFont = kKnownFontMedium; textShadowKnownColor = kKnownColorVerbTextShadow; } else { if ((panelButton->id < 39 || panelButton->id > 50) && panelButton->id != kTextLoadSavedGame) { // Read non-hardcoded strings from the LUT string table, loaded from the game // data files text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[panelButton->id]); } else if (panelButton->id == kTextLoadSavedGame) { // a bit of a kludge, but it will do text = _vm->getTextString(52); } else { // Hardcoded strings in IHNM are read from the ITE hardcoded strings text = _vm->getTextString(panelButton->id); } textFont = kKnownFontVerb; textShadowKnownColor = kKnownColorTransparent; } panel->calcPanelButtonRect(panelButton, rect); if (_vm->getGameId() == GID_ITE) { textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal); textHeight = _vm->_font->getHeight(kKnownFontMedium); } else { textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal); textHeight = _vm->_font->getHeight(kKnownFontVerb); } if (panelButton->xOffset < 0) { // Special case: Centered to dialog. This is used for things like the // title of a dialog. rect.left += 2 + (panel->imageWidth - 1 - textWidth) / 2; } else { // The standard case is used for the things that look a bit like buttons // but are not clickable, e.g. texts like "Music", "Sound", etc. if (_vm->getGameId() == GID_ITE) { rect.left = rect.right - textWidth - 3; } else { rect.left = (rect.right + rect.left - textWidth) / 2; } rect.top = (rect.top + rect.bottom - textHeight) / 2; } textPoint.x = rect.left; textPoint.y = rect.top + 1; _vm->_font->textDraw(textFont, text, textPoint, _vm->KnownColor2ColorId(kKnownColorVerbText), _vm->KnownColor2ColorId(textShadowKnownColor), kFontShadow); } void Interface::drawOption() { const char *text; int fontHeight; uint idx; int fgColor; int bgColor; Rect rect; Rect rect2; PanelButton *panelButton; Point textPoint; Point sliderPoint; int spritenum = 0; _optionPanel.getRect(rect); _vm->_gfx->drawRegion(rect, _optionPanel.image.getBuffer()); for (int i = 0; i < _optionPanel.buttonsCount; i++) { panelButton = &_optionPanel.buttons[i]; if (panelButton->type == kPanelButtonOption) { if (_vm->getGameId() == GID_ITE) { drawPanelButtonText(&_optionPanel, panelButton); } else { drawPanelButtonText(&_optionPanel, panelButton, spritenum); spritenum += 2; // 2 sprites per button (lit and unlit) } } if (panelButton->type == kPanelButtonOptionText) { drawPanelText(&_optionPanel, panelButton); } } if (_optionSaveRectTop.height() > 0) { if (_vm->getGameId() == GID_ITE) _vm->_gfx->drawRect(_optionSaveRectTop, kITEColorDarkGrey); } if (_vm->getGameId() == GID_ITE) { drawButtonBox(_optionSaveRectSlider, kSlider, _optionSaveFileSlider->state > 0); } else { panelButton = &_optionPanel.buttons[0]; sliderPoint.x = _optionPanel.x + panelButton->xOffset; sliderPoint.y = _optionSaveRectSlider.top; _vm->_sprite->draw(_optionPanel.sprites, 0 + _optionSaveFileSlider->state, sliderPoint, 256); } if (_optionSaveRectBottom.height() > 0) { _vm->_gfx->drawRect(_optionSaveRectBottom, kITEColorDarkGrey); } _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect); rect.top++; rect2 = rect; fontHeight = _vm->_font->getHeight(kKnownFontSmall); for (uint j = 0; j < _vm->getDisplayInfo().optionSaveFileVisible; j++) { if (_vm->getGameId() == GID_ITE) bgColor = kITEColorDarkGrey0C; else bgColor = _vm->KnownColor2ColorId(kKnownColorBlack); fgColor = kITEColorBrightWhite; idx = j + _optionSaveFileTop; if (idx == _optionSaveFileTitleNumber) { SWAP(bgColor, fgColor); } if (idx < _vm->getSaveFilesCount()) { rect2.top = rect.top + j * (fontHeight + 1); rect2.bottom = rect2.top + fontHeight; _vm->_gfx->fillRect(rect2, bgColor); text = _vm->getSaveFile(idx)->name; textPoint.x = rect.left + 1; textPoint.y = rect2.top; if (_vm->getGameId() == GID_ITE) _vm->_font->textDraw(kKnownFontSmall, text, textPoint, fgColor, 0, kFontNormal); else _vm->_font->textDraw(kKnownFontVerb, text, textPoint, fgColor, 0, kFontNormal); } } } void Interface::drawQuit() { Rect rect; int i; PanelButton *panelButton; _quitPanel.getRect(rect); if (_vm->getGameId() == GID_ITE) drawButtonBox(rect, kButton, false); else _vm->_gfx->drawRegion(rect, _quitPanel.image.getBuffer()); for (i = 0; i < _quitPanel.buttonsCount; i++) { panelButton = &_quitPanel.buttons[i]; if (panelButton->type == kPanelButtonQuit) { drawPanelButtonText(&_quitPanel, panelButton); } if (panelButton->type == kPanelButtonQuitText) { drawPanelText(&_quitPanel, panelButton); } } } void Interface::handleQuitUpdate(const Point& mousePoint) { bool releasedButton; _quitPanel.currentButton = quitHitTest(mousePoint); releasedButton = (_quitPanel.currentButton != NULL) && (_quitPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed()); if (!_vm->mouseButtonPressed()) { _quitPanel.zeroAllButtonState(); } if (releasedButton) { setQuit(_quitPanel.currentButton); } } void Interface::handleQuitClick(const Point& mousePoint) { _quitPanel.currentButton = quitHitTest(mousePoint); _quitPanel.zeroAllButtonState(); if (_quitPanel.currentButton == NULL) { return; } _quitPanel.currentButton->state = 1; } void Interface::setQuit(PanelButton *panelButton) { _quitPanel.currentButton = NULL; switch (panelButton->id) { case kTextCancel: setMode(kPanelOption); break; case kTextQuit: #ifdef ENABLE_IHNM if (_vm->isIHNMDemo()) _vm->_scene->creditsScene(); // display sales info for IHNM demo else #endif _vm->quitGame(); break; } } void Interface::drawLoad() { Rect rect; int i; PanelButton *panelButton; _loadPanel.getRect(rect); if (_vm->getGameId() == GID_ITE) drawButtonBox(rect, kButton, false); else _vm->_gfx->drawRegion(rect, _loadPanel.image.getBuffer()); for (i = 0; i < _loadPanel.buttonsCount; i++) { panelButton = &_loadPanel.buttons[i]; if (panelButton->type == kPanelButtonLoad) { drawPanelButtonText(&_loadPanel, panelButton); } if (panelButton->type == kPanelButtonLoadText) { drawPanelText(&_loadPanel, panelButton); } } } void Interface::handleLoadUpdate(const Point& mousePoint) { bool releasedButton; _loadPanel.currentButton = loadHitTest(mousePoint); releasedButton = (_loadPanel.currentButton != NULL) && (_loadPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed()); if (!_vm->mouseButtonPressed()) { _loadPanel.zeroAllButtonState(); } if (releasedButton) { setLoad(_loadPanel.currentButton); } } void Interface::handleLoadClick(const Point& mousePoint) { _loadPanel.currentButton = loadHitTest(mousePoint); _loadPanel.zeroAllButtonState(); if (_loadPanel.currentButton == NULL) { return; } _loadPanel.currentButton->state = 1; } void Interface::setLoad(PanelButton *panelButton) { _loadPanel.currentButton = NULL; switch (panelButton->id) { case kTextOK: if (_vm->getGameId() == GID_ITE) { setMode(kPanelMain); } else { if (_vm->getSaveFilesCount() > 0) { if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) { debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); setMode(kPanelMain); _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber)); _vm->syncSoundSettings(); } } } break; case kTextCancel: // IHNM only setMode(kPanelOption); break; } } void Interface::processStatusTextInput(Common::KeyState keystate) { switch (keystate.keycode) { case Common::KEYCODE_ESCAPE: _statusTextInputState = kStatusTextInputAborted; _statusTextInput = false; _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput); break; case Common::KEYCODE_RETURN: _statusTextInputState = kStatusTextInputEntered; _statusTextInput = false; _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput); break; case Common::KEYCODE_BACKSPACE: if (_statusTextInputPos == 0) { break; } _statusTextInputPos--; _statusTextInputString[_statusTextInputPos] = 0; break; default: if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX - 1) { // -1 because of the null termination break; } if (Common::isAlnum(keystate.ascii) || (keystate.ascii == ' ')) { _statusTextInputString[_statusTextInputPos++] = keystate.ascii; _statusTextInputString[_statusTextInputPos] = 0; } } setStatusText(_statusTextInputString); } bool Interface::processTextInput(Common::KeyState keystate) { char ch[2]; char tempString[SAVE_TITLE_SIZE]; uint tempWidth; memset(tempString, 0, SAVE_TITLE_SIZE); ch[1] = 0; // IHNM has a smaller save title size than ITE. We only limit the save title size during text input // in IHNM, to preserve backwards compatibility with older save games uint save_title_size = _vm->getGameId() == GID_ITE ? SAVE_TITLE_SIZE : IHNM_SAVE_TITLE_SIZE; switch (keystate.keycode) { case Common::KEYCODE_RETURN: return false; case Common::KEYCODE_ESCAPE: _textInput = false; break; case Common::KEYCODE_BACKSPACE: if (_textInputPos <= 1) { break; } _textInputPos--; // fall through case Common::KEYCODE_DELETE: if (_textInputPos <= _textInputStringLength) { if (_textInputPos != 1) { strncpy(tempString, _textInputString, _textInputPos - 1); } if (_textInputPos != _textInputStringLength) { strncat(tempString, &_textInputString[_textInputPos], _textInputStringLength - _textInputPos); } strcpy(_textInputString, tempString); _textInputStringLength = strlen(_textInputString); } break; case Common::KEYCODE_LEFT: if (_textInputPos > 1) { _textInputPos--; } break; case Common::KEYCODE_RIGHT: if (_textInputPos <= _textInputStringLength) { _textInputPos++; } break; case Common::KEYCODE_HOME: _textInputPos = 1; break; case Common::KEYCODE_END: _textInputPos = _textInputStringLength + 1; break; default: if (((keystate.ascii <= 255) && (Common::isAlnum(keystate.ascii))) || (keystate.ascii == ' ') || (keystate.ascii == '-') || (keystate.ascii == '_')) { if (_textInputStringLength < save_title_size - 1) { ch[0] = keystate.ascii; tempWidth = _vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal); tempWidth += _vm->_font->getStringWidth(kKnownFontSmall, _textInputString, 0, kFontNormal); if (tempWidth > _textInputMaxWidth) { break; } if (_textInputPos != 1) { strncpy(tempString, _textInputString, _textInputPos - 1); strcat(tempString, ch); } if ((_textInputStringLength == 0) || (_textInputPos == 1)) { strcpy(tempString, ch); } if ((_textInputStringLength != 0) && (_textInputPos != _textInputStringLength)) { strncat(tempString, &_textInputString[_textInputPos - 1], _textInputStringLength - _textInputPos + 1); } strcpy(_textInputString, tempString); _textInputStringLength = strlen(_textInputString); _textInputPos++; } } break; } return true; } void Interface::drawTextInput(InterfacePanel *panel, PanelButton *panelButton) { Point textPoint; Rect rect; char ch[2]; int fgColor; uint i; ch[1] = 0; panel->calcPanelButtonRect(panelButton, rect); drawButtonBox(rect, kEdit, _textInput); rect.left += 4; rect.top += 4; rect.setHeight(_vm->_font->getHeight(kKnownFontSmall)); i = 0; while ((ch[0] = _textInputString[i++]) != 0) { rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal)); if ((i == _textInputPos) && _textInput) { fgColor = _vm->KnownColor2ColorId(kKnownColorBlack); _vm->_gfx->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite)); } else { fgColor = _vm->KnownColor2ColorId(kKnownColorWhite); } textPoint.x = rect.left; textPoint.y = rect.top + 1; _vm->_font->textDraw(kKnownFontSmall, ch, textPoint, fgColor, 0, kFontNormal); rect.left += rect.width(); } if (_textInput && (_textInputPos >= i)) { ch[0] = ' '; rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal)); _vm->_gfx->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite)); } } void Interface::drawSave() { Rect rect; int i; PanelButton *panelButton; _savePanel.getRect(rect); if (_vm->getGameId() == GID_ITE) drawButtonBox(rect, kButton, false); else _vm->_gfx->drawRegion(rect, _savePanel.image.getBuffer()); for (i = 0; i < _savePanel.buttonsCount; i++) { panelButton = &_savePanel.buttons[i]; if (panelButton->type == kPanelButtonSave) { drawPanelButtonText(&_savePanel, panelButton); } if (panelButton->type == kPanelButtonSaveText) { drawPanelText(&_savePanel, panelButton); } } drawTextInput(&_savePanel, _saveEdit); } void Interface::drawProtect() { Rect rect; int i; PanelButton *panelButton; _protectPanel.getRect(rect); drawButtonBox(rect, kButton, false); for (i = 0; i < _protectPanel.buttonsCount; i++) { panelButton = &_protectPanel.buttons[i]; if (panelButton->type == kPanelButtonProtectText) { drawPanelText(&_protectPanel, panelButton); } } drawTextInput(&_protectPanel, _protectEdit); } void Interface::handleSaveUpdate(const Point& mousePoint) { bool releasedButton; _savePanel.currentButton = saveHitTest(mousePoint); validateSaveButtons(); releasedButton = (_savePanel.currentButton != NULL) && (_savePanel.currentButton->state > 0) && (!_vm->mouseButtonPressed()); if (!_vm->mouseButtonPressed()) { _savePanel.zeroAllButtonState(); } if (releasedButton) { setSave(_savePanel.currentButton); } } void Interface::handleSaveClick(const Point& mousePoint) { _savePanel.currentButton = saveHitTest(mousePoint); validateSaveButtons(); _savePanel.zeroAllButtonState(); if (_savePanel.currentButton == NULL) { _textInput = false; return; } _savePanel.currentButton->state = 1; if (_savePanel.currentButton == _saveEdit) { _textInput = true; } } void Interface::setSave(PanelButton *panelButton) { _savePanel.currentButton = NULL; uint titleNumber; char *fileName; switch (panelButton->id) { case kTextSave: if (_textInputStringLength == 0) { break; } if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) { if (_vm->locateSaveFile(_textInputString, titleNumber)) { fileName = _vm->calcSaveFileName(_vm->getSaveFile(titleNumber)->slotNumber); _vm->save(fileName, _textInputString); _optionSaveFileTitleNumber = titleNumber; } else { fileName = _vm->calcSaveFileName(_vm->getNewSaveSlotNumber()); _vm->save(fileName, _textInputString); _vm->fillSaveList(); calcOptionSaveSlider(); } } else { fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); _vm->save(fileName, _textInputString); } resetSaveReminder(); _textInput = false; setMode(kPanelOption); break; case kTextCancel: _textInput = false; setMode(kPanelOption); break; } } void Interface::resetSaveReminder() { _vm->getTimerManager()->removeTimerProc(&saveReminderCallback); _vm->getTimerManager()->installTimerProc(&saveReminderCallback, TIMETOSAVE, this, "sagaSaveReminder"); setSaveReminderState(1); } void Interface::handleOptionUpdate(const Point& mousePoint) { int16 mouseY; Rect rect; int totalFiles = _vm->getSaveFilesCount(); int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible; bool releasedButton; if (_vm->mouseButtonPressed()) { if (_optionSaveFileSlider->state > 0) { _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, rect); mouseY = mousePoint.y - rect.top -_optionSaveFileMouseOff; if (mouseY < 0) mouseY = 0; if (totalFiles - visibleFiles <= 0) { _optionSaveFileTop = 0; } else { _optionSaveFileTop = mouseY * (totalFiles - visibleFiles) / (_optionSaveFileSlider->height - _optionSaveRectSlider.height()); } _optionSaveFileTop = CLIP(_optionSaveFileTop, 0, totalFiles - visibleFiles); calcOptionSaveSlider(); } } _optionPanel.currentButton = optionHitTest(mousePoint); validateOptionButtons(); releasedButton = (_optionPanel.currentButton != NULL) && (_optionPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed()); if (!_vm->mouseButtonPressed()) { _optionPanel.zeroAllButtonState(); } if (releasedButton) { setOption(_optionPanel.currentButton); } } void Interface::handleOptionClick(const Point& mousePoint) { Rect rect; _optionPanel.currentButton = optionHitTest(mousePoint); validateOptionButtons(); _optionPanel.zeroAllButtonState(); if (_optionPanel.currentButton == NULL) { return; } if (_optionPanel.currentButton == _optionSaveFileSlider) { if ((_optionSaveRectTop.height() > 0) && (mousePoint.y < _optionSaveRectTop.bottom)) { _optionSaveFileTop -= _vm->getDisplayInfo().optionSaveFileVisible; } else { if ((_optionSaveRectBottom.height() > 0) && (mousePoint.y >= _optionSaveRectBottom.top)) { _optionSaveFileTop += _vm->getDisplayInfo().optionSaveFileVisible; } else { if (_vm->getDisplayInfo().optionSaveFileVisible < _vm->getSaveFilesCount()) { _optionSaveFileMouseOff = mousePoint.y - _optionSaveRectSlider.top; _optionPanel.currentButton->state = 1; } } } _optionSaveFileTop = CLIP(_optionSaveFileTop, 0, _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible); calcOptionSaveSlider(); } else { if (_optionPanel.currentButton == _optionSaveFilePanel) { _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect); _optionSaveFileTitleNumber = (mousePoint.y - rect.top) / (_vm->_font->getHeight(kKnownFontSmall) + 1); if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) { _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1; } _optionSaveFileTitleNumber += _optionSaveFileTop; if (_optionSaveFileTitleNumber >= _vm->getSaveFilesCount()) { _optionSaveFileTitleNumber = _vm->getSaveFilesCount() - 1; } } else { _optionPanel.currentButton->state = 1; } } } void Interface::handleChapterSelectionUpdate(const Point& mousePoint) { uint16 objectId; int hitZoneIndex; const HitZone * hitZone; // FIXME: Original handled more object types here. objectId = _vm->_actor->hitTest(mousePoint, true); if (objectId == ID_NOTHING) { hitZoneIndex = _vm->_scene->_objectMap->hitTest(mousePoint); if ((hitZoneIndex != -1)) { hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex); objectId = hitZone->getHitZoneId(); } } if (objectId != _vm->_script->_pointerObject) { _vm->_script->_pointerObject = objectId; } } void Interface::handleChapterSelectionClick(const Point& mousePoint) { int obj = _vm->_script->_pointerObject; _vm->_actor->abortSpeech(); if (obj) { int script = 0; HitZone *hitZone; ActorData *a; ObjectData *o; Event event; switch (objectTypeId(obj)) { case kGameObjectHitZone: hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(obj)); if (hitZone == NULL) return; if (hitZone->getFlags() & kHitZoneEnabled) script = hitZone->getScriptNumber(); break; case kGameObjectActor: a = _vm->_actor->getActor(obj); script = a->_scriptEntrypointNumber; break; case kGameObjectObject: o = _vm->_actor->getObj(obj); script = o->_scriptEntrypointNumber; break; } if (script > 0) { event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventExecNonBlocking; event.time = 0; event.param = _vm->_scene->getScriptModuleNumber(); event.param2 = script; event.param3 = _vm->_script->getVerbType(kVerbUse); // Action event.param4 = obj; // Object event.param5 = 0; // With Object event.param6 = obj; // Actor _vm->_events->queue(event); } } } void Interface::setOption(PanelButton *panelButton) { _optionPanel.currentButton = NULL; switch (panelButton->id) { case kTextContinuePlaying: ConfMan.flushToDisk(); if (_vm->getGameId() == GID_ITE) { setMode(kPanelMain); } else { if (_vm->_scene->currentChapterNumber() == 8) { setMode(kPanelChapterSelection); } else if (_vm->_scene->isNonInteractiveIHNMDemoPart()) { setMode(kPanelNull); } else { setMode(kPanelMain); } } break; case kTextQuitGame: setMode(kPanelQuit); break; case kTextLoad: if (_vm->getGameId() == GID_ITE) { if (_vm->getSaveFilesCount() > 0) { if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) { debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber); setMode(kPanelMain); _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber)); _vm->syncSoundSettings(); } } } else { setMode(kPanelLoad); } break; case kTextSave: // Disallow saving in the non-interactive part of the IHNM demo (original demo didn't support saving at all) if (_vm->_scene->isNonInteractiveIHNMDemoPart()) return; if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) { _textInputString[0] = 0; } else { strcpy(_textInputString, _vm->getSaveFile(_optionSaveFileTitleNumber)->name); } setMode(kPanelSave); break; case kTextReadingSpeed: if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY)) { _vm->_subtitlesEnabled = !_vm->_subtitlesEnabled; ConfMan.setBool("subtitles", _vm->_subtitlesEnabled); } else { _vm->_readingSpeed = (_vm->_readingSpeed + 1) % 4; _vm->setTalkspeed(_vm->_readingSpeed); } break; case kTextMusic: _vm->_musicVolume = _vm->_musicVolume + 25; if (_vm->_musicVolume > 255) _vm->_musicVolume = 0; _vm->_music->setVolume(_vm->_musicVolume, 1); ConfMan.setInt("music_volume", _vm->_musicVolume); break; case kTextSound: _vm->_soundVolume = _vm->_soundVolume + 25; if (_vm->_soundVolume > 255) _vm->_soundVolume = 0; ConfMan.setInt("sfx_volume", _vm->_soundVolume); _vm->_sound->setVolume(); break; case kTextVoices: if (_vm->_voiceFilesExist) { if (_vm->_subtitlesEnabled && _vm->_voicesEnabled) { // Both _vm->_subtitlesEnabled = false; // Set it to "Audio" _vm->_voicesEnabled = true; // Not necessary, just for completeness } else if (!_vm->_subtitlesEnabled && _vm->_voicesEnabled) { _vm->_subtitlesEnabled = true; // Set it to "Text" _vm->_voicesEnabled = false; } else if (_vm->_subtitlesEnabled && !_vm->_voicesEnabled) { _vm->_subtitlesEnabled = true; // Set it to "Both" _vm->_voicesEnabled = true; } } else { _vm->_subtitlesEnabled = true; // Set it to "Text" _vm->_voicesEnabled = false; } _vm->_speechVolume = _vm->_speechVolume + 25; if (_vm->_speechVolume > 255) _vm->_speechVolume = 0; ConfMan.setInt("speech_volume", _vm->_speechVolume); _vm->_sound->setVolume(); ConfMan.setBool("subtitles", _vm->_subtitlesEnabled); ConfMan.setBool("voices", _vm->_voicesEnabled); break; } } void Interface::update(const Point& mousePoint, int updateFlag) { if (!_active && _panelMode == kPanelNull && (updateFlag & UPDATE_MOUSECLICK)) _vm->_actor->abortSpeech(); if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut || !_active) { // When opening the psychic profile, or the options screen in the non-interactive part of the IHNM demo, // the interface is locked (_active is false) // Don't return in those cases, so that mouse actions can be processed if (_vm->getGameId() == GID_ITE) { return; } else { if (_panelMode == kPanelPlacard && (updateFlag & UPDATE_MOUSECLICK)) { // the psychic profile or the special screen in IHNM demo is open, don't return } else if (_panelMode == kPanelOption || _panelMode == kPanelQuit) { // options/quit panel is open, set interface to active and don't return _vm->_actor->abortSpeech(); // abort any speech being played _active = true; } else { return; } } } if (_statusTextInput) { return; } switch (_panelMode) { case kPanelMain: if (updateFlag & UPDATE_MOUSEMOVE) { bool lastWasPlayfield = _lastMousePoint.y < _vm->_scene->getHeight(); if (mousePoint.y < _vm->_scene->getHeight()) { if (!lastWasPlayfield) { handleMainUpdate(mousePoint); } _vm->_script->whichObject(mousePoint); } else { if (lastWasPlayfield) { _vm->_script->setNonPlayfieldVerb(); } handleMainUpdate(mousePoint); } } else { if (updateFlag & UPDATE_MOUSECLICK) { if (mousePoint.y < _vm->_scene->getHeight()) { _vm->_script->playfieldClick(mousePoint, (updateFlag & UPDATE_LEFTBUTTONCLICK) != 0); } else { handleMainClick(mousePoint); } } } break; case kPanelConverse: if (updateFlag & UPDATE_MOUSEMOVE) { handleConverseUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { handleConverseClick(mousePoint); } if (updateFlag & UPDATE_WHEELUP) { converseChangePos(-1); } if (updateFlag & UPDATE_WHEELDOWN) { converseChangePos(1); } if (_vm->_scene->isITEPuzzleScene()) { _vm->_puzzle->handleClick(mousePoint); } } break; case kPanelOption: if (updateFlag & UPDATE_MOUSEMOVE) { handleOptionUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { handleOptionClick(mousePoint); } if (updateFlag & UPDATE_WHEELUP) { if (_optionSaveFileTop) _optionSaveFileTop--; calcOptionSaveSlider(); } if (updateFlag & UPDATE_WHEELDOWN) { if (_optionSaveFileTop < _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible) _optionSaveFileTop++; calcOptionSaveSlider(); } } break; case kPanelQuit: if (updateFlag & UPDATE_MOUSEMOVE) { handleQuitUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { handleQuitClick(mousePoint); } } break; case kPanelLoad: if (updateFlag & UPDATE_MOUSEMOVE) { handleLoadUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { handleLoadClick(mousePoint); } } break; case kPanelSave: if (updateFlag & UPDATE_MOUSEMOVE) { handleSaveUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { handleSaveClick(mousePoint); } } break; case kPanelMap: if (updateFlag & UPDATE_MOUSECLICK) mapPanelClean(); break; case kPanelSceneSubstitute: if (updateFlag & UPDATE_MOUSECLICK) { _vm->_render->clearFlag(RF_DEMO_SUBST); _vm->_gfx->setPalette(_mapSavedPal); setMode(kPanelMain); _vm->_script->setNoPendingVerb(); } break; case kPanelChapterSelection: if (updateFlag & UPDATE_MOUSEMOVE) { handleChapterSelectionUpdate(mousePoint); } else { if (updateFlag & UPDATE_MOUSECLICK) { Rect rect; rect.left = _vm->getDisplayInfo().saveReminderXOffset; rect.top = _vm->getDisplayInfo().saveReminderYOffset; rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth; rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight; if (rect.contains(mousePoint)) setMode(kPanelOption); else handleChapterSelectionClick(mousePoint); } } break; case kPanelProtect: // No mouse interaction break; case kPanelPlacard: #ifdef ENABLE_IHNM if (_vm->getGameId() == GID_IHNM) { // Any mouse click here returns the user back to the game if (updateFlag & UPDATE_MOUSECLICK) { if (!_vm->isIHNMDemo()) { _vm->_scene->clearPsychicProfile(); _vm->_script->wakeUpThreads(kWaitTypeDelay); } else { setMode(kPanelConverse); _vm->_scene->_textList.clear(); _vm->_script->wakeUpThreads(kWaitTypeDelay); } } } #endif break; case kPanelNull: #ifdef ENABLE_IHNM if (_vm->_scene->isNonInteractiveIHNMDemoPart() && (updateFlag & UPDATE_MOUSECLICK)) _vm->_scene->showIHNMDemoSpecialScreen(); #endif break; } _lastMousePoint = mousePoint; } void Interface::drawStatusBar() { Rect rect; Point textPoint; int stringWidth; int color; // The default colors in the Spanish version of IHNM are shifted by one // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This // also applies to the German and French versions (bug #7064 - "IHNM: // text mistake in german version"). int offset = (_vm->getFeatures() & GF_IHNM_COLOR_FIX) ? 1 : 0; // Disable the status text in IHNM when the chapter is 8 if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8) return; // Don't draw the status bar while fading out if (_fadeMode == kFadeOut) return; // Erase background of status bar rect.left = _vm->getDisplayInfo().statusXOffset; rect.top = _vm->getDisplayInfo().statusYOffset; rect.right = rect.left + _vm->getDisplayInfo().width; rect.bottom = rect.top + _vm->getDisplayInfo().statusHeight; _vm->_gfx->drawRect(rect, _vm->getDisplayInfo().statusBGColor - offset); stringWidth = _vm->_font->getStringWidth(kKnownFontSmall, _statusText, 0, kFontNormal); if (_statusOnceColor == -1) color = _vm->getDisplayInfo().statusTextColor - offset; else color = _statusOnceColor; textPoint.x = _vm->getDisplayInfo().statusXOffset + (_vm->getDisplayInfo().statusWidth - stringWidth) / 2; textPoint.y = _vm->getDisplayInfo().statusYOffset + _vm->getDisplayInfo().statusTextY; if (_vm->getGameId() == GID_ITE) _vm->_font->textDraw(kKnownFontSmall, _statusText, textPoint, color, 0, kFontNormal); else _vm->_font->textDraw(kKnownFontVerb, _statusText, textPoint, color, 0, kFontNormal); if (_saveReminderState > 0) { rect.left = _vm->getDisplayInfo().saveReminderXOffset; rect.top = _vm->getDisplayInfo().saveReminderYOffset; rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth; rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight; _vm->_sprite->draw(_vm->_sprite->_saveReminderSprites, _vm->getDisplayInfo().saveReminderFirstSpriteNumber + _saveReminderState - 1, rect, 256); } } void Interface::handleMainClick(const Point& mousePoint) { PanelButton *panelButton; panelButton = verbHitTest(mousePoint); if (panelButton) { _vm->_script->setVerb(panelButton->id); return; } panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons); if (panelButton != NULL) { if (panelButton->type == kPanelButtonArrow) { panelButton->state = 1; converseChangePos(panelButton->id); } if (panelButton->type == kPanelButtonInventory) { if (_vm->_script->_pointerObject != ID_NOTHING) { _vm->_script->hitObject(_vm->leftMouseButtonPressed()); } if (_vm->_script->_pendingVerb) { _vm->_actor->_protagonist->_currentAction = kActionWait; _vm->_script->doVerb(); } } } else { if (_saveReminderState > 0) { Rect rect; rect.left = _vm->getDisplayInfo().saveReminderXOffset; rect.top = _vm->getDisplayInfo().saveReminderYOffset; rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth; rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight; if (rect.contains(mousePoint)) { setMode(kPanelOption); } } } } void Interface::handleMainUpdate(const Point& mousePoint) { PanelButton *panelButton; panelButton = verbHitTest(mousePoint); if (_mainPanel.currentButton != panelButton) { if (_mainPanel.currentButton) { if (_mainPanel.currentButton->type == kPanelButtonVerb) { setVerbState(_mainPanel.currentButton->id, 0); } } if (panelButton) { setVerbState(panelButton->id, 1); } } if (panelButton) { _mainPanel.currentButton = panelButton; return; } if (!_vm->mouseButtonPressed()) { // remove pressed flag if (_inventoryUpButton) { _inventoryUpButton->state = 0; _inventoryDownButton->state = 0; } } panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons); bool changed = false; if ((panelButton != NULL) && (panelButton->type == kPanelButtonArrow)) { if (panelButton->state == 1) { inventoryChangePos(panelButton->id); } changed = true; } else { _vm->_script->whichObject(mousePoint); } changed = changed || (panelButton != _mainPanel.currentButton); _mainPanel.currentButton = panelButton; if (changed) { draw(); } } //inventory stuff void Interface::inventoryChangePos(int chg) { // Arrows will scroll the inventory up or down up to 4 items for (int i = 1; i <= 4; i++) { if ((chg < 0 && _inventoryStart + chg >= 0) || (chg > 0 && _inventoryStart < _inventoryEnd)) { _inventoryStart += chg; } } draw(); } void Interface::inventorySetPos(int key) { _inventoryBox = key - '1'; _inventoryPos = _inventoryStart + _inventoryBox; if (_inventoryPos >= _inventoryCount) _inventoryPos = -1; } void Interface::updateInventory(int pos) { int cols = _vm->getDisplayInfo().inventoryColumns; if (pos >= _inventoryCount) { pos = _inventoryCount - 1; } if (pos < 0) { pos = 0; } _inventoryStart = (pos - cols) / cols * cols; if (_inventoryStart < 0) { _inventoryStart = 0; } _inventoryEnd = (_inventoryCount - 1 - cols) / cols * cols; if (_inventoryEnd < 0) { _inventoryEnd = 0; } } void Interface::addToInventory(int objectId) { if (uint(_inventoryCount) >= _inventory.size()) { return; } for (int i = _inventoryCount; i > 0; i--) { _inventory[i] = _inventory[i - 1]; } _inventory[0] = objectId; _inventoryCount++; _inventoryPos = 0; updateInventory(0); draw(); } void Interface::removeFromInventory(int objectId) { int j = inventoryItemPosition(objectId); if (j == -1) { return; } int i; for (i = j; i < _inventoryCount - 1; i++) { _inventory[i] = _inventory[i + 1]; } --_inventoryCount; _inventory[_inventoryCount] = 0; updateInventory(j); draw(); } void Interface::clearInventory() { for (int i = 0; i < _inventoryCount; i++) _inventory[i] = 0; _inventoryCount = 0; updateInventory(0); } int Interface::inventoryItemPosition(int objectId) { for (int i = 0; i < _inventoryCount; i++) if (_inventory[i] == objectId) return i; return -1; } void Interface::drawInventory() { if (!isInMainMode()) return; Rect rect; int ci = _inventoryStart; ObjectData *obj; if (_inventoryStart != 0) { drawPanelButtonArrow(&_mainPanel, _inventoryUpButton); } if (_inventoryStart != _inventoryEnd) { drawPanelButtonArrow(&_mainPanel, _inventoryDownButton); } for (int i = 0; i < _mainPanel.buttonsCount; i++) { if (_mainPanel.buttons[i].type != kPanelButtonInventory) { continue; } _mainPanel.calcPanelButtonRect(&_mainPanel.buttons[i], rect); if (_vm->getGameId() == GID_ITE) _vm->_gfx->drawRect(rect, kITEColorDarkGrey); else _vm->_gfx->drawRect(rect, _vm->KnownColor2ColorId(kKnownColorBlack)); if (ci < _inventoryCount) { obj = _vm->_actor->getObj(_inventory[ci]); _vm->_sprite->draw(_vm->_sprite->_inventorySprites, obj->_spriteListResourceId, rect, 256); } ci++; } } void Interface::setVerbState(int verb, int state) { PanelButton * panelButton = getPanelButtonByVerbType(verb); if (panelButton == NULL) return; if (state == 2) { state = (_mainPanel.currentButton == panelButton) ? 1 : 0; } panelButton->state = state; draw(); } void Interface::drawButtonBox(const Rect& rect, ButtonKind kind, bool down) { byte cornerColor; byte frameColor; byte fillColor; byte solidColor; byte odl, our, idl, iur; switch (kind) { case kSlider: cornerColor = 0x8b; frameColor = _vm->KnownColor2ColorId(kKnownColorBlack); fillColor = kITEColorLightBlue96; odl = kITEColorDarkBlue8a; our = kITEColorLightBlue92; idl = 0x89; iur = 0x94; solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96; break; case kEdit: if (_vm->getGameId() == GID_ITE) { cornerColor = frameColor = fillColor = kITEColorLightBlue96; our = kITEColorDarkBlue8a; odl = kITEColorLightBlue94; solidColor = down ? kITEColorBlue : kITEColorDarkGrey0C; } else { cornerColor = frameColor = fillColor = _vm->KnownColor2ColorId(kKnownColorBlack); our = odl = solidColor = _vm->KnownColor2ColorId(kKnownColorBlack); } iur = 0x97; idl = 0x95; break; default: cornerColor = 0x8b; frameColor = _vm->KnownColor2ColorId(kKnownColorBlack); solidColor = fillColor = kITEColorLightBlue96; odl = kITEColorDarkBlue8a; our = kITEColorLightBlue94; idl = 0x97; iur = 0x95; if (down) { SWAP(odl, our); SWAP(idl, iur); } break; } int x = rect.left; int y = rect.top; int w = rect.width(); int h = rect.height(); int xe = rect.right - 1; int ye = rect.bottom - 1; _vm->_gfx->setPixelColor(x, y, cornerColor); _vm->_gfx->setPixelColor(x, ye, cornerColor); _vm->_gfx->setPixelColor(xe, y, cornerColor); _vm->_gfx->setPixelColor(xe, ye, cornerColor); _vm->_gfx->hLine(x + 1, y, x + w - 2, frameColor); _vm->_gfx->hLine(x + 1, ye, x + w - 2, frameColor); _vm->_gfx->vLine(x, y + 1, y + h - 2, frameColor); _vm->_gfx->vLine(xe, y + 1, y + h - 2, frameColor); x++; y++; xe--; ye--; w -= 2; h -= 2; _vm->_gfx->vLine(x, y, y + h - 1, odl); _vm->_gfx->hLine(x, ye, x + w - 1, odl); _vm->_gfx->vLine(xe, y, y + h - 2, our); _vm->_gfx->hLine(x + 1, y, x + 1 + w - 2, our); x++; y++; xe--; ye--; w -= 2; h -= 2; _vm->_gfx->setPixelColor(x, y, fillColor); _vm->_gfx->setPixelColor(xe, ye, fillColor); _vm->_gfx->vLine(x, y + 1, y + 1 + h - 2, idl); _vm->_gfx->hLine(x + 1, ye, x + 1 + w - 2, idl); _vm->_gfx->vLine(xe, y, y + h - 2, iur); _vm->_gfx->hLine(x + 1, y, x + 1 + w - 2, iur); x++; y++; w -= 2; h -= 2; Common::Rect fill(x, y, x + w, y + h); _vm->_gfx->fillRect(fill, solidColor); _vm->_render->addDirtyRect(rect); } static const int readingSpeeds[] = { kTextClick, kTextSlow, kTextMid, kTextFast }; void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelButton, int spritenum) { const char *text; int textId; int textWidth; int textHeight; Point point; Point texturePoint; KnownColor textColor; Rect rect; int litButton = 0; KnownColor textShadowKnownColor = kKnownColorVerbTextShadow; KnownFont textFont = kKnownFontMedium; textId = panelButton->id; switch (panelButton->id) { case kTextReadingSpeed: if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY)) { if (_vm->_subtitlesEnabled) textId = kTextOn; else textId = kTextOff; } else { textId = readingSpeeds[_vm->_readingSpeed]; } break; case kTextMusic: if (_vm->_musicVolume) { textId = kText10Percent + _vm->_musicVolume / 25 - 1; if (textId > kTextMax) { textId = kTextMax; } } else textId = kTextOff; break; case kTextSound: if (_vm->_soundVolume) { textId = kText10Percent + _vm->_soundVolume / 25 - 1; if (textId > kTextMax) { textId = kTextMax; } } else textId = kTextOff; break; case kTextVoices: if (_vm->_subtitlesEnabled && _vm->_voicesEnabled) textId = kTextBoth; else if (_vm->_subtitlesEnabled && !_vm->_voicesEnabled) textId = kTextText; else if (!_vm->_subtitlesEnabled && _vm->_voicesEnabled) textId = kTextAudio; break; } if (_vm->getGameId() == GID_ITE) { if (textId > kTextEnterProtectAnswer) error("This should not happen. Please report to ScummVM Team how you achieved this error."); text = _vm->getTextString(textId); textFont = kKnownFontMedium; textShadowKnownColor = kKnownColorVerbTextShadow; textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal); textHeight = _vm->_font->getHeight(kKnownFontMedium); } else { if (textId < 39 || textId > 50) { // Read non-hardcoded strings from the LUT string table, loaded from the game // data files text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[textId]); } else { // Hardcoded strings in IHNM are read from the ITE hardcoded strings text = _vm->getTextString(textId); } textFont = kKnownFontVerb; textShadowKnownColor = kKnownColorTransparent; textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal); textHeight = _vm->_font->getHeight(kKnownFontVerb); } point.x = panel->x + panelButton->xOffset + (panelButton->width / 2) - (textWidth / 2); point.y = panel->y + panelButton->yOffset + (panelButton->height / 2) - (textHeight / 2); if (panelButton == panel->currentButton) { textColor = kKnownColorVerbTextActive; } else { textColor = kKnownColorVerbText; } panel->calcPanelButtonRect(panelButton, rect); if (_vm->getGameId() == GID_ITE) { drawButtonBox(rect, kButton, panelButton->state > 0); } else { litButton = panelButton->state > 0; if (panel == &_optionPanel) { texturePoint.x = _optionPanel.x + panelButton->xOffset - 1; texturePoint.y = _optionPanel.y + panelButton->yOffset - 1; _vm->_sprite->draw(_optionPanel.sprites, spritenum + 2 + litButton, texturePoint, 256); } else if (panel == &_quitPanel) { texturePoint.x = _quitPanel.x + panelButton->xOffset - 3; texturePoint.y = _quitPanel.y + panelButton->yOffset - 3; _vm->_sprite->draw(_quitPanel.sprites, litButton, texturePoint, 256); } else if (panel == &_savePanel) { texturePoint.x = _savePanel.x + panelButton->xOffset - 3; texturePoint.y = _savePanel.y + panelButton->yOffset - 3; _vm->_sprite->draw(_savePanel.sprites, litButton, texturePoint, 256); // Input text box sprite texturePoint.x = _savePanel.x + _saveEdit->xOffset - 2; texturePoint.y = _savePanel.y + _saveEdit->yOffset - 2; _vm->_sprite->draw(_savePanel.sprites, 2, texturePoint, 256); } else if (panel == &_loadPanel) { texturePoint.x = _loadPanel.x + panelButton->xOffset - 3; texturePoint.y = _loadPanel.y + panelButton->yOffset - 3; _vm->_sprite->draw(_loadPanel.sprites, litButton, texturePoint, 256); } else { // revert to default behavior drawButtonBox(rect, kButton, panelButton->state > 0); } } _vm->_font->textDraw(textFont, text, point, _vm->KnownColor2ColorId(textColor), _vm->KnownColor2ColorId(textShadowKnownColor), kFontShadow); } void Interface::drawPanelButtonArrow(InterfacePanel *panel, PanelButton *panelButton) { Point point; int spriteNumber; if (panel->currentButton == panelButton) { if (panelButton->state != 0) { spriteNumber = panelButton->downSpriteNumber; } else { spriteNumber = panelButton->overSpriteNumber; } } else { spriteNumber = panelButton->upSpriteNumber; } point.x = panel->x + panelButton->xOffset; point.y = panel->y + panelButton->yOffset; if (_vm->getGameId() == GID_ITE) _vm->_sprite->draw(_vm->_sprite->_mainSprites, spriteNumber, point, 256); else _vm->_sprite->draw(_vm->_sprite->_arrowSprites, spriteNumber, point, 256); } void Interface::drawVerbPanelText(PanelButton *panelButton, KnownColor textKnownColor, KnownColor textShadowKnownColor) { const char *text; int textWidth; Point point; int textId; if (_vm->getGameId() == GID_ITE) { textId = verbToTextIdITE[panelButton->id - 1]; text = _vm->getTextString(textId); } else { textId = panelButton->id; text = _vm->_script->_mainStrings.getString(textId + 1); textShadowKnownColor = kKnownColorTransparent; } textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal); if (_vm->getGameId() == GID_ITE) { point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - 1 - textWidth) / 2; point.y = _mainPanel.y + panelButton->yOffset + 1; } else { point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - textWidth) / 2; point.y = _mainPanel.y + panelButton->yOffset + 12; } _vm->_font->textDraw(kKnownFontVerb, text, point, _vm->KnownColor2ColorId(textKnownColor), _vm->KnownColor2ColorId(textShadowKnownColor), (textShadowKnownColor != kKnownColorTransparent) ? kFontShadow : kFontNormal); } // Converse stuff void Interface::converseClear() { for (int i = 0; i < CONVERSE_MAX_TEXTS; i++) { _converseText[i].text.clear(); _converseText[i].stringNum = -1; _converseText[i].replyId = 0; _converseText[i].replyFlags = 0; _converseText[i].replyBit = 0; } _converseTextCount = 0; _converseStrCount = 0; _converseStartPos = 0; _converseEndPos = 0; _conversePos = -1; } bool Interface::converseAddText(const char *text, int strId, int replyId, byte replyFlags, int replyBit) { int count = 0; // count how many pieces of text per string int i; int len; byte c; assert(strlen(text) < CONVERSE_MAX_WORK_STRING); Common::strlcpy(_converseWorkString, text, CONVERSE_MAX_WORK_STRING); while (1) { len = strlen(_converseWorkString); for (i = len; i >= 0; i--) { c = _converseWorkString[i]; if (_vm->getGameId() == GID_ITE) { if ((c == ' ' || c == '\0') && (_vm->_font->getStringWidth(kKnownFontSmall, _converseWorkString, i, kFontNormal) <= _vm->getDisplayInfo().converseMaxTextWidth)) break; } else { if ((c == ' ' || c == '\0') && (_vm->_font->getStringWidth(kKnownFontVerb, _converseWorkString, i, kFontNormal) <= _vm->getDisplayInfo().converseMaxTextWidth)) break; } } if (i < 0) { return true; } if (_converseTextCount == CONVERSE_MAX_TEXTS) { return true; } _converseText[_converseTextCount].text.resize(i + 1); strncpy(&_converseText[_converseTextCount].text.front(), _converseWorkString, i); _converseText[_converseTextCount].strId = strId; _converseText[_converseTextCount].text[i] = 0; _converseText[_converseTextCount].textNum = count; _converseText[_converseTextCount].stringNum = _converseStrCount; _converseText[_converseTextCount].replyId = replyId; _converseText[_converseTextCount].replyFlags = replyFlags; _converseText[_converseTextCount].replyBit = replyBit; _converseTextCount++; count++; if (len == i) break; strncpy(_converseWorkString, &_converseWorkString[i + 1], len - i); } _converseStrCount++; return false; } void Interface::converseDisplayText() { int end; _converseStartPos = 0; end = _converseTextCount - _vm->getDisplayInfo().converseTextLines; if (end < 0) end = 0; _converseEndPos = end; draw(); } void Interface::converseSetTextLines(int row) { int pos = row + _converseStartPos; if (pos >= _converseTextCount) pos = -1; if (pos != _conversePos) { _conversePos = pos; draw(); } } void Interface::converseDisplayTextLines() { int relPos; byte foregnd; byte backgnd; byte bulletForegnd; byte bulletBackgnd; const char *str; char bullet[2] = { (char)0xb7, 0 }; assert(_conversePanel.buttonsCount >= 6); Rect rect(8, _vm->getDisplayInfo().converseTextLines * _vm->getDisplayInfo().converseTextHeight); rect.moveTo(_conversePanel.x + _conversePanel.buttons[0].xOffset, _conversePanel.y + _conversePanel.buttons[0].yOffset); Point textPoint; if (_vm->getGameId() == GID_ITE) { bulletForegnd = kITEColorGreen; bulletBackgnd = kITEColorBlack; } else { bulletForegnd = _vm->KnownColor2ColorId(kKnownColorBrightWhite); bulletBackgnd = _vm->KnownColor2ColorId(kKnownColorBlack); bullet[0] = '>'; // different bullet in IHNM } if (_vm->getGameId() == GID_ITE) _vm->_gfx->drawRect(rect, kITEColorDarkGrey); // fill bullet place else if (_vm->getGameId() == GID_IHNM) // TODO: Add these to IHNM_DisplayInfo? _vm->_gfx->drawRect(Common::Rect(118, 345, 603, 463), _vm->KnownColor2ColorId(kKnownColorBlack)); // fill converse rect for (int i = 0; i < _vm->getDisplayInfo().converseTextLines; i++) { relPos = _converseStartPos + i; if (_converseTextCount <= relPos) { break; } if (_conversePos >= 0 && _converseText[_conversePos].stringNum == _converseText[relPos].stringNum) { if (_vm->getGameId() == GID_ITE) { foregnd = kITEColorBrightWhite; backgnd = (!_vm->leftMouseButtonPressed()) ? kITEColorDarkGrey : kITEColorGrey; } else { foregnd = _vm->KnownColor2ColorId(kKnownColorVerbTextActive); backgnd = _vm->KnownColor2ColorId(kKnownColorVerbTextActive); } } else { if (_vm->getGameId() == GID_ITE) { foregnd = kITEColorBlue; backgnd = kITEColorDarkGrey; } else { foregnd = _vm->KnownColor2ColorId(kKnownColorBrightWhite); backgnd = _vm->KnownColor2ColorId(kKnownColorBlack); } } _conversePanel.calcPanelButtonRect(&_conversePanel.buttons[i], rect); rect.left += 8; _vm->_gfx->drawRect(rect, backgnd); str = &_converseText[relPos].text.front(); if (_converseText[relPos].textNum == 0) { // first entry textPoint.x = rect.left - 6; textPoint.y = rect.top; if (_vm->getGameId() == GID_ITE) _vm->_font->textDraw(kKnownFontSmall, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap)); else _vm->_font->textDraw(kKnownFontVerb, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap)); } textPoint.x = rect.left + 1; textPoint.y = rect.top; if (_vm->getGameId() == GID_ITE) _vm->_font->textDraw(kKnownFontSmall, str, textPoint, foregnd, kITEColorBlack, kFontShadow); else _vm->_font->textDraw(kKnownFontVerb, str, textPoint, foregnd, _vm->KnownColor2ColorId(kKnownColorBlack), kFontShadow); } if (_converseStartPos != 0) { drawPanelButtonArrow(&_conversePanel, _converseUpButton); } if (_converseStartPos != _converseEndPos) { drawPanelButtonArrow(&_conversePanel, _converseDownButton); } } void Interface::converseChangePos(int chg) { // Arrows will scroll the converse panel or down up to 4 conversation options for (int i = 1; i <= 4; i++) { if ((chg < 0 && _converseStartPos + chg >= 0) || (chg > 0 && _converseStartPos < _converseEndPos)) { _converseStartPos += chg; } } draw(); } void Interface::converseSetPos(int key) { Converse *ct; int selection = key - '1'; if (selection >= _converseTextCount) return; converseSetTextLines(selection); ct = &_converseText[_conversePos]; _vm->_script->finishDialog(ct->strId, ct->replyId, ct->replyFlags, ct->replyBit); if (_vm->_scene->isITEPuzzleScene()) _vm->_puzzle->handleReply(ct->replyId); _conversePos = -1; } void Interface::handleConverseUpdate(const Point& mousePoint) { bool changed; PanelButton *last = _conversePanel.currentButton; if (!_vm->mouseButtonPressed()) { // remove pressed flag if (_converseUpButton) { _converseUpButton->state = 0; _converseDownButton->state = 0; } } _conversePanel.currentButton = converseHitTest(mousePoint); changed = last != _conversePanel.currentButton; if (_conversePanel.currentButton == NULL) { _conversePos = -1; if (changed) { draw(); } return; } if (_conversePanel.currentButton->type == kPanelButtonConverseText) { converseSetTextLines(_conversePanel.currentButton->id); } if (_conversePanel.currentButton->type == kPanelButtonArrow) { if (_conversePanel.currentButton->state == 1) { converseChangePos(_conversePanel.currentButton->id); } draw(); } } void Interface::handleConverseClick(const Point& mousePoint) { _conversePanel.currentButton = converseHitTest(mousePoint); if (_conversePanel.currentButton == NULL) { return; } if (_conversePanel.currentButton->type == kPanelButtonConverseText) { converseSetPos(_conversePanel.currentButton->ascii); } if (_conversePanel.currentButton->type == kPanelButtonArrow) { _conversePanel.currentButton->state = 1; converseChangePos(_conversePanel.currentButton->id); } } void Interface::saveState(Common::OutSaveFile *out) { out->writeUint16LE(_inventoryCount); for (int i = 0; i < _inventoryCount; i++) { out->writeUint16LE(_inventory[i]); } } void Interface::loadState(Common::InSaveFile *in) { _inventoryCount = in->readUint16LE(); for (int i = 0; i < _inventoryCount; i++) { _inventory[i] = in->readUint16LE(); } updateInventory(0); } void Interface::mapPanelShow() { int i; ByteArray resourceData; Rect rect; ByteArray image; int imageWidth, imageHeight; const byte *pal; PalEntry cPal[PAL_ENTRIES]; _vm->_gfx->showCursor(false); rect.left = rect.top = 0; _vm->_resource->loadResource(_interfaceContext, _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resourceData); if (resourceData.empty()) { error("Interface::mapPanelShow() unable to load Tycho map resource"); } _vm->_gfx->getCurrentPal(_mapSavedPal); for (i = 0; i < 6; i++) { _vm->_gfx->palToBlack(_mapSavedPal, 0.2 * i); _vm->_render->drawScene(); _vm->_system->delayMillis(5); } _vm->_render->setFlag(RF_MAP); _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight); pal = _vm->getImagePal(resourceData); for (i = 0; i < PAL_ENTRIES; i++) { cPal[i].red = *pal++; cPal[i].green = *pal++; cPal[i].blue = *pal++; } rect.setWidth(imageWidth); rect.setHeight(imageHeight); _vm->_gfx->drawRegion(rect, image.getBuffer()); // Evil Evil for (i = 0; i < 6; i++) { _vm->_gfx->blackToPal(cPal, 0.2 * i); _vm->_render->drawScene(); _vm->_system->delayMillis(5); } setSaveReminderState(false); _mapPanelCrossHairState = true; } void Interface::mapPanelClean() { PalEntry pal[PAL_ENTRIES]; int i; _vm->_gfx->getCurrentPal(pal); for (i = 0; i < 6; i++) { _vm->_gfx->palToBlack(pal, 0.2 * i); _vm->_render->drawScene(); _vm->_system->delayMillis(5); } _vm->_render->clearFlag(RF_MAP); setMode(kPanelMain); _vm->_gfx->showCursor(true); _vm->_render->drawScene(); for (i = 0; i < 6; i++) { _vm->_gfx->blackToPal(_mapSavedPal, 0.2 * i); _vm->_render->drawScene(); _vm->_system->delayMillis(5); } } void Interface::mapPanelDrawCrossHair() { _mapPanelCrossHairState = !_mapPanelCrossHairState; Point mapPosition = _vm->_isoMap->getMapPosition(); Rect screen(_vm->getDisplayInfo().width, _vm->_scene->getHeight()); if (screen.contains(mapPosition)) { _vm->_sprite->draw(_vm->_sprite->_mainSprites, _mapPanelCrossHairState ? RID_ITE_SPR_CROSSHAIR : RID_ITE_SPR_CROSSHAIR + 1, mapPosition, 256); } } void Interface::keyBoss() { if (_vm->getGameId() == GID_ITE) return; if (_bossMode != -1 || _fadeMode != kNoFade) return; _vm->_sound->pauseVoice(); _vm->_sound->pauseSound(); _vm->_music->pause(); int i; ByteArray resourceData; Rect rect; ByteArray image; int imageWidth, imageHeight; //const byte *pal; PalEntry cPal[PAL_ENTRIES]; _vm->_gfx->showCursor(false); rect.left = rect.top = 0; _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resourceData); if (resourceData.empty()) { error("Interface::bossKey() unable to load Boss image resource"); } _bossMode = _panelMode; setMode(kPanelBoss); _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight); rect.setWidth(imageWidth); rect.setHeight(imageHeight); _vm->_gfx->getCurrentPal(_mapSavedPal); //pal = _vm->getImagePal(resourceData); cPal[0].red = 0; cPal[0].green = 0; cPal[0].blue = 0; for (i = 1; i < PAL_ENTRIES; i++) { cPal[i].red = 128; cPal[i].green = 128; cPal[i].blue = 128; } _vm->_gfx->drawRegion(rect, image.getBuffer()); _vm->_gfx->setPalette(cPal); } void Interface::keyBossExit() { PalEntry pal[PAL_ENTRIES]; _vm->_sound->resumeVoice(); _vm->_sound->resumeSound(); _vm->_music->resume(); _vm->_gfx->getCurrentPal(pal); _vm->_gfx->palToBlack(pal, 1); setMode(_bossMode); _vm->_render->drawScene(); _vm->_gfx->blackToPal(_mapSavedPal, 1); _vm->_gfx->showCursor(true); _bossMode = -1; } } // End of namespace Saga