diff options
Diffstat (limited to 'engines/cryomni3d/versailles/toolbar.cpp')
-rw-r--r-- | engines/cryomni3d/versailles/toolbar.cpp | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/engines/cryomni3d/versailles/toolbar.cpp b/engines/cryomni3d/versailles/toolbar.cpp new file mode 100644 index 0000000000..e0168e29cc --- /dev/null +++ b/engines/cryomni3d/versailles/toolbar.cpp @@ -0,0 +1,599 @@ +/* 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/system.h" + +#include "cryomni3d/cryomni3d.h" + +#include "cryomni3d/versailles/toolbar.h" + +namespace CryOmni3D { +namespace Versailles { + +void Toolbar::init(const Sprites *sprites, FontManager *fontManager, + const Common::Array<Common::String> *messages, Inventory *inventory, + CryOmni3DEngine *engine) { + _sprites = sprites; + _fontManager = fontManager; + _messages = messages; + _inventory = inventory; + _engine = engine; + + _bgSurface.create(640, 60, Graphics::PixelFormat::createFormatCLUT8()); + _destSurface.create(640, 60, Graphics::PixelFormat::createFormatCLUT8()); + + // Inventory + addZone(51, 56, Common::Point(211, 8), &Toolbar::callbackInventory<0>); + addZone(51, 56, Common::Point(258, 8), &Toolbar::callbackInventory<1>); + addZone(51, 56, Common::Point(305, 8), &Toolbar::callbackInventory<2>); + addZone(51, 56, Common::Point(352, 8), &Toolbar::callbackInventory<3>); + addZone(51, 56, Common::Point(399, 8), &Toolbar::callbackInventory<4>); + addZone(51, 56, Common::Point(446, 8), &Toolbar::callbackInventory<5>); + addZone(51, 56, Common::Point(493, 8), &Toolbar::callbackInventory<6>); + addZone(51, 56, Common::Point(540, 8), &Toolbar::callbackInventory<7>); + + // Documentation + const Graphics::Cursor &cursorDoc = _sprites->getCursor(133); + Common::Point docPos(627 - cursorDoc.getWidth(), 42 - cursorDoc.getHeight()); + addZone(133, 137, docPos, &Toolbar::callbackDocumentation); + + // Options + const Graphics::Cursor &cursorOpt = _sprites->getCursor(225); + Common::Point optPos(0, 60 - cursorOpt.getHeight()); + addZone(225, 225, optPos, &Toolbar::callbackOptions); + + // Previous or next + addZone(183, -1, Common::Point(190, 18), &Toolbar::callbackInventoryPrev); + addZone(240, -1, Common::Point(574, 18), &Toolbar::callbackInventoryNext); + // View + addZone(142, -1, Common::Point(158, 12), &Toolbar::callbackViewObject); +} + +Toolbar::~Toolbar() { + _bgSurface.free(); + _destSurface.free(); +} + +void Toolbar::inventoryChanged(unsigned int newPosition) { + if (newPosition != -1u && newPosition > _inventoryOffset) { + _inventoryOffset = newPosition - 7; + } + // Refresh + updateZones(); +} + +void Toolbar::addZone(uint16 cursorMainId, uint16 cursorSecondaryId, Common::Point position, + ZoneCallback callback) { + const Graphics::Cursor &cursorMain = _sprites->getCursor(cursorMainId); + Common::Rect rct(cursorMain.getWidth(), cursorMain.getHeight()); + rct.moveTo(position); + + // By default it's the secondary image + Zone zone = { rct, cursorMainId, cursorSecondaryId, callback, true, false }; + _zones.push_back(zone); +} + +Common::Array<Toolbar::Zone>::const_iterator Toolbar::hitTestZones(const Common::Point &mousePos) +const { + Common::Array<Zone>::const_iterator it; + for (it = _zones.begin(); it != _zones.end(); it++) { + if (!it->hidden && it->rect.contains(mousePos) && it->callback) { + break; + } + } + return it; +} + +unsigned int Toolbar::captureEvent(const Common::Point &mousePos, unsigned int dragStatus) { + unsigned int result = 0; + Common::Array<Zone>::const_iterator it = hitTestZones(mousePos); + if (it != _zones.end()) { + result = (this->*(it->callback))(dragStatus); + } + return result; +} + +void Toolbar::updateZones() { + _zones[8].secondary = !_engine->hasPlaceDocumentation(); + + Inventory::const_iterator inventoryIt, inventorySelectedIt; + if (!_inventoryEnabled) { + _inventoryMaxOffset = 0; + _inventoryOffset = 0; + _zones[10].secondary = true; + _zones[11].secondary = true; + } else { + _inventoryMaxOffset = 0; + // Find an object in inventory after the 8 first + for (inventoryIt = _inventory->begin() + 8; inventoryIt != _inventory->end(); inventoryIt++) { + if (*inventoryIt != nullptr) { + _inventoryMaxOffset = (inventoryIt - _inventory->begin()) - 7; + } + } + _zones[10].secondary = !_inventoryMaxOffset; + _zones[11].secondary = !_inventoryMaxOffset; + if (_inventoryOffset > _inventoryMaxOffset) { + // Clamp inventory offset to its max + _inventoryOffset = _inventoryMaxOffset; + } + inventoryIt = _inventory->begin() + _inventoryOffset; + inventorySelectedIt = _inventory->begin() + _inventorySelected; + } + // Inventory zones are from 0 to 7 + for (Common::Array<Zone>::iterator zoneIt = _zones.begin(); zoneIt != _zones.begin() + 8; + zoneIt++, inventoryIt++) { + if (!_inventoryEnabled) { + zoneIt->hidden = true; + zoneIt->imageMain = 0; + zoneIt->imageSecondary = 0; + zoneIt->secondary = false; + } else if (inventoryIt >= _inventory->end() || *inventoryIt == nullptr) { + // Nothing in inventory at this position + zoneIt->hidden = false; + zoneIt->imageMain = 51; + zoneIt->imageSecondary = 56; + zoneIt->secondary = true; + } else { + // Setup inventory icon + zoneIt->hidden = false; + zoneIt->imageMain = (*inventoryIt)->idCA(); + zoneIt->imageSecondary = (*inventoryIt)->idCl(); + zoneIt->secondary = (inventorySelectedIt != inventoryIt); + } + } +} + +unsigned int Toolbar::callbackInventory(unsigned int invId, unsigned int dragStatus) { + if (!_inventoryEnabled) { + return 0; + } + + invId += _inventoryOffset; + Object *obj = nullptr; + if (invId < _inventory->size()) { + obj = (*_inventory)[invId]; + } + if (obj == nullptr) { + return 0; + } + + if (!obj->valid()) { + return 0; + } + + switch (dragStatus) { + case kDragStatus_Pressed: + _inventorySelected = invId; + _engine->setCursor(181); + _zones[12].secondary = (obj->viewCallback() == nullptr); + _inventory_button_dragging = true; + return 1; + case kDragStatus_Dragging: + if (_inventorySelected == invId) { + return 0; + } + _inventorySelected = invId; + _zones[12].secondary = (obj->viewCallback() == nullptr); + _inventory_button_dragging = true; + return 1; + case kDragStatus_Finished: + _engine->setCursor(obj->idSl()); + _inventory->setSelectedObject(obj); + _inventorySelected = invId; + return 1; + default: + return 0; + } + +} + +unsigned int Toolbar::callbackInventoryPrev(unsigned int dragStatus) { + if (!_inventoryEnabled) { + return 0; + } + + if (dragStatus == kDragStatus_Pressed && _inventoryOffset > 0) { + // Restart auto repeat only if there could be something + _engine->setAutoRepeatClick(150); + _inventoryOffset--; + return 1; + } + // In any other case we didn't do anything + return 0; +} + +unsigned int Toolbar::callbackInventoryNext(unsigned int dragStatus) { + if (!_inventoryEnabled) { + return 0; + } + + if (dragStatus == kDragStatus_Pressed && _inventoryOffset < _inventoryMaxOffset) { + _engine->setAutoRepeatClick(150); + _inventoryOffset++; + return 1; + } + // In any other case we didn't do anything + return 0; +} + +unsigned int Toolbar::callbackViewObject(unsigned int dragStatus) { + if (!_inventoryEnabled) { + return 0; + } + + _mouse_in_view_object = true; + + if (_inventorySelected == -1u) { + // Nothing selected in toolbar + return 0; + } + Inventory::const_iterator inventorySelectedIt = _inventory->begin() + _inventorySelected; + Object *selectedObject = *inventorySelectedIt; + if (selectedObject == nullptr || selectedObject->viewCallback() == nullptr) { + // Nothing to view, the sprite isn't even displayed + return 0; + } + + switch (dragStatus) { + case kDragStatus_NoDrag: + _backup_selected_object = selectedObject; + _engine->setCursor(181); + return 0; + case kDragStatus_Pressed: + case kDragStatus_Dragging: + return 1; + case kDragStatus_Finished: + // Just clicked + g_system->showMouse(false); + (*selectedObject->viewCallback())(); + g_system->showMouse(true); + _parentMustRedraw = true; + _shortExit = true; + return 1; + default: + return 0; + } +} + +unsigned int Toolbar::callbackOptions(unsigned int dragStatus) { + _mouse_in_options = true; + + switch (dragStatus) { + case kDragStatus_NoDrag: + _backup_selected_object = _inventory->selectedObject(); + _engine->setCursor(181); + return 0; + case kDragStatus_Pressed: + case kDragStatus_Dragging: + // Nothing to do, we wait release + return 0; + case kDragStatus_Finished: + // Just clicked + _engine->displayOptions(); + _parentMustRedraw = true; + _shortExit = true; + _engine->setMousePos(Common::Point(320, 240)); // Center of screen + // Displaying options hides the mouse + g_system->showMouse(true); + return 0; + default: + return 0; + } +} + +unsigned int Toolbar::callbackDocumentation(unsigned int dragStatus) { + _mouse_in_options = true; + + switch (dragStatus) { + case kDragStatus_NoDrag: + case kDragStatus_Pressed: + case kDragStatus_Dragging: + // Nothing to do, we wait release + return 0; + case kDragStatus_Finished: + // Just clicked + if (_engine->displayPlaceDocumentation()) { + _parentMustRedraw = true; + _shortExit = true; + _engine->setMousePos(Common::Point(320, 240)); // Center of screen + } + return 0; + default: + return 0; + } +} + +void Toolbar::drawToolbar(const Graphics::Surface *original) { + if (_position > 60) { + _position = 60; + } + + if (_position != 0) { + // Not entirely drawn, we must copy a part of the original image + Common::Rect rct(0, 420, 640, 420 + _position); + _destSurface.copyRectToSurface(*original, 0, 0, rct); + } + + if (_position == 60) { + // Entirely hidden, just stop there, we have nothing to draw + return; + } + + // Not entirely hidden, we must display the transparent background prepared for us + Common::Rect rct(0, _position, 640, 60); + _destSurface.copyRectToSurface(_bgSurface, 0, _position, rct); + + // Now draw the various zones on the surface + for (Common::Array<Zone>::const_iterator it = _zones.begin(); it != _zones.end(); it++) { + if (it->hidden) { + continue; + } + + uint16 spriteId = it->secondary ? it->imageSecondary : it->imageMain; + if (spriteId == uint16(-1)) { + continue; + } + + Common::Rect dst = it->rect; + dst.translate(0, _position); + + // Clip the rectangle to fit inside the surface + dst.clip(Common::Rect(_destSurface.w, _destSurface.h)); + + if (dst.isEmpty()) { + continue; + } + + const Graphics::Surface &sprite = _sprites->getSurface(spriteId); + _destSurface.transBlitFrom(sprite, Common::Rect(dst.width(), dst.height()), dst, + _sprites->getKeyColor(spriteId)); + } + + // And now draw the object description if needed + if (_inventoryEnabled && _inventoryHovered != -1u) { + Object *obj = (*_inventory)[_inventoryHovered]; + + unsigned int zoneId = _inventoryHovered - _inventoryOffset; + if (zoneId >= 8) { + // The object is hidden: huh? + return; + } + + _fontManager->setSurface(&_destSurface); + _fontManager->setForeColor(243); + _fontManager->setCurrentFont(5); + _fontManager->setTransparentBackground(true); + const Common::String &objName = (*_messages)[obj->idOBJ()]; + unsigned int x = 195 - _fontManager->getStrWidth(objName); + unsigned int startX = _zones[zoneId].rect.left + kTextOffset; + _fontManager->displayStr(x, 38 + _position, objName); + _destSurface.hLine(x, 54 + _position, startX - 1, 243); // minus 1 because hLine draws inclusive + _destSurface.vLine(startX, 42 + _position, 54 + _position, 243); + } +} + +bool Toolbar::displayToolbar(const Graphics::Surface *original) { + /** + * In game there are 2 functions to handle toolbar: one in warp and one in fixed images + * This one is the warp one and fixed images have a more asynchronous one during pop-up/down phases + * Let's make it simple for now + */ + + // WORKAROUND: Set cursor here to be more consistent: it's thumb cursor just before showing until just after showed + _engine->setCursor(181); + + _parentMustRedraw = false; + _shortExit = false; + + // Prepare the background of the toolbar by making it translucent + // Get the lowest part of the image + const Graphics::Surface subset = original->getSubArea(Common::Rect(0, original->h - _bgSurface.h, + _bgSurface.w, original->h)); + _engine->makeTranslucent(_bgSurface, subset); + + // Draw the original surface on the surface to use for toolbar + g_system->copyRectToScreen(original->getPixels(), original->pitch, 0, 0, original->w, original->h); + + // WORKAROUND: Reset the inventory status at init to let sprites highlighted until toolbar is hidden + _inventorySelected = -1; + _inventoryHovered = -1; + _zones[12].secondary = true; + + updateZones(); + + for (_position = 60; _position > 0; _position--) { + // Make the toolbar go up + drawToolbar(original); + g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0, + original->h - _destSurface.h, _destSurface.w, _destSurface.h); + g_system->updateScreen(); + // pollEvents will slow down the animation because it waits 10ms + _engine->pollEvents(); + if (g_engine->shouldQuit()) { + return false; + } + } + + // Flush events + _engine->clearKeys(); + _engine->waitMouseRelease(); + + handleToolbarEvents(original); + if (g_engine->shouldQuit()) { + return false; + } + + if (_shortExit) { + return _parentMustRedraw; + } + + for (_position = 0; _position <= 60; _position++) { + // Make the toolbar go up + drawToolbar(original); + g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0, + original->h - _destSurface.h, _destSurface.w, _destSurface.h); + g_system->updateScreen(); + // pollEvents will slow down the animation because it waits 10ms + _engine->pollEvents(); + if (g_engine->shouldQuit()) { + return false; + } + } + + return _parentMustRedraw; +} + +void Toolbar::handleToolbarEvents(const Graphics::Surface *original) { + bool mouseInsideToolbar; + bool exitToolbar = false; + bool redrawToolbar; + + // Don't have anything hovered for now + _inventoryHovered = -1; + _inventorySelected = -1; + _inventory->setSelectedObject(nullptr); + _backup_selected_object = nullptr; + + // Refresh zones because we erased selected object + updateZones(); + + // No need of original surface because the toolbar is fully displayed + drawToolbar(original); + + // TODO: countdown + + g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0, + original->h - _destSurface.h, _destSurface.w, _destSurface.h); + g_system->updateScreen(); + + _engine->setCursor(181); + + mouseInsideToolbar = (_engine->getMousePos().y > 388); + + while (!exitToolbar) { + _mouse_in_options = false; + _mouse_in_view_object = false; + + _engine->pollEvents(); + if (g_engine->shouldQuit()) { + exitToolbar = true; + break; + } + + redrawToolbar = false; + if (_engine->checkKeysPressed(2, Common::KEYCODE_ESCAPE, Common::KEYCODE_SPACE) || + _engine->getCurrentMouseButton() == 2) { + _engine->waitMouseRelease(); + exitToolbar = true; + break; + } + + Common::Point mousePosInToolbar = _engine->getMousePos(); + mousePosInToolbar -= Common::Point(0, 420); + + if (captureEvent(mousePosInToolbar, _engine->getDragStatus())) { + // Something has changed with the zones handling, update zones + updateZones(); + redrawToolbar = true; + } else if (_engine->getDragStatus() == kDragStatus_Pressed) { + // A click happened and wasn't handled, deselect object + _inventorySelected = -1; + _inventory->setSelectedObject(nullptr); + _engine->setCursor(181); + // Reset view object + _zones[12].secondary = true; + updateZones(); + redrawToolbar = true; + } + + if (!mouseInsideToolbar) { + mouseInsideToolbar = (_engine->getMousePos().y > 388); + } else if (_engine->getMousePos().y <= 388) { + // mouseInsideToolbar is true and the mouse is outside the toolbar + exitToolbar = true; + break; + } + + if (_engine->getCurrentMouseButton() == 1) { + // When the mouse button is down, nothing is selected + // It's selected on release + _inventory->setSelectedObject(nullptr); + } + + if (_backup_selected_object != nullptr && !(_mouse_in_options || _mouse_in_view_object) && + !_engine->getCurrentMouseButton()) { + _inventory->setSelectedObject(_backup_selected_object); + _engine->setCursor(_backup_selected_object->idSl()); + _backup_selected_object = nullptr; + } + + // Hover the inventory objects + if (_inventory->selectedObject() == nullptr /* || _inventory_button_dragging */) { + // The 2nd above condition is maybe useless because when the mouse button is down the selected object is always null + bool shouldHover = false; + Common::Array<Zone>::const_iterator zoneIt = hitTestZones(mousePosInToolbar); + unsigned int zoneId = zoneIt - _zones.begin(); + unsigned int inventoryId = zoneId + _inventoryOffset; + if (zoneId < 8 && inventoryId < _inventory->size() && (*_inventory)[inventoryId] != nullptr) { + // It's the inventory + shouldHover = true; + if (_inventoryHovered != inventoryId && (*_inventory)[inventoryId]->valid()) { + // It's not the one currently hovered and it's a valid object + _inventoryHovered = inventoryId; + redrawToolbar = true; + } + } + if (!shouldHover && _inventoryHovered != -1u && !_mouse_in_view_object) { + // Remove hovering + _inventoryHovered = -1; + _inventorySelected = -1; + updateZones(); + if (!_inventory->selectedObject()) { + // Reset back the cursor if nothing is selected + _engine->setCursor(181); + } + // Remove view + _zones[12].secondary = true; + redrawToolbar = true; + } + _inventory_button_dragging = false; + } + + if (_parentMustRedraw) { + break; + } + + if (redrawToolbar) { + drawToolbar(original); + g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0, + original->h - _destSurface.h, _destSurface.w, _destSurface.h); + } + + g_system->updateScreen(); + } + + // Hide description when finished and selected object + // WORKAROUND: moved to the start to keep the selected object hilighted until the toolbar disappearance +} + +} // End of namespace Versailles +} // End of namespace CryOmni3D |