aboutsummaryrefslogtreecommitdiff
path: root/engines/mads/user_interface.cpp
diff options
context:
space:
mode:
authoruruk2014-05-30 11:14:47 +0200
committeruruk2014-05-30 11:14:47 +0200
commitbb2f8dd68e1d6b2b30b07f60c0cd4e125b47ea4d (patch)
tree9a1e28cfb1eb1a322225c05adc0962a2f96ea521 /engines/mads/user_interface.cpp
parent5ad4e157e5398347651a0da0db07f9daf01bf373 (diff)
parent0a46d67baea121bed0511ce45bfdd8438a43d35d (diff)
downloadscummvm-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.cpp1096
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