aboutsummaryrefslogtreecommitdiff
path: root/engines/cryomni3d/versailles/toolbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/cryomni3d/versailles/toolbar.cpp')
-rw-r--r--engines/cryomni3d/versailles/toolbar.cpp599
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