scummvm/engines/stark/ui.cpp
Bastien Bouclet 273a358ad3 STARK: Initiate rework of the user interface code
A base class, Window, will be used to ensure all the UI containers
work in a similar way.
2016-01-01 07:29:48 +01:00

282 lines
7.9 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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 "common/events.h"
#include "engines/stark/ui.h"
#include "engines/stark/actionmenu.h"
#include "engines/stark/cursor.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/services/fmvplayer.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/ui/topmenu.h"
#include "engines/stark/ui/dialoginterface.h"
#include "engines/stark/ui/inventoryinterface.h"
namespace Stark {
UI::UI(Gfx::Driver *gfx, Cursor *cursor) :
_gfx(gfx),
_cursor(cursor),
_objectUnderCursor(nullptr),
_hasClicked(false),
_topMenu(nullptr),
_dialogInterface(nullptr),
_inventoryInterface(nullptr),
_selectedInventoryItem(-1),
_exitGame(false),
_fmvPlayer(nullptr),
_actionMenu(nullptr)
{
}
UI::~UI() {
delete _actionMenu;
delete _topMenu;
delete _dialogInterface;
delete _inventoryInterface;
delete _fmvPlayer;
}
void UI::init() {
_topMenu = new TopMenu();
_dialogInterface = new DialogInterface();
_inventoryInterface = new InventoryInterface(_gfx, _cursor);
_fmvPlayer = new FMVPlayer();
_actionMenu = new ActionMenu(_gfx, _cursor);
}
void UI::update(Gfx::RenderEntryArray renderEntries) {
Common::Point pos = _cursor->getMousePosition();
if (_actionMenu->isVisible() && _actionMenu->isMouseInside()) {
_actionMenu->handleMouseMove();
return;
}
// Check for inventory to avoid mouse-overs from the world poking through.
if (_inventoryInterface->isVisible() && _inventoryInterface->isMouseInside()) {
renderEntries = _inventoryInterface->getRenderEntries();
}
// Check for UI mouse overs
if (_topMenu->containsPoint(pos)) {
_cursor->setCursorType(Cursor::kActive);
_cursor->setMouseHint(_selectedInventoryItemText + _topMenu->getMouseHintAtPosition(pos));
return;
}
if (_dialogInterface->containsPoint(pos)) {
_dialogInterface->handleMouseOver(pos);
return;
}
pos -= Common::Point(0, Gfx::Driver::kTopBorderHeight);
// Check for game world mouse overs
UserInterface *ui = StarkServices::instance().userInterface;
_objectUnderCursor = ui->getItemAtPosition(pos, renderEntries);
Common::String mouseHint = ui->getMouseHintForItem(_objectUnderCursor);
if (_objectUnderCursor) {
Resources::ActionArray actionsPossible = ui->getActionsPossibleForObject(_objectUnderCursor, pos);
setCursorDependingOnActionsAvailable(actionsPossible);
} else {
// Not an object
_cursor->setCursorType(Cursor::kPassive);
}
_cursor->setMouseHint(_selectedInventoryItemText + mouseHint);
}
void UI::setCursorDependingOnActionsAvailable(Resources::ActionArray actionsAvailable) {
if (actionsAvailable.empty()) {
_cursor->setCursorType(Cursor::kPassive);
return;
}
uint32 count = 0;
Cursor::CursorType cursorType;
for (uint i = 0; i < actionsAvailable.size(); i++) {
switch (actionsAvailable[i]) {
case Resources::PATTable::kActionLook:
cursorType = Cursor::kEye;
count++;
break;
case Resources::PATTable::kActionTalk:
cursorType = Cursor::kMouth;
count++;
break;
case Resources::PATTable::kActionUse:
cursorType = Cursor::kHand;
count++;
break;
}
}
if (count == 1) {
_cursor->setCursorType(cursorType);
} else {
_cursor->setCursorType(Cursor::kActive);
}
}
void UI::handleClick() {
UserInterface *ui = StarkServices::instance().userInterface;
if (_actionMenu->isVisible()) {
if (_actionMenu->isMouseInside()) {
_actionMenu->handleClick();
} else {
_actionMenu->close();
}
} else if (_objectUnderCursor) {
Common::Point pos = _cursor->getMousePosition();
pos -= Common::Point(0, Gfx::Driver::kTopBorderHeight);
// Possibilites:
// * Click on something that doesn't take an action
// * Click on something that takes exactly 1 action.
// * Click on something that takes more than 1 action (open action menu)
// * Click in the action menu, which has 0 available actions (TODO)
if (_selectedInventoryItem != -1) {
if (!ui->itemDoActionAt(_objectUnderCursor, _selectedInventoryItem, pos)) {
warning("Could not perform action %d on %s", _selectedInventoryItem, _objectUnderCursor->getName().c_str());
}
} else {
Resources::ActionArray actions = ui->getStockActionsPossibleForObject(_objectUnderCursor, pos);
if (actions.size() == 1) {
for (uint i = 0; i < actions.size(); i++) {
if (actions[i] == Resources::PATTable::kActionLook) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionLook, pos);
break;
} else if (actions[i] == Resources::PATTable::kActionTalk) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionTalk, pos);
break;
} else if (actions[i] == Resources::PATTable::kActionUse) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionUse, pos);
break;
}
}
} else if (actions.size() > 1) {
_actionMenu->open(_objectUnderCursor, pos);
}
}
} else {
ui->walkTo(g_system->getEventManager()->getMousePos());
// // Assume all inventory objects need action menu.
// if (isInventoryObject(item)) {
// return false;
// }
// {
// if (_selectedInventoryItem != -1) {
// _inventoryInterface->update();
// _selectedInventoryItem = -1;
// _selectedInventoryItemText = "";
// }
// }
}
// Check this before handling the menu clicks, otherwise it closes again on the same event.
if (_inventoryInterface->isVisible() && !_inventoryInterface->isMouseInside()) {
_inventoryInterface->close();
}
if (_topMenu->containsPoint(_cursor->getMousePosition())) {
_topMenu->handleClick(_cursor->getMousePosition());
}
if (_dialogInterface->containsPoint(_cursor->getMousePosition())) {
_dialogInterface->handleClick(_cursor->getMousePosition());
}
_hasClicked = false;
}
void UI::notifyClick() {
_hasClicked = true;
}
void UI::notifySubtitle(const Common::String &subtitle) {
_dialogInterface->notifySubtitle(subtitle);
}
void UI::notifyDialogOptions(const Common::StringArray &options) {
_dialogInterface->notifyDialogOptions(options);
}
void UI::notifyShouldOpenInventory() {
// Make the inventory update it's contents.
_inventoryInterface->open();
}
void UI::notifyFMVRequest(const Common::String &name) {
_fmvPlayer->play(name);
}
void UI::notifySelectedInventoryItem(Resources::Item *selectedItem) {
_selectedInventoryItem = selectedItem->getIndex();
_selectedInventoryItemText = selectedItem->getName() + " -> ";
}
bool UI::isPlayingFMV() const {
return _fmvPlayer->isPlaying();
}
void UI::stopPlayingFMV() {
_fmvPlayer->stop();
}
void UI::render() {
if (_fmvPlayer->isPlaying()) {
_fmvPlayer->render();
return;
}
// Can't handle clicks before this point, since we need to have updated the mouse-over state to include the UI.
if (_hasClicked) {
handleClick();
}
// TODO: Unhardcode
if (_cursor->getMousePosition().y < 40) {
_topMenu->render();
}
_inventoryInterface->render();
_dialogInterface->render();
_actionMenu->render();
}
} // End of namespace Stark