diff options
author | vanfanel | 2015-11-11 17:56:12 +0100 |
---|---|---|
committer | vanfanel | 2015-11-11 17:56:12 +0100 |
commit | 99739a13fe844c807d3cdd87e67e207e888fd48a (patch) | |
tree | 6afbf4763326277efbf528f0bb9e587bf7a01788 /engines/sherlock/tattoo/tattoo_user_interface.cpp | |
parent | 37e157a11c3fc731dfdcf6ec6b6a5a448550219b (diff) | |
parent | 7e44493fe8877a3c6a65f83b9ed84a5f59169005 (diff) | |
download | scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.gz scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.bz2 scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.zip |
Merge branch 'master' into dispmanx
Diffstat (limited to 'engines/sherlock/tattoo/tattoo_user_interface.cpp')
-rw-r--r-- | engines/sherlock/tattoo/tattoo_user_interface.cpp | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp new file mode 100644 index 0000000000..ee028f89c2 --- /dev/null +++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp @@ -0,0 +1,968 @@ +/* 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. + * + */ + +#include "sherlock/tattoo/tattoo_user_interface.h" +#include "sherlock/tattoo/tattoo_fixed_text.h" +#include "sherlock/tattoo/tattoo_journal.h" +#include "sherlock/tattoo/tattoo_scene.h" +#include "sherlock/tattoo/tattoo.h" + +namespace Sherlock { + +namespace Tattoo { + +bool WidgetList::contains(const WidgetBase *item) const { + for (const_iterator i = begin(); i != end(); ++i) { + if ((*i) == item) + return true; + } + + return false; +} + +/*-------------------------------------------------------------------------*/ + +TattooUserInterface::TattooUserInterface(SherlockEngine *vm): UserInterface(vm), + _inventoryWidget(vm), _messageWidget(vm), _textWidget(vm), _tooltipWidget(vm), + _verbsWidget(vm), _creditsWidget(vm), _optionsWidget(vm), _quitWidget(vm) { + Common::fill(&_lookupTable[0], &_lookupTable[PALETTE_COUNT], 0); + Common::fill(&_lookupTable1[0], &_lookupTable1[PALETTE_COUNT], 0); + _scrollSize = 0; + _scrollSpeed = 16; + _drawMenu = false; + _bgShape = nullptr; + _personFound = false; + _lockoutTimer = 0; + _exitZone = -1; + _scriptZone = -1; + _arrowZone = _oldArrowZone = -1; + _activeObj = -1; + _cAnimFramePause = 0; + _scrollHighlight = SH_NONE; + _mask = _mask1 = nullptr; + _maskCounter = 0; + + _interfaceImages = new ImageFile("intrface.vgs"); +} + +TattooUserInterface::~TattooUserInterface() { + delete _interfaceImages; + delete _mask; + delete _mask1; +} + +void TattooUserInterface::initScrollVars() { + Screen &screen = *_vm->_screen; + _scrollSize = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH; + _targetScroll = Common::Point(0, 0); + screen._currentScroll = Common::Point(0, 0); +} + +void TattooUserInterface::lookAtObject() { + Events &events = *_vm->_events; + People &people = *_vm->_people; + Scene &scene = *_vm->_scene; + Sound &sound = *_vm->_sound; + Talk &talk = *_vm->_talk; + Common::Point mousePos = events.mousePos(); + Common::String desc; + + _lookPos = mousePos; + _menuMode = LOOK_MODE; + + if (_personFound) { + desc = people[_bgFound - 1000]._examine; + } else { + // Check if there is a Look animation + if (_bgShape->_lookcAnim != 0) { + int cAnimSpeed = _bgShape->_lookcAnim & 0xe0; + cAnimSpeed >>= 5; + ++cAnimSpeed; + + _cAnimFramePause = _bgShape->_lookFrames; + desc = _bgShape->_examine; + + int cNum = (_bgShape->_lookcAnim & 0x1f) - 1; + scene.startCAnim(cNum); + } else if (_bgShape->_lookPosition.y != 0) { + // Need to walk to object before looking at it + people[HOLMES].walkToCoords(_bgShape->_lookPosition, _bgShape->_lookPosition._facing); + } + + if (!talk._talkToAbort) { + desc = _bgShape->_examine; + + if (_bgShape->_lookFlag) + _vm->setFlags(_bgShape->_lookFlag); + + // Find the Sound File to Play if there is one + if (!desc.hasPrefix("_")) { + for (uint idx = 0; idx < scene._objSoundList.size(); ++idx) { + // Get the object name up to the equals + const char *p = strchr(scene._objSoundList[idx].c_str(), '='); + + // Form the name and remove any trailing spaces + Common::String name(scene._objSoundList[idx].c_str(), p); + while (name.hasSuffix(" ")) + name.deleteLastChar(); + + // See if this Object Sound List entry matches the object's name + if (!_bgShape->_name.compareToIgnoreCase(name)) { + // Move forward to get the sound filename + while ((*p == ' ') || (*p == '=')) + ++p; + + // If it's not "NONE", play the speech File + Common::String soundName(p); + if (soundName.compareToIgnoreCase("NONE")) { + soundName.toLowercase(); + if (!soundName.contains('.')) + soundName += ".wav"; + + sound.playSound(soundName, WAIT_RETURN_IMMEDIATELY); + } + + break; + } + } + } + } + } + + // Only show the desciption if the object has one, and if no talk file interrupted while walking to it + if (!talk._talkToAbort && !desc.empty()) { + if (_cAnimFramePause == 0) + printObjectDesc(desc, true); + else + // The description was already printed by an animation + _cAnimFramePause = 0; + } else if (desc.empty()) { + // There was no description to display, so reset back to STD_MODE + _menuMode = STD_MODE; + } +} + +void TattooUserInterface::printObjectDesc(const Common::String &str, bool firstTime) { + Events &events = *_vm->_events; + TattooScene &scene = *(TattooScene *)_vm->_scene; + Talk &talk = *_vm->_talk; + + if (str.hasPrefix("_")) { + // The passed string specifies a talk file + _lookScriptFlag = true; + events.setCursor(MAGNIFY); + int savedSelector = _selector; + + if (!_invLookFlag) + _windowOpen = false; + + talk.talkTo(str.c_str() + 1); + _lookScriptFlag = false; + + if (talk._talkToAbort) { + events.setCursor(ARROW); + return; + } + + // See if we're looking at an inventory item + if (_invLookFlag) { + _selector = _oldSelector = savedSelector; + doInventory(0); + _invLookFlag = false; + + } else { + // Nope + events.setCursor(ARROW); + _key = -1; + _menuMode = scene._labTableScene ? LAB_MODE : STD_MODE; + events._pressed = events._released = events._rightReleased = false; + events._oldButtons = 0; + } + } else { + events._pressed = events._released = events._rightReleased = false; + + // Show text dialog + _textWidget.load(str); + _textWidget.summonWindow(); + + if (firstTime) + _selector = _oldSelector = -1; + + _drawMenu = _windowOpen = true; + } +} + +void TattooUserInterface::doJournal() { + TattooJournal &journal = *(TattooJournal *)_vm->_journal; + TattooScene &scene = *(TattooScene *)_vm->_scene; + Screen &screen = *_vm->_screen; + byte lookupTable[PALETTE_COUNT], lookupTable1[PALETTE_COUNT]; + + Common::copy(&_lookupTable[0], &_lookupTable[PALETTE_COUNT], &lookupTable[0]); + Common::copy(&_lookupTable1[0], &_lookupTable1[PALETTE_COUNT], &lookupTable1[0]); + _menuMode = JOURNAL_MODE; + journal.show(); + + _menuMode = STD_MODE; + _windowOpen = false; + _key = -1; + + // Restore the the old screen palette and greyscale lookup table + screen.clear(); + screen.setPalette(screen._cMap); + Common::copy(&lookupTable[0], &lookupTable[PALETTE_COUNT], &_lookupTable[0]); + Common::copy(&lookupTable1[0], &lookupTable1[PALETTE_COUNT], &_lookupTable1[0]); + + // Restore the scene + screen._backBuffer1.blitFrom(screen._backBuffer2); + scene.updateBackground(); + screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); +} + +void TattooUserInterface::reset() { + UserInterface::reset(); + _lookPos = Common::Point(SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2); + _tooltipWidget.setText(""); + _widgets.clear(); + _fixedWidgets.clear(); +} + +void TattooUserInterface::handleInput() { + TattooEngine &vm = *(TattooEngine *)_vm; + Events &events = *_vm->_events; + TattooScene &scene = *(TattooScene *)_vm->_scene; + Common::Point mousePos = events.mousePos(); + + _keyState.keycode = Common::KEYCODE_INVALID; + + // Check for credits starting + if (_vm->readFlags(3000) && !_creditsWidget.active()) + _creditsWidget.initCredits(); + + // Check the mouse positioning + if (events.isCursorVisible()) + _bgFound = scene.findBgShape(mousePos); + _personFound = _bgFound >= 1000; + _bgShape = (_bgFound != -1 && _bgFound < 1000) ? &scene._bgShapes[_bgFound] : nullptr; + + if (_lockoutTimer) + --_lockoutTimer; + + // Key handling + if (events.kbHit()) { + _keyState = events.getKey(); + + if (_keyState.keycode == Common::KEYCODE_ESCAPE && vm._runningProlog && !_lockoutTimer) { + vm.setFlags(-76); + vm.setFlags(396); + scene._goToScene = STARTING_GAME_SCENE; + } else if (_menuMode == STD_MODE) { + if (_keyState.keycode == Common::KEYCODE_s && vm._allowFastMode) { + events.toggleSpeed(); + + } else if (_keyState.keycode == Common::KEYCODE_l && _bgFound != -1) { + // Beging used for testing that Look dialogs work + lookAtObject(); + } + } + } + + if (!events.isCursorVisible()) + _keyState.keycode = Common::KEYCODE_INVALID; + + // If there's any active widgets/windows, let the most recently open one do event processing + if (!_widgets.empty()) + _widgets.back()->handleEvents(); + else if (!_fixedWidgets.empty()) + _fixedWidgets.back()->handleEvents(); + + // Handle input depending on what mode we're in + switch (_menuMode) { + case STD_MODE: + doStandardControl(); + break; + case LOOK_MODE: + doLookControl(); + break; + default: + break; + } +} + +void TattooUserInterface::drawInterface(int bufferNum) { + Screen &screen = *_vm->_screen; + + // Draw any active on-screen widgets + for (Common::List<WidgetBase *>::iterator i = _fixedWidgets.begin(); i != _fixedWidgets.end(); ++i) + (*i)->draw(); + for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) + (*i)->draw(); + + // Handle drawing credits + // TODO: See if credits are only shown on a single screen. If so, _fixedWidgets could be used + if (_creditsWidget.active()) + _creditsWidget.drawCredits(); + + // Bring the widgets to the screen + if (_mask != nullptr) + screen._flushScreen = true; +} + +void TattooUserInterface::doBgAnimRestoreUI() { + TattooScene &scene = *((TattooScene *)_vm->_scene); + Screen &screen = *_vm->_screen; + + // If there are any on-screen widgets, then erase them + for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) + (*i)->erase(); + for (Common::List<WidgetBase *>::iterator i = _fixedWidgets.begin(); i != _fixedWidgets.end(); ++i) + (*i)->erase(); + + // If there is a Text Tag being display, restore the area underneath it + _tooltipWidget.erase(); + + // If a canimation is active, restore the graphics underneath it + if (scene._activeCAnim.active()) + screen.restoreBackground(scene._activeCAnim._oldBounds); + + // If a canimation just ended, remove its graphics from the backbuffer + if (scene._activeCAnim._removeBounds.width() > 0) + screen.restoreBackground(scene._activeCAnim._removeBounds); +} + +void TattooUserInterface::doScroll() { + Screen &screen = *_vm->_screen; + + // If we're already at the target scroll position, nothing needs to be done + if (_targetScroll.x == screen._currentScroll.x) + return; + + screen._flushScreen = true; + if (_targetScroll.x > screen._currentScroll.x) { + screen._currentScroll.x += _scrollSpeed; + if (screen._currentScroll.x > _targetScroll.x) + screen._currentScroll.x = _targetScroll.x; + } else if (_targetScroll.x < screen._currentScroll.x) { + screen._currentScroll.x -= _scrollSpeed; + if (screen._currentScroll.x < _targetScroll.x) + screen._currentScroll.x = _targetScroll.x; + } + + // Reset the default look position to the center of the new screen area + _lookPos = screen._currentScroll + Common::Point(SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2); +} + +void TattooUserInterface::doStandardControl() { + TattooEngine &vm = *(TattooEngine *)_vm; + Events &events = *_vm->_events; + People &people = *_vm->_people; + SaveManager &saves = *_vm->_saves; + TattooScene &scene = *(TattooScene *)_vm->_scene; + Talk &talk = *_vm->_talk; + Common::Point mousePos = events.mousePos(); + + // Don't do any input processing whilst the prolog is running + if (vm._runningProlog) + return; + + // When the end credits are active, any press will open the ScummVM global main menu + if (_creditsWidget.active()) { + if (_keyState.keycode || events._released || events._rightReleased) { + vm._canLoadSave = true; + vm.openMainMenuDialog(); + vm._canLoadSave = false; + } + + return; + } + + // Display the names of any Objects the cursor is pointing at + displayObjectNames(); + + switch (_keyState.keycode) { + case Common::KEYCODE_F5: + // Save game + events.warpMouse(); + saveGame(); + return; + + case Common::KEYCODE_F7: + // Load game + events.warpMouse(); + loadGame(); + return; + + case Common::KEYCODE_F1: + // Display journal + if (vm.readFlags(FLAG_PLAYER_IS_HOLMES)) { + freeMenu(); + doJournal(); + + // See if we're in a Lab Table Room + _menuMode = (scene._labTableScene) ? LAB_MODE : STD_MODE; + return; + } + break; + + case Common::KEYCODE_TAB: + case Common::KEYCODE_F3: + // Display inventory + freeMenu(); + doInventory(3); + return; + + case Common::KEYCODE_F4: + // Display options + events.warpMouse(); + _optionsWidget.load(); + return; + + case Common::KEYCODE_F10: + // Quit menu + freeMenu(); + events.warpMouse(); + doQuitMenu(); + return; + + default: + break; + } + + // See if a mouse button was released + if (events._released || events._rightReleased) { + // See if the mouse was released in an exit (Arrow) zone. Unless it's also pointing at an object + // within the zone, in which case the object gets precedence + _exitZone = -1; + if (_arrowZone != -1 && events._released) + _exitZone = _arrowZone; + + // Turn any Text display off + if (_arrowZone == -1 || events._rightReleased) + freeMenu(); + + bool noDesc = false; + if (_personFound) { + if (people[_bgFound - 1000]._description.empty() || people[_bgFound - 1000]._description.hasPrefix(" ")) + noDesc = true; + } else if (_bgFound != -1) { + if (_bgShape->_description.empty() || _bgShape->_description.hasPrefix(" ")) + noDesc = true; + } else { + noDesc = true; + } + + if (events._rightReleased) { + // Show the verbs menu for the highlighted object + _tooltipWidget.banishWindow(); + saves.createThumbnail(); + _verbsWidget.load(!noDesc); + _verbsWidget.summonWindow(); + + _selector = _oldSelector = -1; + _activeObj = _bgFound; + _menuMode = VERB_MODE; + } else if (_personFound || (_bgFound != -1 && _bgFound < 1000 && _bgShape->_aType == PERSON)) { + // The object found is a person (the default for people is TALK) + talk.initTalk(_bgFound); + _activeObj = -1; + } else if (!noDesc) { + // Either call the code to Look at its Examine Field or call the Exit animation + // if the object is an exit, specified by the first four characters of the name being "EXIT" + Common::String name = _personFound ? people[_bgFound - 1000]._name : _bgShape->_name; + if (!name.hasPrefix("EXIT")) { + lookAtObject(); + } else { + // Run the Exit animation and set which scene to go to next + for (int idx = 0; idx < 6; ++idx) { + if (!_bgShape->_use[idx]._verb.compareToIgnoreCase("Open")) { + checkAction(_bgShape->_use[idx], _bgFound); + _activeObj = -1; + } + } + } + } else { + // See if there are any Script Zones where they clicked + if (scene.checkForZones(mousePos, _scriptZone) != 0) { + // Mouse click in a script zone + events._pressed = events._released = false; + } else if (scene.checkForZones(mousePos, NOWALK_ZONE) != 0) { + events._pressed = events._released = false; + } else { + // Walk to where the mouse was clicked + people[HOLMES]._walkDest = mousePos; + people[HOLMES].goAllTheWay(); + } + } + } +} + +void TattooUserInterface::doLookControl() { + Events &events = *_vm->_events; + TattooScene &scene = *(TattooScene *)_vm->_scene; + + // See if a mouse button was released or a key pressed to close the active look dialog + if (events._released || events._rightReleased || _keyState.keycode) { + // See if we were looking at an inventory object + if (!_invLookFlag) { + // See if there is any more text to display + if (!_textWidget._remainingText.empty()) { + printObjectDesc(_textWidget._remainingText, false); + } else { + // Otherwise restore the background and go back into STD_MODE + freeMenu(); + _key = -1; + _menuMode = scene._labTableScene ? LAB_MODE : STD_MODE; + + events.setCursor(ARROW); + events._pressed = events._released = events._rightReleased = false; + events._oldButtons = 0; + } + } else { + // We were looking at a Inventory object + // Erase the text window, and then redraw the inventory window + _textWidget.banishWindow(); + doInventory(0); + + _invLookFlag = false; + _key = -1; + + events.setCursor(ARROW); + events._pressed = events._released = events._rightReleased = false; + events._oldButtons = 0; + } + } +} + +void TattooUserInterface::displayObjectNames() { + Events &events = *_vm->_events; + Scene &scene = *_vm->_scene; + Common::Point mousePos = events.mousePos(); + _arrowZone = -1; + + if (_bgFound == -1 || scene._currentScene == OVERHEAD_MAP2) { + for (uint idx = 0; idx < scene._exits.size() && _arrowZone == -1; ++idx) { + Exit &exit = scene._exits[idx]; + if (exit.contains(mousePos)) + _arrowZone = idx; + } + } + + _tooltipWidget.handleEvents(); + _oldArrowZone = _arrowZone; +} + +void TattooUserInterface::doInventory(int mode) { + People &people = *_vm->_people; + people[HOLMES].gotoStand(); + + _inventoryWidget.load(mode); + _inventoryWidget.summonWindow(); + + _menuMode = INV_MODE; +} + +void TattooUserInterface::doControls() { + _optionsWidget.load(); +} + +void TattooUserInterface::pickUpObject(int objNum) { + Inventory &inv = *_vm->_inventory; + Scene &scene = *_vm->_scene; + Talk &talk = *_vm->_talk; + Object &obj = scene._bgShapes[objNum]; + bool printed = false; + int verbField = -1; + + // Find which Verb field to use for pick up data + for (int idx = 0; idx < 6; ++idx) { + if (!scumm_stricmp(obj._use[idx]._target.c_str(), "*PICKUP")) + verbField = idx; + } + + if (verbField != -1) { + if (obj._use[verbField]._cAnimNum) + scene.startCAnim(obj._use[verbField]._cAnimNum - 1); + } + + if (!talk._talkToAbort) { + if (obj._type == NO_SHAPE) + obj._type = INVALID; + else + // Erase shape + obj._type = REMOVE; + } else { + return; + } + + if (verbField != -1) { + for (int idx = 0; idx < 4 && !talk._talkToAbort; ++idx) { + if (obj.checkNameForCodes(obj._use[verbField]._names[idx])) { + if (!talk._talkToAbort) + printed = true; + } + } + } + + if (talk._talkToAbort) + return; + + // Add the item to the player's inventory + inv.putItemInInventory(obj); + + if (!printed) { + Common::String desc = obj._description; + desc.setChar(tolower(desc[0]), 0); + + putMessage("%s %s", FIXED(PickedUp), desc.c_str()); + } + + if (_menuMode != TALK_MODE && _menuMode != MESSAGE_MODE) { + _menuMode = STD_MODE; + _keyState.keycode = Common::KEYCODE_INVALID; + } +} + +void TattooUserInterface::doQuitMenu() { + _quitWidget.show(); +} + +void TattooUserInterface::putMessage(const char *formatStr, ...) { + // Create the string to display + va_list args; + va_start(args, formatStr); + Common::String str = Common::String::vformat(formatStr, args); + va_end(args); + + // Open the message widget + _menuMode = MESSAGE_MODE; + _messageWidget.load(str, 25); + _messageWidget.summonWindow(); +} + +void TattooUserInterface::setupBGArea(const byte cMap[PALETTE_SIZE]) { + Scene &scene = *_vm->_scene; + + // This requires that there is a 16 grayscale palette sequence in the palette that goes from lighter + // to darker as the palette numbers go up. The last palette entry in that run is specified by _bgColor + byte *p = &_lookupTable[0]; + for (int idx = 0; idx < PALETTE_COUNT; ++idx) + *p++ = BG_GREYSCALE_RANGE_END - (cMap[idx * 3] * 30 + cMap[idx * 3 + 1] * 59 + cMap[idx * 3 + 2] * 11) / 480; + + // If we're going to a scene with a haze special effect, initialize the translate table to lighten the colors + if (_mask != nullptr) { + p = &_lookupTable1[0]; + + for (int idx = 0; idx < PALETTE_COUNT; ++idx) { + int r, g, b; + switch (scene._currentScene) { + case 8: + r = cMap[idx * 3] * 4 / 5; + g = cMap[idx * 3 + 1] * 3 / 4; + b = cMap[idx * 3 + 2] * 3 / 4; + break; + + case 18: + case 68: + r = cMap[idx * 3] * 4 / 3; + g = cMap[idx * 3 + 1] * 4 / 3; + b = cMap[idx * 3 + 2] * 4 / 3; + break; + + case 7: + case 53: + r = cMap[idx * 3] * 4 / 3; + g = cMap[idx * 3 + 1] * 4 / 3; + b = cMap[idx * 3 + 2] * 4 / 3; + break; + + default: + r = g = b = 0; + break; + } + + byte c = 0xff; + int cd = 99999; + + for (int pal = 0; pal < PALETTE_COUNT; ++pal) { + int d = (r - cMap[pal * 3]) * (r - cMap[pal * 3]) + (g - cMap[pal * 3 + 1]) * (g - cMap[pal * 3 + 1]) + + (b - cMap[pal * 3 + 2]) * (b - cMap[pal * 3 + 2]); + + if (d < cd) { + c = pal; + cd = d; + if (!d) + break; + } + } + *p++ = c; + } + } +} + +void TattooUserInterface::doBgAnimEraseBackground() { + People &people = *_vm->_people; + Scene &scene = *_vm->_scene; + Screen &screen = *_vm->_screen; + + static const int16 OFFSETS[16] = { -1, -2, -3, -3, -2, -1, -1, 0, 1, 2, 3, 3, 2, 1, 0, 0 }; + + if (_mask != nullptr) { + // Since a mask is active, restore the screen from the secondary back buffer prior to applying the mask + screen._backBuffer1.blitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0, + screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + + switch (scene._currentScene) { + case 7: + if (++_maskCounter == 2) { + _maskCounter = 0; + if (--_maskOffset.x < 0) + _maskOffset.x = SHERLOCK_SCREEN_WIDTH - 1; + } + break; + + case 8: + _maskOffset.x += 2; + if (_maskOffset.x >= SHERLOCK_SCREEN_WIDTH) + _maskOffset.x = 0; + break; + + case 18: + case 68: + ++_maskCounter; + if (_maskCounter / 4 >= 16) + _maskCounter = 0; + + _maskOffset.x = OFFSETS[_maskCounter / 4]; + break; + + case 53: + if (++_maskCounter == 2) { + _maskCounter = 0; + if (++_maskOffset.x == screen._backBuffer1.w()) + _maskOffset.x = 0; + } + break; + + default: + break; + } + } else { + // Standard scene without mask, so call user interface to erase any UI elements as necessary + doBgAnimRestoreUI(); + + // Restore background for any areas covered by characters and shapes + for (int idx = 0; idx < MAX_CHARACTERS; ++idx) + screen.restoreBackground(Common::Rect(people[idx]._oldPosition.x, people[idx]._oldPosition.y, + people[idx]._oldPosition.x + people[idx]._oldSize.x, people[idx]._oldPosition.y + people[idx]._oldSize.y)); + + for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) { + Object &obj = scene._bgShapes[idx]; + + if ((obj._type == ACTIVE_BG_SHAPE && (obj._maxFrames > 1 || obj._delta.x != 0 || obj._delta.y != 0)) || + obj._type == HIDE_SHAPE || obj._type == REMOVE) + screen._backBuffer1.blitFrom(screen._backBuffer2, obj._oldPosition, + Common::Rect(obj._oldPosition.x, obj._oldPosition.y, obj._oldPosition.x + obj._oldSize.x, + obj._oldPosition.y + obj._oldSize.y)); + } + + // If credits are active, erase the area they cover + if (_creditsWidget.active()) + _creditsWidget.eraseCredits(); + } + + for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) { + Object &obj = scene._bgShapes[idx]; + + if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) { + screen._backBuffer1.blitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds()); + + obj._oldPosition = obj._position; + obj._oldSize = obj._noShapeSize; + } + } + + // Adjust the Target Scroll if needed + if ((people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - screen._currentScroll.x) < + (SHERLOCK_SCREEN_WIDTH / 8) && people[people._walkControl]._delta.x < 0) { + + _targetScroll.x = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - + SHERLOCK_SCREEN_WIDTH / 8 - 250); + if (_targetScroll.x < 0) + _targetScroll.x = 0; + } + + if ((people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - screen._currentScroll.x) > + (SHERLOCK_SCREEN_WIDTH / 4 * 3) && people[people._walkControl]._delta.x > 0) + _targetScroll.x = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - + SHERLOCK_SCREEN_WIDTH / 4 * 3 + 250); + + if (_targetScroll.x > _scrollSize) + _targetScroll.x = _scrollSize; + + doScroll(); +} + +void TattooUserInterface::drawMaskArea(bool mode) { + Scene &scene = *_vm->_scene; + int xp = mode ? _maskOffset.x : 0; + + if (_mask != nullptr) { + switch (scene._currentScene) { + case 7: + maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110)); + maskArea(*_mask, Common::Point(_maskOffset.x, 110)); + maskArea(*_mask, Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 110)); + break; + + case 8: + maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 180)); + maskArea(*_mask, Common::Point(_maskOffset.x, 180)); + maskArea(*_mask, Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 180)); + if (!_vm->readFlags(880)) + maskArea(*_mask1, Common::Point(940, 300)); + break; + + case 18: + maskArea(*_mask, Common::Point(xp, 203)); + if (!_vm->readFlags(189)) + maskArea(*_mask1, Common::Point(124 + xp, 239)); + break; + + case 53: + maskArea(*_mask, Common::Point(_maskOffset.x, 110)); + if (mode) + maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110)); + break; + + case 68: + maskArea(*_mask, Common::Point(xp, 203)); + maskArea(*_mask1, Common::Point(124 + xp, 239)); + break; + } + } +} + +void TattooUserInterface::maskArea(Common::SeekableReadStream &mask, const Common::Point &pt) { + Screen &screen = *_vm->_screen; + Surface &bb1 = screen._backBuffer1; + mask.seek(0); + int xSize = mask.readUint16LE(); + int ySize = mask.readUint16LE(); + int pixel, len, xp, yp; + + for (yp = 0; yp < ySize; ++yp) { + byte *ptr = bb1.getBasePtr(pt.x, pt.y + yp); + + for (xp = 0; xp < xSize;) { + // The mask data consists of pairs of pixel/lengths, where all non-zero pixels means that the + // given pixel on the back buffer is darkened (the mask pixel value isn't otherwise used) + pixel = mask.readByte(); + len = mask.readByte(); + + for (; len > 0; --len, ++xp, ++ptr) { + if (pixel && (pt.x + xp) >= screen._currentScroll.x && (pt.x + xp) < (screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH)) { + *ptr = _lookupTable1[*ptr]; + } + } + } + + assert(xp == xSize); + } +} + +void TattooUserInterface::makeBGArea(const Common::Rect &r) { + Screen &screen = *_vm->_screen; + + for (int yp = r.top; yp < r.bottom; ++yp) { + byte *ptr = screen._backBuffer1.getBasePtr(r.left, yp); + + for (int xp = r.left; xp < r.right; ++xp, ++ptr) + *ptr = _lookupTable[*ptr]; + } + + screen.slamRect(r); +} + +void TattooUserInterface::drawDialogRect(Surface &s, const Common::Rect &r, bool raised) { + if (raised) { + // Draw Left + s.vLine(r.left, r.top, r.bottom - 1, INFO_TOP); + s.vLine(r.left + 1, r.top, r.bottom - 2, INFO_TOP); + // Draw Top + s.hLine(r.left + 2, r.top, r.right - 1, INFO_TOP); + s.hLine(r.left + 2, r.top + 1, r.right - 2, INFO_TOP); + // Draw Right + s.vLine(r.right - 1, r.top + 1, r.bottom - 1, INFO_BOTTOM); + s.vLine(r.right - 2, r.top + 2, r.bottom - 1, INFO_BOTTOM); + // Draw Bottom + s.hLine(r.left + 1, r.bottom - 1, r.right - 3, INFO_BOTTOM); + s.hLine(r.left + 2, r.bottom - 2, r.right - 3, INFO_BOTTOM); + + } else { + // Draw Left + s.vLine(r.left, r.top, r.bottom - 1, INFO_BOTTOM); + s.vLine(r.left + 1, r.top, r.bottom - 2, INFO_BOTTOM); + // Draw Top + s.hLine(r.left + 2, r.top, r.right - 1, INFO_BOTTOM); + s.hLine(r.left + 2, r.top + 1, r.right - 2, INFO_BOTTOM); + // Draw Right + s.vLine(r.right - 1, r.top + 1, r.bottom - 1, INFO_TOP); + s.vLine(r.right - 2, r.top + 2, r.bottom - 1, INFO_TOP); + // Draw Bottom + s.hLine(r.left + 1, r.bottom - 1, r.right - 3, INFO_TOP); + s.hLine(r.left + 2, r.bottom - 2, r.right - 3, INFO_TOP); + } +} + +void TattooUserInterface::banishWindow(bool slideUp) { + if (!_widgets.empty()) + _widgets.back()->banishWindow(); +} + +void TattooUserInterface::freeMenu() { + for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) + (*i)->erase(); + _widgets.clear(); +} + +void TattooUserInterface::clearWindow() { + banishWindow(); +} + +void TattooUserInterface::loadGame() { + WidgetFiles &files = *(WidgetFiles *)_vm->_saves; + files.show(SAVEMODE_LOAD); +} + +void TattooUserInterface::saveGame() { + WidgetFiles &files = *(WidgetFiles *)_vm->_saves; + files.show(SAVEMODE_SAVE); +} + +void TattooUserInterface::addFixedWidget(WidgetBase *widget) { + _fixedWidgets.push_back(widget); + widget->summonWindow(); +} + +} // End of namespace Tattoo + +} // End of namespace Sherlock |