diff options
author | uruk | 2014-05-30 11:14:47 +0200 |
---|---|---|
committer | uruk | 2014-05-30 11:14:47 +0200 |
commit | bb2f8dd68e1d6b2b30b07f60c0cd4e125b47ea4d (patch) | |
tree | 9a1e28cfb1eb1a322225c05adc0962a2f96ea521 /engines/mads/user_interface.cpp | |
parent | 5ad4e157e5398347651a0da0db07f9daf01bf373 (diff) | |
parent | 0a46d67baea121bed0511ce45bfdd8438a43d35d (diff) | |
download | scummvm-rg350-bb2f8dd68e1d6b2b30b07f60c0cd4e125b47ea4d.tar.gz scummvm-rg350-bb2f8dd68e1d6b2b30b07f60c0cd4e125b47ea4d.tar.bz2 scummvm-rg350-bb2f8dd68e1d6b2b30b07f60c0cd4e125b47ea4d.zip |
Merge branch 'master' of https://github.com/scummvm/scummvm into cge2
Diffstat (limited to 'engines/mads/user_interface.cpp')
-rw-r--r-- | engines/mads/user_interface.cpp | 1096 |
1 files changed, 1096 insertions, 0 deletions
diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp new file mode 100644 index 0000000000..e410d173d7 --- /dev/null +++ b/engines/mads/user_interface.cpp @@ -0,0 +1,1096 @@ +/* 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 "common/scummsys.h" +#include "mads/mads.h" +#include "mads/compression.h" +#include "mads/user_interface.h" +#include "mads/nebular/game_nebular.h" + +namespace MADS { + +UISlot::UISlot() { + _flags = IMG_STATIC; + _segmentId = 0; + _spritesIndex = 0; + _frameNumber = 0; + _width = _height = 0; +} + +/*------------------------------------------------------------------------*/ + +void UISlots::fullRefresh() { + UISlot slot; + slot._flags = IMG_REFRESH; + slot._segmentId = -1; + + push_back(slot); +} + +void UISlots::add(const Common::Rect &bounds) { + assert(size() < 50); + + UISlot ie; + ie._flags = IMG_OVERPRINT; + ie._segmentId = IMG_TEXT_UPDATE; + ie._position = Common::Point(bounds.left, bounds.top); + ie._width = bounds.width(); + ie._height = bounds.height(); + + push_back(ie); +} + +void UISlots::add(const AnimFrameEntry &frameEntry) { + assert(size() < 50); + + UISlot ie; + ie._flags = IMG_UPDATE; + ie._segmentId = frameEntry._seqIndex; + ie._spritesIndex = frameEntry._spriteSlot._spritesIndex; + ie._frameNumber = frameEntry._spriteSlot._frameNumber; + ie._position = frameEntry._spriteSlot._position; + + push_back(ie); +} + +void UISlots::draw(bool updateFlag, bool delFlag) { + Scene &scene = _vm->_game->_scene; + UserInterface &userInterface = scene._userInterface; + DirtyArea *dirtyAreaPtr = nullptr; + + // Loop through setting up the dirty areas + for (uint idx = 0; idx < size(); ++idx) { + DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; + UISlot &slot = (*this)[idx]; + + if (slot._flags >= IMG_STATIC) { + dirtyArea._active = false; + } else { + dirtyArea.setUISlot(&slot); + dirtyArea._textActive = true; + if (slot._segmentId == IMG_SPINNING_OBJECT && slot._flags == IMG_FULL_UPDATE) { + dirtyArea._active = false; + dirtyAreaPtr = &dirtyArea; + } + } + } + + userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); + if (dirtyAreaPtr) + dirtyAreaPtr->_active = true; + + // Copy parts of the user interface background that need to be erased + for (uint idx = 0; idx < size(); ++idx) { + DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; + UISlot &slot = (*this)[idx]; + + if (dirtyArea._active && dirtyArea._bounds.width() > 0 + && dirtyArea._bounds.height() > 0 && slot._flags > -20) { + + if (slot._flags >= IMG_ERASE) { + // Merge area + userInterface.mergeFrom(&userInterface._surface, dirtyArea._bounds, + Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); + } else { + // Copy area + userInterface._surface.copyTo(&userInterface, dirtyArea._bounds, + Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); + } + } + } + + for (uint idx = 0; idx < size(); ++idx) { + DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; + UISlot &slot = (*this)[idx]; + + int slotType = slot._flags; + if (slotType >= IMG_STATIC) { + dirtyArea.setUISlot(&slot); + if (!updateFlag) + slotType &= ~0x40; + + dirtyArea._textActive = slotType > 0; + slot._flags &= 0x40; + } + } + + userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); + + for (uint idx = 0; idx < size(); ++idx) { + DirtyArea *dirtyArea = &userInterface._dirtyAreas[idx]; + UISlot &slot = (*this)[idx]; + + if (slot._flags >= IMG_STATIC && !(slot._flags & 0x40)) { + if (!dirtyArea->_active) { + do { + dirtyArea = dirtyArea->_mergedArea; + } while (!dirtyArea->_active); + } + + if (dirtyArea->_textActive) { + SpriteAsset *asset = scene._sprites[slot._spritesIndex]; + + // Get the frame details + int frameNumber = ABS(slot._frameNumber); + bool flipped = slot._frameNumber < 0; + + if (slot._segmentId == IMG_SPINNING_OBJECT) { + MSprite *sprite = asset->getFrame(frameNumber); + sprite->copyTo(&userInterface, slot._position, + sprite->getTransparencyIndex()); + } else { + MSprite *sprite = asset->getFrame(frameNumber - 1); + + if (flipped) { + MSurface *spr = sprite->flipHorizontal(); + userInterface.mergeFrom(spr, spr->getBounds(), slot._position, + sprite->getTransparencyIndex()); + delete spr; + } else { + userInterface.mergeFrom(sprite, sprite->getBounds(), slot._position, + sprite->getTransparencyIndex()); + } + } + } + } + } + + // Mark areas of the screen surface for updating + if (updateFlag) { + for (uint idx = 0; idx < size(); ++idx) { + DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; + + if (dirtyArea._active && dirtyArea._textActive && + dirtyArea._bounds.width() > 0 && dirtyArea._bounds.height() > 0) { + // Flag area of screen as needing update + Common::Rect r = dirtyArea._bounds; + r.translate(0, scene._interfaceY); + _vm->_screen.copyRectToScreen(r); + } + } + } + + // Post-processing to remove slots no longer needed + for (int idx = (int)size() - 1; idx >= 0; --idx) { + UISlot &slot = (*this)[idx]; + + if (slot._flags < IMG_STATIC) { + if (delFlag || updateFlag) + remove_at(idx); + else if (slot._flags > -20) + slot._flags -= 20; + } else { + if (updateFlag) + slot._flags &= ~0x40; + else + slot._flags |= 0x40; + } + } +} + +/*------------------------------------------------------------------------*/ + +MADSEngine *Conversation::_vm; + +void Conversation::init(MADSEngine *vm) { + _vm = vm; +} + +void Conversation::setup(int globalId, ...) { + va_list va; + va_start(va, globalId); + + // Load the list of conversation quotes + _quotes.clear(); + int quoteId = va_arg(va, int); + while (quoteId > 0) { + _quotes.push_back(quoteId); + quoteId = va_arg(va, int); + } + va_end(va); + + if (quoteId < 0) { + // For an ending value of -1, also initial the bitflags for the global + // associated with the conversation entry, which enables all the quote Ids + _vm->_game->globals()[globalId] = (int16)0xffff; + } + + _globalId = globalId; +} + +void Conversation::set(int quoteId, ...) { + _vm->_game->globals()[_globalId] = 0; + + va_list va; + va_start(va, quoteId); + + // Loop through handling each quote + while (quoteId > 0) { + for (uint idx = 0; idx < _quotes.size(); ++idx) { + if (_quotes[idx] == quoteId) { + // Found index, so set that bit in the global keeping track of conversation state + _vm->_game->globals()[_globalId] |= 1 << idx; + break; + } + } + + quoteId = va_arg(va, int); + } + va_end(va); +} + +int Conversation::read(int quoteId) { + uint16 flags = _vm->_game->globals()[_globalId]; + int count = 0; + + for (uint idx = 0; idx < _quotes.size(); ++idx) { + if (flags & (1 << idx)) + ++count; + + if (_quotes[idx] == quoteId) + return flags & (1 << idx); + } + + // Could not find it, simply return number of active quotes + return count; +} + +void Conversation::write(int quoteId, bool flag) { + for (uint idx = 0; idx < _quotes.size(); ++idx) { + if (_quotes[idx] == quoteId) { + // Found index, so set or clear the flag + if (flag) { + // Set bit + _vm->_game->globals()[_globalId] |= 1 << idx; + } else { + // Clear bit + _vm->_game->globals()[_globalId] &= ~(1 << idx); + } + return; + } + } +} + +void Conversation::start() { + UserInterface &userInterface = _vm->_game->_scene._userInterface; + userInterface.emptyConversationList(); + + // Loop through each of the quotes loaded into the conversation + for (uint idx = 0; idx < _quotes.size(); ++idx) { + // Check whether the given quote is enabled or not + if (_vm->_game->globals()[_globalId] & (1 << idx)) { + // Quote enabled, so add it to the list of talk selections + Common::String msg = _vm->_game->getQuote(_quotes[idx]); + userInterface.addConversationMessage(_quotes[idx], msg); + } + } + + userInterface.setup(kInputConversation); +} + +/*------------------------------------------------------------------------*/ + +UserInterface::UserInterface(MADSEngine *vm) : _vm(vm), _dirtyAreas(vm), + _uiSlots(vm) { + _invSpritesIndex = -1; + _invFrameNumber = 1; + _scrollMilli = 0; + _scrollFlag = false; + _category = CAT_NONE; + _inventoryTopIndex = 0; + _selectedInvIndex = -1; + _selectedActionIndex = 0; + _selectedItemVocabIdx = -1; + _scrollbarActive = SCROLLBAR_NONE; + _scrollbarOldActive = SCROLLBAR_NONE; + _scrollbarStrokeType = SCROLLBAR_NONE; + _scrollbarQuickly = false; + _scrollbarMilliTime = 0; + _scrollbarElevator = _scrollbarOldElevator = 0; + _highlightedCommandIndex = -1; + _highlightedInvIndex = -1; + _highlightedItemVocabIndex = -1; + _dirtyAreas.resize(50); + _inventoryChanged = false; + _noSegmentsActive = 0; + _someSegmentsActive = 0; + _rectP = nullptr; + + Common::fill(&_categoryIndexes[0], &_categoryIndexes[7], 0); + + // Map the user interface to the bottom of the game's screen surface + byte *pData = _vm->_screen.getBasePtr(0, MADS_SCENE_HEIGHT); + setPixels(pData, MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT); + + _surface.setSize(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT); +} + +void UserInterface::load(const Common::String &resName) { + File f(resName); + MadsPack madsPack(&f); + + // Load in the palette + Common::SeekableReadStream *palStream = madsPack.getItemStream(0); + + uint32 *gamePalP = &_vm->_palette->_palFlags[0]; + byte *palP = &_vm->_palette->_mainPalette[0]; + + for (int i = 0; i < 16; ++i, gamePalP++, palP += 3) { + RGB6 rgb; + rgb.load(palStream); + palP[0] = rgb.r; + palP[1] = rgb.g; + palP[2] = rgb.b; + *gamePalP |= 1; + } + delete palStream; + + // Read in the surface data + Common::SeekableReadStream *pixelsStream = madsPack.getItemStream(1); + pixelsStream->read(_surface.getData(), MADS_SCREEN_WIDTH * MADS_INTERFACE_HEIGHT); + delete pixelsStream; +} + +void UserInterface::setup(InputMode inputMode) { + Scene &scene = _vm->_game->_scene; + + if (_vm->_game->_screenObjects._inputMode != inputMode) { + Common::String resName = _vm->_game->_aaName; + + // Strip off any extension + const char *p = strchr(resName.c_str(), '.'); + if (p) { + resName = Common::String(resName.c_str(), p); + } + + // Add on suffix if necessary + if (inputMode != kInputBuildingSentences) + resName += "A"; + + resName += ".INT"; + + load(resName); + _surface.copyTo(this); + } + _vm->_game->_screenObjects._inputMode = inputMode; + + scene._userInterface._uiSlots.clear(); + scene._userInterface._uiSlots.fullRefresh(); + _vm->_game->_screenObjects._baseTime = _vm->_events->getFrameCounter(); + _highlightedCommandIndex = -1; + _highlightedItemVocabIndex = -1; + _highlightedInvIndex = -1; + + if (_vm->_game->_kernelMode == KERNEL_ACTIVE_CODE) + scene._userInterface._uiSlots.draw(false, false); + + scene._action.clear(); + drawTextElements(); + loadElements(); + scene._dynamicHotspots.refresh(); +} + +void UserInterface::drawTextElements() { + if (_vm->_game->_screenObjects._inputMode) { + drawConversationList(); + } else { + // Draw the actions + drawActions(); + drawInventoryList(); + drawItemVocabList(); + } +} + +void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds, + const Common::Point &destPos, int transparencyIndex) { + // Validation of the rectangle and position + int destX = destPos.x, destY = destPos.y; + if ((destX >= w) || (destY >= h)) + return; + + Common::Rect copyRect = srcBounds; + if (destX < 0) { + copyRect.left += -destX; + destX = 0; + } else if (destX + copyRect.width() > w) { + copyRect.right -= destX + copyRect.width() - w; + } + if (destY < 0) { + copyRect.top += -destY; + destY = 0; + } else if (destY + copyRect.height() > h) { + copyRect.bottom -= destY + copyRect.height() - h; + } + + if (!copyRect.isValidRect()) + return; + + // Copy the specified area + + byte *data = src->getData(); + byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left); + byte *destPtr = (byte *)this->pixels + (destY * getWidth()) + destX; + + for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { + // Process each line of the area + for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) { + // Check for the range used for the user interface background, + // which are the only pixels that can be replaced + if ((destPtr[xCtr] >= 8 && destPtr[xCtr] <= 15) && (int)srcPtr[xCtr] != transparencyIndex) + destPtr[xCtr] = srcPtr[xCtr]; + } + + srcPtr += src->getWidth(); + destPtr += getWidth(); + } +} + +void UserInterface::drawActions() { + for (int idx = 0; idx < 10; ++idx) { + writeVocab(CAT_COMMAND, idx); + } +} + +void UserInterface::drawInventoryList() { + int endIndex = MIN((int)_vm->_game->_objects._inventoryList.size(), _inventoryTopIndex + 5); + for (int idx = _inventoryTopIndex; idx < endIndex; ++idx) { + writeVocab(CAT_INV_LIST, idx); + } +} + +void UserInterface::drawItemVocabList() { + if (_selectedInvIndex >= 0) { + InventoryObject &io = _vm->_game->_objects[ + _vm->_game->_objects._inventoryList[_selectedInvIndex]]; + for (int idx = 0; idx < io._vocabCount; ++idx) { + writeVocab(CAT_INV_VOCAB, idx); + } + } +} + +void UserInterface::drawScrolller() { + if (_scrollbarActive) + writeVocab(CAT_INV_SCROLLER, _scrollbarActive); + writeVocab(CAT_INV_SCROLLER, 4); +} + +void UserInterface::updateInventoryScroller() { + ScreenObjects &screenObjects = _vm->_game->_screenObjects; + Common::Array<int> &inventoryList = _vm->_game->_objects._inventoryList; + + if (screenObjects._inputMode != kInputBuildingSentences) + return; + + _scrollbarActive = SCROLLBAR_NONE; + + if ((screenObjects._category == CAT_INV_SCROLLER) || (screenObjects._category != CAT_INV_SCROLLER + && _scrollbarOldActive == SCROLLBAR_ELEVATOR && _vm->_events->_mouseStatusCopy)) { + if (_vm->_events->_mouseStatusCopy || _vm->_easyMouse) { + if ((_vm->_events->_mouseClicked || (_vm->_easyMouse && !_vm->_events->_mouseStatusCopy)) + && (screenObjects._category == CAT_INV_SCROLLER)) + _scrollbarStrokeType = (ScrollbarActive)screenObjects._spotId; + + if (screenObjects._spotId == _scrollbarStrokeType || _scrollbarOldActive == SCROLLBAR_ELEVATOR) { + _scrollbarActive = _scrollbarStrokeType; + uint32 currentMilli = g_system->getMillis(); + uint32 timeInc = _scrollbarQuickly ? 100 : 380; + + if (_vm->_events->_mouseStatus && (_scrollbarMilliTime + timeInc) <= currentMilli) { + _scrollbarQuickly = _vm->_events->_vD2 < 1; + _scrollbarMilliTime = currentMilli; + + switch (_scrollbarStrokeType) { + case SCROLLBAR_UP: + // Scroll up + if (_inventoryTopIndex > 0 && inventoryList.size() > 0) { + --_inventoryTopIndex; + _inventoryChanged = true; + } + break; + + case SCROLLBAR_DOWN: + // Scroll down + if (_inventoryTopIndex < ((int)inventoryList.size() - 1) && inventoryList.size() > 1) { + ++_inventoryTopIndex; + _inventoryChanged = true; + } + break; + + case SCROLLBAR_ELEVATOR: { + // Inventory slider + int newIndex = CLIP((int)_vm->_events->currentPos().y - 170, 0, 17) + * inventoryList.size() / 10; + if (newIndex >= (int)inventoryList.size()) + newIndex = inventoryList.size() - 1; + + if (inventoryList.size() > 0) { + _inventoryChanged = newIndex != _inventoryTopIndex; + _inventoryTopIndex = newIndex; + } + break; + } + + default: + break; + } + + if (_inventoryChanged) { + int dummy; + updateSelection(CAT_INV_LIST, 0, &dummy); + } + } + } + } + } + + if (_scrollbarActive != _scrollbarOldActive || _scrollbarElevator != _scrollbarOldElevator) + scrollbarChanged(); + + _scrollbarOldActive = _scrollbarActive; + _scrollbarOldElevator = _scrollbarElevator; +} + +void UserInterface::scrollbarChanged() { + Common::Rect r(73, 4, 73 + 9, 4 + 38); + _uiSlots.add(r); + _uiSlots.draw(false, false); + drawScrolller(); + updateRect(r); +} + +void UserInterface::writeVocab(ScrCategory category, int id) { + Common::Rect bounds; + if (!getBounds(category, id, bounds)) + return; + + Scene &scene = _vm->_game->_scene; + Font *font = nullptr; + + int vocabId; + Common::String vocabStr; + switch (category) { + case CAT_COMMAND: + font = _vm->_font->getFont(FONT_INTERFACE); + vocabId = scene._verbList[id]._id; + if (id == _highlightedCommandIndex) { + _vm->_font->setColorMode(SELMODE_HIGHLIGHTED); + } else { + _vm->_font->setColorMode(id == _selectedActionIndex ? SELMODE_SELECTED : SELMODE_UNSELECTED); + } + vocabStr = scene.getVocab(vocabId); + vocabStr.setChar(toupper(vocabStr[0]), 0); + font->writeString(this, vocabStr, Common::Point(bounds.left, bounds.top)); + break; + + case CAT_INV_LIST: + font = _vm->_font->getFont(FONT_INTERFACE); + vocabId = _vm->_game->_objects.getItem(id)._descId; + if (id == _highlightedInvIndex) { + _vm->_font->setColorMode(SELMODE_HIGHLIGHTED); + } else { + _vm->_font->setColorMode(id == _selectedInvIndex ? SELMODE_SELECTED : SELMODE_UNSELECTED); + } + + vocabStr = scene.getVocab(vocabId); + vocabStr.setChar(toupper(vocabStr[0]), 0); + font->writeString(this, vocabStr, Common::Point(bounds.left, bounds.top)); + break; + + case CAT_TALK_ENTRY: + font = _vm->_font->getFont(FONT_INTERFACE); + font->setColorMode(id == _highlightedCommandIndex ? SELMODE_HIGHLIGHTED : SELMODE_UNSELECTED); + font->writeString(this, _talkStrings[id], Common::Point(bounds.left, bounds.top)); + break; + + case CAT_INV_SCROLLER: + font = _vm->_font->getFont(FONT_MISC); + + switch (id) { + case 1: + vocabStr = "a"; + break; + case 2: + vocabStr = "b"; + break; + case 3: + vocabStr = "d"; + break; + case 4: + vocabStr = "c"; + break; + default: + break; + } + + font->setColorMode((id == 4) || (_scrollbarActive == SCROLLBAR_ELEVATOR) ? + SELMODE_HIGHLIGHTED : SELMODE_UNSELECTED); + font->writeString(this, vocabStr, Common::Point(bounds.left, bounds.top)); + break; + default: + // Item specific verbs + font = _vm->_font->getFont(FONT_INTERFACE); + vocabId = _vm->_game->_objects.getItem(_selectedInvIndex)._vocabList[id]._vocabId; + if (id == _highlightedItemVocabIndex) { + _vm->_font->setColorMode(SELMODE_HIGHLIGHTED); + } else { + _vm->_font->setColorMode(id == _selectedInvIndex ? SELMODE_SELECTED : SELMODE_UNSELECTED); + vocabStr = scene.getVocab(vocabId); + vocabStr.setChar(toupper(vocabStr[0]), 0); + font->writeString(this, vocabStr, Common::Point(bounds.left, bounds.top)); + break; + } + break; + } +} + +void UserInterface::loadElements() { + Scene &scene = _vm->_game->_scene; + Common::Rect bounds; + _vm->_game->_screenObjects.clear(); + + if (_vm->_game->_screenObjects._inputMode == kInputBuildingSentences) { + // Set up screen objects for the inventory scroller + for (int idx = 1; idx <= 3; ++idx) { + getBounds(CAT_INV_SCROLLER, idx, bounds); + moveRect(bounds); + + _vm->_game->_screenObjects.add(bounds, LAYER_GUI, CAT_INV_SCROLLER, idx); + } + + // Set up actions + _categoryIndexes[CAT_COMMAND - 1] = _vm->_game->_screenObjects.size() + 1; + for (int idx = 0; idx < 10; ++idx) { + getBounds(CAT_COMMAND, idx, bounds); + moveRect(bounds); + + _vm->_game->_screenObjects.add(bounds, LAYER_GUI, CAT_COMMAND, idx); + } + + // Set up inventory list + _categoryIndexes[CAT_INV_LIST - 1] = _vm->_game->_screenObjects.size() + 1; + for (int idx = 0; idx < 5; ++idx) { + getBounds(CAT_INV_LIST, idx, bounds); + moveRect(bounds); + + _vm->_game->_screenObjects.add(bounds, LAYER_GUI, CAT_INV_LIST, idx); + } + + // Set up the inventory vocab list + _categoryIndexes[CAT_INV_VOCAB - 1] = _vm->_game->_screenObjects.size() + 1; + for (int idx = 0; idx < 5; ++idx) { + getBounds(CAT_INV_VOCAB, idx, bounds); + moveRect(bounds); + + _vm->_game->_screenObjects.add(bounds, LAYER_GUI, CAT_INV_VOCAB, idx); + } + + // Set up the inventory item picture + _categoryIndexes[CAT_INV_ANIM - 1] = _vm->_game->_screenObjects.size() + 1; + _vm->_game->_screenObjects.add(Common::Rect(160, 159, 231, 194), LAYER_GUI, + CAT_INV_ANIM, 0); + } + + if (_vm->_game->_screenObjects._inputMode == kInputBuildingSentences || + _vm->_game->_screenObjects._inputMode == kInputLimitedSentences) { + _categoryIndexes[CAT_HOTSPOT - 1] = _vm->_game->_screenObjects.size() + 1; + for (int hotspotIdx = scene._hotspots.size() - 1; hotspotIdx >= 0; --hotspotIdx) { + Hotspot &hs = scene._hotspots[hotspotIdx]; + _vm->_game->_screenObjects.add(hs._bounds, LAYER_GUI, CAT_HOTSPOT, hotspotIdx); + } + } + + if (_vm->_game->_screenObjects._inputMode == kInputConversation) { + // setup areas for talk entries + _categoryIndexes[CAT_TALK_ENTRY - 1] = _vm->_game->_screenObjects.size() + 1; + for (int idx = 0; idx < 5; ++idx) { + getBounds(CAT_TALK_ENTRY, idx, bounds); + moveRect(bounds); + + _vm->_game->_screenObjects.add(bounds, LAYER_GUI, CAT_TALK_ENTRY, idx); + } + } + + // Store the number of UI elements loaded for easy nuking/refreshing hotspots added later + _vm->_game->_screenObjects._uiCount = _vm->_game->_screenObjects.size(); +} + +bool UserInterface::getBounds(ScrCategory category, int v, Common::Rect &bounds) { + int heightMultiplier, widthMultiplier; + int leftStart, yOffset, widthAmt; + + switch (category) { + case CAT_COMMAND: + heightMultiplier = v % 5; + widthMultiplier = v / 5; + leftStart = 2; + yOffset = 3; + widthAmt = 32; + break; + + case CAT_INV_LIST: + if (v < _inventoryTopIndex || v >= (_inventoryTopIndex + 5)) + return false; + + heightMultiplier = v - _inventoryTopIndex; + widthMultiplier = 0; + leftStart = 90; + yOffset = 3; + widthAmt = 69; + break; + + case CAT_TALK_ENTRY: + heightMultiplier = v; + widthMultiplier = 0; + leftStart = 2; + yOffset = 3; + widthAmt = 310; + break; + + case CAT_INV_SCROLLER: + heightMultiplier = 0; + widthMultiplier = 0; + yOffset = 0; + widthAmt = 9; + leftStart = (v != 73) ? 73 : 75; + break; + + default: + heightMultiplier = v; + widthMultiplier = 0; + leftStart = 240; + yOffset = 3; + widthAmt = 80; + break; + } + + bounds.left = (widthMultiplier > 0) ? widthMultiplier * widthAmt + leftStart : leftStart; + bounds.setWidth(widthAmt); + bounds.top = heightMultiplier * 8 + yOffset; + bounds.setHeight(8); + + if (category == CAT_INV_SCROLLER) { + switch (v) { + case SCROLLBAR_UP: + // Arrow up + bounds.top = 4; + bounds.setHeight(7); + break; + case SCROLLBAR_DOWN: + // Arrow down + bounds.top = 35; + bounds.setHeight(7); + break; + case SCROLLBAR_ELEVATOR: + // Scroller + bounds.top = 12; + bounds.setHeight(22); + break; + case SCROLLBAR_THUMB: + // Thumb + bounds.top = _scrollbarElevator + 14; + bounds.setHeight(1); + break; + default: + break; + } + } + + return true; +} + +void UserInterface::moveRect(Common::Rect &bounds) { + bounds.translate(0, MADS_SCENE_HEIGHT); +} + +void UserInterface::drawConversationList() { + for (uint idx = 0; idx < _talkStrings.size(); ++idx) { + writeVocab(CAT_TALK_ENTRY, idx); + } +} + +void UserInterface::emptyConversationList() { + _talkStrings.clear(); + _talkIds.clear(); +} + +void UserInterface::addConversationMessage(int vocabId, const Common::String &msg) { + assert(_talkStrings.size() < 5); + + _talkStrings.push_back(msg); + _talkIds.push_back(vocabId); +} + +void UserInterface::loadInventoryAnim(int objectId) { + Scene &scene = _vm->_game->_scene; + noInventoryAnim(); + + if (_vm->_invObjectsAnimated) { + Common::String resName = Common::String::format("*OB%.3dI", objectId); + SpriteAsset *asset = new SpriteAsset(_vm, resName, 8); + _invSpritesIndex = scene._sprites.add(asset, 1); + if (_invSpritesIndex >= 0) { + _invFrameNumber = 1; + } + } +} + +void UserInterface::noInventoryAnim() { + Scene &scene = _vm->_game->_scene; + + if (_invSpritesIndex >= 0) { + scene._sprites.remove(_invSpritesIndex); + _vm->_game->_screenObjects._baseTime = _vm->_events->getFrameCounter(); + _invSpritesIndex = -1; + } + + if (_vm->_game->_screenObjects._inputMode == kInputBuildingSentences) + refresh(); +} + +void UserInterface::refresh() { + _uiSlots.clear(); + _uiSlots.fullRefresh(); + _uiSlots.draw(false, false); + + drawTextElements(); +} + +void UserInterface::inventoryAnim() { + Scene &scene = _vm->_game->_scene; + if (_vm->_game->_screenObjects._inputMode == kInputConversation || + _vm->_game->_screenObjects._inputMode == kInputLimitedSentences || + _invSpritesIndex < 0) + return; + + // Move to the next frame number in the sequence, resetting if at the end + SpriteAsset *asset = scene._sprites[_invSpritesIndex]; + if (++_invFrameNumber > asset->getCount()) + _invFrameNumber = 1; + + // Loop through the slots list for inventory animation entry + for (uint i = 0; i < _uiSlots.size(); ++i) { + if (_uiSlots[i]._segmentId == IMG_SPINNING_OBJECT) + _uiSlots[i]._flags = IMG_FULL_UPDATE; + } + + // Add a new slot entry for the inventory animation + UISlot slot; + slot._flags = IMG_UPDATE; + slot._segmentId = IMG_SPINNING_OBJECT; + slot._frameNumber = _invFrameNumber; + slot._spritesIndex = _invSpritesIndex; + slot._position = Common::Point(160, 3); + + _uiSlots.push_back(slot); +} + +void UserInterface::doBackgroundAnimation() { + Scene &scene = _vm->_game->_scene; + Common::Array<AnimUIEntry> &uiEntries = scene._animationData->_uiEntries; + Common::Array<AnimFrameEntry> &frameEntries = scene._animationData->_frameEntries; + + _noSegmentsActive = !_someSegmentsActive; + _someSegmentsActive = false; + + for (int idx = 0; idx < (int)uiEntries.size(); ++idx) { + AnimUIEntry &uiEntry = uiEntries[idx]; + + if (uiEntry._counter < 0) { + if (uiEntry._counter == -1) { + int probabilityRandom = _vm->getRandomNumber(1, 30000); + int probability = uiEntry._probability; + if (uiEntry._probability > 30000) { + if (_noSegmentsActive) { + probability -= 30000; + } else { + probability = -1; + } + } + if (probabilityRandom <= probability) { + uiEntry._counter = uiEntry._firstImage; + _someSegmentsActive = true; + } + } else { + uiEntry._counter = uiEntry._firstImage; + _someSegmentsActive = true; + } + } else { + for (int idx2 = 0; idx2 < ANIM_SPAWN_COUNT; idx2++) { + if (uiEntry._spawnFrame[idx2] == (uiEntry._counter - uiEntry._firstImage)) { + int tempIndex = uiEntry._spawn[idx2]; + if (idx >= tempIndex) { + uiEntries[tempIndex]._counter = uiEntries[tempIndex]._firstImage; + } else { + uiEntries[tempIndex]._counter = -2; + } + _someSegmentsActive = true; + } + } + + ++uiEntry._counter; + if (uiEntry._counter > uiEntry._lastImage) { + uiEntry._counter = -1; + } else { + _someSegmentsActive = true; + } + } + } + + for (uint idx = 0; idx < uiEntries.size(); ++idx) { + int imgScan = uiEntries[idx]._counter; + if (imgScan >= 0) { + _uiSlots.add(frameEntries[imgScan]); + } + } +} + +void UserInterface::categoryChanged() { + _highlightedInvIndex = -1; + _vm->_events->initVars(); + _category = CAT_NONE; +} + +void UserInterface::selectObject(int invIndex) { + if (_selectedInvIndex != invIndex || _inventoryChanged) { + int oldVocabCount = _selectedInvIndex < 0 ? 0 : _vm->_game->_objects.getItem(_selectedInvIndex)._vocabCount; + int newVocabCount = invIndex < 0 ? 0 : _vm->_game->_objects.getItem(invIndex)._vocabCount; + int maxVocab = MAX(oldVocabCount, newVocabCount); + + updateSelection(CAT_INV_LIST, invIndex, &_selectedInvIndex); + _highlightedItemVocabIndex = -1; + _selectedItemVocabIdx = -1; + + if (maxVocab) { + assert(_uiSlots.size() < 50); + int vocabHeight = maxVocab * 8; + + Common::Rect bounds(240, 3, 240 + 80, 3 + vocabHeight); + _uiSlots.add(bounds); + _uiSlots.draw(false, false); + drawItemVocabList(); + updateRect(bounds); + } + } + + if (invIndex == -1) { + noInventoryAnim(); + } else { + loadInventoryAnim(_vm->_game->_objects._inventoryList[invIndex]); + _vm->_palette->setPalette(_vm->_palette->_mainPalette, 7, 1); + _vm->_palette->setPalette(_vm->_palette->_mainPalette, 246, 2); + } +} + +void UserInterface::updateSelection(ScrCategory category, int newIndex, int *idx) { + Game &game = *_vm->_game; + Common::Array<int> &invList = game._objects._inventoryList; + Common::Rect bounds; + + if (category == CAT_INV_LIST && _inventoryChanged) { + *idx = newIndex; + bounds = Common::Rect(90, 3, 90 + 69, 3 + 40); + _uiSlots.add(bounds); + _uiSlots.draw(false, false); + drawInventoryList(); + updateRect(bounds); + _inventoryChanged = false; + + if (invList.size() < 2) { + _scrollbarElevator = 0; + } else { + int v = _inventoryTopIndex * 18 / (invList.size() - 1); + _scrollbarElevator = MIN(v, 17); + } + } else { + int oldIndex = *idx; + *idx = newIndex; + + if (oldIndex >= 0) { + writeVocab(category, oldIndex); + + if (getBounds(category, oldIndex, bounds)) + updateRect(bounds); + } + + if (newIndex >= 0) { + writeVocab(category, newIndex); + + if (getBounds(category, newIndex, bounds)) + updateRect(bounds); + } + } +} + +void UserInterface::updateRect(const Common::Rect &bounds) { + Common::Rect r = bounds; + r.translate(0, MADS_SCENE_HEIGHT); + _vm->_screen.copyRectToScreen(r); +} + +void UserInterface::scrollerChanged() { + warning("TODO: scrollerChanged"); +} + +void UserInterface::scrollInventory() { + Common::Array<int> &invList = _vm->_game->_objects._inventoryList; + + if (_vm->_events->_mouseButtons) { + int yp = _vm->_events->currentPos().y; + if (yp < MADS_SCENE_HEIGHT || yp == (MADS_SCREEN_HEIGHT - 1)) { + uint32 timeDiff = _scrollFlag ? 100 : 380; + uint32 currentMilli = g_system->getMillis(); + _vm->_game->_screenObjects._v8332A = -1; + + if (currentMilli >= (_scrollMilli + timeDiff)) { + _scrollMilli = currentMilli; + _scrollFlag = true; + + if (yp == (MADS_SCREEN_HEIGHT - 1)) { + if (_inventoryTopIndex < ((int)invList.size() - 1)) { + ++_inventoryTopIndex; + _inventoryChanged = true; + } + } else { + if (_inventoryTopIndex > 0) { + --_inventoryTopIndex; + _inventoryChanged = true; + } + } + } + } + } + + _vm->_game->_screenObjects._v8332A = 0; +} + +void UserInterface::synchronize(Common::Serializer &s) { + InventoryObjects &invObjects = _vm->_game->_objects; + + if (s.isLoading()) { + _selectedInvIndex = invObjects._inventoryList.empty() ? -1 : 0; + } + + for (int i = 0; i < 8; ++i) + s.syncAsSint16LE(_categoryIndexes[i]); +} + +} // End of namespace MADS |