/* 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 3 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, see . * */ #include "engines/nancy/nancy.h" #include "engines/nancy/graphics.h" #include "engines/nancy/resource.h" #include "engines/nancy/cursor.h" #include "engines/nancy/sound.h" #include "engines/nancy/input.h" #include "engines/nancy/util.h" #include "engines/nancy/ui/inventorybox.h" #include "engines/nancy/state/scene.h" #include "engines/nancy/ui/scrollbar.h" namespace Nancy { namespace UI { InventoryBox::InventoryBox() : RenderObject(6), _scrollbarPos(0), _highlightedHotspot(-1), _inventoryData(nullptr) {} void InventoryBox::init() { auto *bootSummary = GetEngineData(BSUM); assert(bootSummary); _inventoryData = GetEngineData(INV); assert(_inventoryData); _order.clear(); moveTo(bootSummary->inventoryBoxScreenPosition); g_nancy->_resource->loadImage(_inventoryData->inventoryBoxIconsImageName, _iconsSurface); _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((g_nancy->getStaticData().numItems / 4) + 1), g_nancy->_graphics->getScreenPixelFormat()); Common::Rect sourceRect = _screenPosition; sourceRect.moveTo(0, 0); _drawSurface.create(_fullInventorySurface, sourceRect); for (uint i = 0; i < 4; ++i) { Common::Rect &r = _itemHotspots[i].hotspot; r = _screenPosition; r.setWidth(r.width() / 2); r.setHeight(r.height() / 2); r.translate((i % 2) * r.width(), (i / 2) * r.height()); } RenderObject::init(); _scrollbar.reset(new Scrollbar( 9, _inventoryData->scrollbarSrcBounds, _inventoryData->scrollbarDefaultPos, _inventoryData->scrollbarMaxScroll - _inventoryData->scrollbarDefaultPos.y)); _scrollbar->init(); _curtains.init(); } void InventoryBox::updateGraphics() { if (_scrollbarPos != _scrollbar->getPos()) { _scrollbarPos = _scrollbar->getPos(); onScrollbarMove(); } } void InventoryBox::registerGraphics() { RenderObject::registerGraphics(); _scrollbar->registerGraphics(); _curtains.registerGraphics(); } void InventoryBox::handleInput(NancyInput &input) { // Disable input when primary video is playing if (NancySceneState.getActiveConversation()) { return; } if (_order.size()) { _scrollbar->handleInput(input); } int hoveredHotspot = -1; for (uint i = 0; i < 4; ++i) { if (_itemHotspots[i].hotspot.contains(input.mousePos)) { if (NancySceneState.getHeldItem() != -1) { g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow); if (input.input & NancyInput::kLeftMouseButtonUp) { NancySceneState.addItemToInventory(NancySceneState.getHeldItem()); g_nancy->_sound->playSound("BULS"); } } else if (_itemHotspots[i].itemID != -1) { g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow); hoveredHotspot = i; if (input.input & NancyInput::kLeftMouseButtonUp) { uint16 itemID = _itemHotspots[i].itemID; INV::ItemDescription item = _inventoryData->itemDescriptions[itemID]; byte disabled = NancySceneState.getItemDisabledState(itemID); if (!disabled) { // Item is not disabled NancySceneState.removeItemFromInventory(itemID, item.keepItem != kInvItemNewSceneView); _highlightedHotspot = -1; hoveredHotspot = -1; if (item.keepItem == kInvItemNewSceneView) { // Transport the player to a close-up scene, temporarily remove the item from the inventory NancySceneState.pushScene(itemID); SceneChangeDescription sceneChange; sceneChange.sceneID = item.sceneID; sceneChange.continueSceneSound = item.sceneSoundFlag; NancySceneState.changeScene(sceneChange); } } else { // Item is disabled if (disabled == 2) { // ...and set so it plays the "can't" sound when you click it NancySceneState.playItemCantSound(itemID); } } } } break; } } if (_highlightedHotspot != hoveredHotspot) { if (_highlightedHotspot != -1) { // Un-highlight last hovered item drawItemInSlot(_itemHotspots[_highlightedHotspot].itemID, _itemHotspots[_highlightedHotspot].itemOrder, false); _highlightedHotspot = -1; } if (hoveredHotspot != -1) { // Highlight hovered item drawItemInSlot(_itemHotspots[hoveredHotspot].itemID, _itemHotspots[hoveredHotspot].itemOrder, true); _highlightedHotspot = hoveredHotspot; } } } void InventoryBox::addItem(const int16 itemID) { if (_order.size() == 0) { // Adds first item, start curtains animation _curtains.setOpen(true); } Common::Array back = _order; _order.clear(); _order.push_back(itemID); _order.push_back(back); onReorder(); } void InventoryBox::removeItem(const int16 itemID) { for (auto &i : _order) { if (i == itemID) { _order.erase(&i); onReorder(); break; } } } void InventoryBox::onReorder() { onScrollbarMove(); _fullInventorySurface.clear(); for (uint i = 0; i < _order.size(); ++i) { drawItemInSlot(_order[i], i); } if (_order.size() > 0) { _curtains.setOpen(true); } else { _curtains.setOpen(false); } _needsRedraw = true; } void InventoryBox::setHotspots(const uint pageNr) { for (uint i = 0; i < 4; ++i) { if (i + pageNr * 4 < _order.size()) { _itemHotspots[i].itemID = _order[i + pageNr * 4]; _itemHotspots[i].itemOrder = i + pageNr * 4; } else { _itemHotspots[i].itemID = -1; _itemHotspots[i].itemOrder = -1; } } } void InventoryBox::drawItemInSlot(const uint itemID, const uint slotID, const bool highlighted) { auto &item = _inventoryData->itemDescriptions[itemID]; Common::Rect dest; dest.setWidth(_screenPosition.width() / 2); dest.setHeight(_screenPosition.height() / 2); dest.moveTo((slotID % 2) * dest.width(), (slotID / 2) * dest.height()); Common::Point destPoint = Common::Point (dest.left, dest.top); _fullInventorySurface.blitFrom(_iconsSurface, highlighted ? item.highlightedSourceRect : item.sourceRect, destPoint); _needsRedraw = true; } void InventoryBox::onScrollbarMove() { float scrollPos = _scrollbar->getPos(); float numPages = (_order.size() - 1) / 4 + 1; float pageFrac = 1 / numPages; uint curPage = MIN(scrollPos / pageFrac, numPages - 1); Common::Rect sourceRect = _screenPosition; sourceRect.moveTo(0, curPage * (sourceRect.height() - 1)); _drawSurface.create(_fullInventorySurface, sourceRect); setHotspots(curPage); _needsRedraw = true; } InventoryBox::Curtains::Curtains() : RenderObject(10), _soundTriggered(false), _areOpen(false), _curFrame(0), _numFrames(g_nancy->getGameType() == kGameTypeVampire ? 10 : 7) {} void InventoryBox::Curtains::init() { auto *inventoryData = GetEngineData(INV); assert(inventoryData); moveTo(inventoryData->curtainsScreenPosition); Common::Rect bounds = _screenPosition; bounds.moveTo(0, 0); _drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphics->getInputPixelFormat()); if (g_nancy->getGameType() == kGameTypeVampire) { uint8 palette[256 * 3]; g_nancy->_graphics->_object0.grabPalette(palette, 0, 256); _drawSurface.setPalette(palette, 0, 256); } _nextFrameTime = 0; setAnimationFrame(_curFrame); setTransparent(true); RenderObject::init(); } void InventoryBox::Curtains::updateGraphics() { Time time = g_nancy->getTotalPlayTime(); if (_areOpen) { if (_curFrame < _numFrames && time > _nextFrameTime) { auto *inventoryData = GetEngineData(INV); assert(inventoryData); setAnimationFrame(++_curFrame); _nextFrameTime = time + inventoryData->curtainsFrameTime; if (!_soundTriggered) { _soundTriggered = true; g_nancy->_sound->playSound("CURT"); } } } else { if (_curFrame > 0 && time > _nextFrameTime) { auto *inventoryData = GetEngineData(INV); assert(inventoryData); setAnimationFrame(--_curFrame); _nextFrameTime = time + inventoryData->curtainsFrameTime; if (!_soundTriggered) { _soundTriggered = true; g_nancy->_sound->playSound("CURT"); } } } if (_curFrame == 0 || _curFrame == _numFrames) { _soundTriggered = false; } } void InventoryBox::Curtains::setAnimationFrame(uint frame) { Graphics::ManagedSurface &_object0 = g_nancy->_graphics->_object0; Common::Rect srcRect; Common::Point destPoint; if (frame > (uint)(_numFrames - 1)) { // TVD keeps the last frame visible if (g_nancy->getGameType() > kGameTypeVampire) { setVisible(false); } return; } else { setVisible(true); } auto *inventoryData = GetEngineData(INV); assert(inventoryData); _drawSurface.clear(g_nancy->_graphics->getTransColor()); // Draw left curtain srcRect = inventoryData->curtainAnimationSrcs[frame * 2]; _drawSurface.blitFrom(_object0, srcRect, destPoint); // Draw right curtain srcRect = inventoryData->curtainAnimationSrcs[frame * 2 + 1]; destPoint.x = getBounds().width() - srcRect.width(); _drawSurface.blitFrom(_object0, srcRect, destPoint); _needsRedraw = true; } } // End of namespace UI } // End of namespace Nancy