aboutsummaryrefslogtreecommitdiff
path: root/engines/illusions/menusystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/illusions/menusystem.cpp')
-rw-r--r--engines/illusions/menusystem.cpp591
1 files changed, 591 insertions, 0 deletions
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
new file mode 100644
index 0000000000..f26a461223
--- /dev/null
+++ b/engines/illusions/menusystem.cpp
@@ -0,0 +1,591 @@
+/* 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 "illusions/menusystem.h"
+#include "illusions/illusions.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "illusions/screentext.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// MenuItem
+
+MenuItem::MenuItem(const Common::String text, BaseMenuAction *action)
+ : _text(text), _action(action) {
+}
+
+MenuItem::~MenuItem() {
+ delete _action;
+}
+
+void MenuItem::executeAction() {
+ _action->execute();
+}
+
+// BaseMenu
+
+BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE,
+ uint defaultMenuItemIndex)
+ : _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _fieldC(fieldC), _fieldE(fieldE),
+ _defaultMenuItemIndex(defaultMenuItemIndex)
+{
+}
+
+BaseMenu::~BaseMenu() {
+ for (MenuItems::iterator it = _menuItems.begin(); it != _menuItems.end(); ++it)
+ delete *it;
+}
+
+void BaseMenu::addText(const Common::String text) {
+ _text.push_back(text);
+}
+
+void BaseMenu::addMenuItem(MenuItem *menuItem) {
+ _menuItems.push_back(menuItem);
+}
+
+uint BaseMenu::getHeaderLinesCount() {
+ return _text.size();
+}
+
+const Common::String& BaseMenu::getHeaderLine(uint index) {
+ return _text[index];
+}
+
+uint BaseMenu::getMenuItemsCount() {
+ return _menuItems.size();
+}
+
+MenuItem *BaseMenu::getMenuItem(uint index) {
+ return _menuItems[index];
+}
+
+void BaseMenu::enterMenu() {
+ // Empty, implemented if neccessary by the inherited class when the menu is entered
+}
+
+// BaseMenuSystem
+
+BaseMenuSystem::BaseMenuSystem(IllusionsEngine *vm)
+ : _vm(vm), _isTimeOutEnabled(false), _menuChoiceOffset(0) {
+}
+
+BaseMenuSystem::~BaseMenuSystem() {
+}
+
+void BaseMenuSystem::playSoundEffect13() {
+ // TODO
+}
+
+void BaseMenuSystem::playSoundEffect14() {
+ // TODO
+}
+
+void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
+ if (choiceIndex > 0 && _menuChoiceOffset) {
+ *_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1];
+ debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);
+ }
+ _vm->_threads->notifyId(_menuCallerThreadId);
+ _menuCallerThreadId = 0;
+ closeMenu();
+}
+
+void BaseMenuSystem::leaveMenu() {
+ playSoundEffect13();
+ if (!_menuStack.empty())
+ leaveSubMenu();
+ else
+ closeMenu();
+}
+
+void BaseMenuSystem::enterSubMenu(BaseMenu *menu) {
+ _menuStack.push(_activeMenu);
+ activateMenu(menu);
+ _hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+ _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+ setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+ placeActor318();
+ placeActor323();
+}
+
+void BaseMenuSystem::leaveSubMenu() {
+ _activeMenu = _menuStack.pop();
+ _field54 = _activeMenu->_field2C18;
+ _menuLinesCount = _activeMenu->getHeaderLinesCount();
+ _hoveredMenuItemIndex = 1;
+ _vm->_screenText->removeText();
+ _vm->_screenText->removeText();
+ activateMenu(_activeMenu);
+ _hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+ _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+ setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+ initActor318();
+ placeActor323();
+}
+
+void BaseMenuSystem::enterSubMenuById(int menuId) {
+ BaseMenu *menu = getMenuById(menuId);
+ enterSubMenu(menu);
+}
+
+uint BaseMenuSystem::getQueryConfirmationChoiceIndex() const {
+ return _queryConfirmationChoiceIndex;
+}
+
+void BaseMenuSystem::setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex) {
+ _queryConfirmationChoiceIndex = queryConfirmationChoiceIndex;
+}
+
+void BaseMenuSystem::setMouseCursorToMenuItem(int menuItemIndex) {
+ Common::Point mousePos;
+ if (calcMenuItemMousePos(menuItemIndex, mousePos))
+ setMousePos(mousePos);
+}
+
+void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) {
+ FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
+ int charHeight = font->getCharHeight() + font->getLineIncr();
+
+ _vm->_screenText->getTextInfoPosition(rect._topLeft);
+ /* TODO
+ if (_activeMenu->_field8) {
+ rect._topLeft.y += 4;
+ rect._topLeft.x += 4;
+ }
+ */
+ rect._topLeft.y += charHeight * (menuItemIndex + _menuLinesCount - 1);
+
+ WidthHeight textInfoDimensions;
+ _vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+ rect._bottomRight.x = rect._topLeft.x + textInfoDimensions._width;
+ rect._bottomRight.y = rect._topLeft.y + charHeight;
+}
+
+bool BaseMenuSystem::calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt) {
+ if (menuItemIndex < _hoveredMenuItemIndex3 || menuItemIndex >= _hoveredMenuItemIndex3 + _menuItemCount)
+ return false;
+
+ WRect rect;
+ calcMenuItemRect(menuItemIndex - _hoveredMenuItemIndex3 + 1, rect);
+ pt.x = rect._topLeft.x;
+ pt.y = rect._topLeft.y + (rect._bottomRight.y - rect._topLeft.y) / 2;
+ return true;
+}
+
+bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex) {
+ WRect rect;
+ calcMenuItemRect(1, rect);
+
+ uint index = _hoveredMenuItemIndex3 + (pt.y - rect._topLeft.y) / (rect._bottomRight.y - rect._topLeft.y);
+
+ if (pt.y < rect._topLeft.y || pt.x < rect._topLeft.x || pt.x > rect._bottomRight.x ||
+ index > _field54 || index > _hoveredMenuItemIndex3 + _menuItemCount - 1)
+ return false;
+
+ menuItemIndex = index;
+ return true;
+}
+
+void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
+ _vm->_input->setCursorPosition(mousePos);
+ Control *mouseCursor = _vm->getObjectControl(0x40004);
+ mouseCursor->_actor->_position = mousePos;
+}
+
+void BaseMenuSystem::activateMenu(BaseMenu *menu) {
+ _activeMenu = menu;
+ // TODO Run menu enter callback if neccessary
+ _menuLinesCount = menu->getHeaderLinesCount();
+ menu->_field2C18 = menu->getMenuItemsCount();
+ _hoveredMenuItemIndex3 = 1;
+ _field54 = menu->_field2C18;
+
+ uint v2 = drawMenuText(menu);
+ if (menu->_field2C18 <= v2)
+ _menuItemCount = menu->_field2C18;
+ else
+ _menuItemCount = v2;
+
+}
+
+void BaseMenuSystem::initActor318() {
+ Control *v0 = _vm->getObjectControl(0x4013E);
+ if (!v0) {
+ WidthHeight dimensions;
+ dimensions._width = 300;
+ dimensions._height = 15;
+ _vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18);
+ v0 = _vm->getObjectControl(0x4013E);
+ v0->_flags |= 8;
+ }
+ placeActor318();
+ v0->appearActor();
+}
+
+void BaseMenuSystem::placeActor318() {
+ Control *v0 = _vm->getObjectControl(0x4013E);
+ v0->fillActor(0);
+
+ WidthHeight textInfoDimensions;
+ _vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+
+ /* TODO
+ if ( _activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8)
+ textInfoDimensions._width -= 6;
+ */
+
+ WidthHeight frameDimensions;
+ v0->getActorFrameDimensions(frameDimensions);
+
+ FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
+ int charHeight = font->getCharHeight() + font->getLineIncr();
+ if (frameDimensions._height < charHeight)
+ charHeight = frameDimensions._height;
+
+ v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight - 1), _activeMenu->_fieldE);
+
+ updateActor318();
+}
+
+void BaseMenuSystem::updateActor318() {
+ Control *v0 = _vm->getObjectControl(0x4013E);
+ WRect rect;
+ calcMenuItemRect(_hoveredMenuItemIndex2 - _hoveredMenuItemIndex3 + 1, rect);
+ v0->setActorPosition(rect._topLeft);
+}
+
+void BaseMenuSystem::hideActor318() {
+ Control *v0 = _vm->getObjectControl(0x4013E);
+ if (v0)
+ v0->disappearActor();
+}
+
+void BaseMenuSystem::initActor323() {
+ Control *v0 = _vm->getObjectControl(0x40143);
+ if (!v0) {
+ WidthHeight dimensions;
+ dimensions._width = 300;
+ dimensions._height = 180;
+ _vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17);
+ v0 = _vm->getObjectControl(0x40143);
+ v0->_flags |= 8;
+ }
+ placeActor323();
+ v0->appearActor();
+}
+
+void BaseMenuSystem::placeActor323() {
+ Control *v0 = _vm->getObjectControl(0x40143);
+ v0->fillActor(0);
+
+ Common::Point textInfoPosition;
+ WidthHeight textInfoDimensions;
+ _vm->_screenText->getTextInfoPosition(textInfoPosition);
+ _vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+
+ /* TODO
+ if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) {
+ textInfoDimensions._width -= 2;
+ textInfoDimensions._height -= 6;
+ }
+ */
+
+ v0->setActorPosition(textInfoPosition);
+ v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_fieldC);
+
+}
+
+void BaseMenuSystem::hideActor323() {
+ Control *v0 = _vm->getObjectControl(0x40143);
+ if (v0)
+ v0->disappearActor();
+}
+
+void BaseMenuSystem::openMenu(BaseMenu *menu) {
+
+ _isActive = true;
+ _menuStack.clear();
+
+ _cursorInitialVisibleFlag = initMenuCursor();
+ _savedCursorPos = _vm->_input->getCursorPosition();
+ _savedGameState = getGameState();
+ Control *cursorControl = _vm->getObjectControl(0x40004);
+ _savedCursorActorIndex = cursorControl->_actor->_actorIndex;
+ _savedCursorSequenceId = cursorControl->_actor->_sequenceId;
+
+ setMenuCursorNum(1);
+
+ setGameState(4);
+
+ activateMenu(menu);
+
+ _hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+ _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+ setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+ initActor318();
+ initActor323();
+ _vm->_input->discardAllEvents();
+}
+
+void BaseMenuSystem::closeMenu() {
+ while (!_menuStack.empty()) {
+ _vm->_screenText->removeText();
+ _menuStack.pop();
+ }
+ _vm->_screenText->removeText();
+ hideActor318();
+ hideActor323();
+ Control *mouseCursor = _vm->getObjectControl(0x40004);
+ setGameState(_savedGameState);
+ mouseCursor->_actor->_actorIndex = _savedCursorActorIndex;
+ mouseCursor->_actor->_position = _savedCursorPos;
+ setMousePos(_savedCursorPos);
+ mouseCursor->startSequenceActor(_savedCursorSequenceId, 2, 0);
+ if (_cursorInitialVisibleFlag)
+ mouseCursor->disappearActor();
+ _vm->_input->discardAllEvents();
+ _isActive = false;
+}
+
+void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
+
+ if (menuItemIndex == 0) {
+ playSoundEffect14();
+ return;
+ }
+
+ MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
+ menuItem->executeAction();
+
+}
+
+uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
+ MenuTextBuilder *menuTextBuilder = new MenuTextBuilder();
+ uint lineCount = 0;
+
+ for (uint i = 0; i < menu->getHeaderLinesCount(); ++i) {
+ menuTextBuilder->appendString(menu->getHeaderLine(i));
+ menuTextBuilder->appendNewLine();
+ }
+
+ for (uint i = _hoveredMenuItemIndex3; i <= _field54; ++i) {
+ menuTextBuilder->appendString(menu->getMenuItem(i - 1)->getText());
+ if (i + 1 <= menu->getMenuItemsCount())
+ menuTextBuilder->appendNewLine();
+ ++lineCount;
+ }
+
+ menuTextBuilder->finalize();
+
+ uint16 *text = menuTextBuilder->getText();
+
+ Common::Point textPt;
+ int16 v9 = 0;
+ if (menu->_field8)
+ v9 = 4;
+ textPt.x = v9;
+ textPt.y = v9;
+
+ uint flags = 1;
+ if (menu->_field8 != menu->_fieldA)
+ flags = 25;
+
+ WidthHeight dimensions;
+ dimensions._width = 300;
+ dimensions._height = 180;
+
+ uint16 *outTextPtr;
+ if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_field8, menu->_fieldA, 0xFF, 0xFF, 0xFF, outTextPtr)) {
+ --lineCount;
+ for ( ; *outTextPtr; ++outTextPtr) {
+ if (*outTextPtr == 13)
+ --lineCount;
+ }
+ }
+
+ delete menuTextBuilder;
+
+ return lineCount;
+}
+
+void BaseMenuSystem::update(Control *cursorControl) {
+ Common::Point mousePos = _vm->_input->getCursorPosition();
+ setMousePos(mousePos);
+
+ uint newHoveredMenuItemIndex;
+ bool resetTimeOut = false;
+
+ if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex) && newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
+ if (_hoveredMenuItemIndex == 0)
+ initActor318();
+ _hoveredMenuItemIndex = newHoveredMenuItemIndex;
+ _hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
+ setMenuCursorNum(2);
+ updateActor318();
+ resetTimeOut = true;
+ } else if (_hoveredMenuItemIndex == 0) {
+ setMenuCursorNum(1);
+ hideActor318();
+ _hoveredMenuItemIndex = 0;
+ resetTimeOut = true;
+ }
+
+ if (_vm->_input->hasNewEvents())
+ resetTimeOut = true;
+
+ if (_vm->_input->pollEvent(kEventLeftClick)) {
+ handleClick(_hoveredMenuItemIndex, mousePos);
+ } else if (_vm->_input->pollEvent(kEventAbort) && _activeMenu->_defaultMenuItemIndex) {
+ handleClick(_activeMenu->_defaultMenuItemIndex, mousePos);
+ } else if (_vm->_input->pollEvent(kEventUp)) {
+ // TODO handleUpKey();
+ } else if (_vm->_input->pollEvent(kEventDown)) {
+ // TODO handleDownKey();
+ }
+
+ updateTimeOut(resetTimeOut);
+}
+
+void BaseMenuSystem::setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex) {
+ if (duration > 0) {
+ _isTimeOutEnabled = true;
+ _isTimeOutReached = false;
+ _timeOutDuration = duration;
+ _timeOutMenuChoiceIndex = timeOutMenuChoiceIndex;
+ _timeOutStartTime = getCurrentTime();
+ _timeOutEndTime = duration + _timeOutStartTime;
+ } else {
+ _isTimeOutEnabled = false;
+ }
+}
+
+void BaseMenuSystem::setMenuCallerThreadId(uint32 menuCallerThreadId) {
+ _menuCallerThreadId = menuCallerThreadId;
+}
+
+void BaseMenuSystem::setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset) {
+ _menuChoiceOffsets = menuChoiceOffsets;
+ _menuChoiceOffset = menuChoiceOffset;
+}
+
+void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
+
+ if (!_isTimeOutEnabled)
+ return;
+
+ if (_menuStack.empty()) {
+ if (_isTimeOutReached) {
+ resetTimeOut = true;
+ _isTimeOutReached = false;
+ }
+ } else if (!_isTimeOutReached) {
+ _isTimeOutReached = true;
+ }
+
+ if (!_isTimeOutReached) {
+ if (resetTimeOut) {
+ _timeOutStartTime = getCurrentTime();
+ _timeOutEndTime = _timeOutDuration + _timeOutStartTime;
+ } else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) {
+ _isTimeOutEnabled = false;
+ selectMenuChoiceIndex(_timeOutMenuChoiceIndex);
+ // TODO *_menuChoiceOffset = *((_WORD *)&_menuCallerThreadId + _timeOutMenuChoiceIndex + 1);
+ //_vm->_threads->notifyId(_menuCallerThreadId);
+ //_menuCallerThreadId = 0;
+ //closeMenu();
+ }
+ }
+
+}
+
+// MenuTextBuilder
+
+MenuTextBuilder::MenuTextBuilder() : _pos(0) {
+}
+
+void MenuTextBuilder::appendString(const Common::String &value) {
+ for (uint i = 0; i < value.size(); ++i)
+ _text[_pos++] = value[i];
+}
+
+void MenuTextBuilder::appendNewLine() {
+ _text[_pos++] = '\r';
+}
+
+void MenuTextBuilder::finalize() {
+ _text[_pos] = '\0';
+}
+
+// BaseMenuAction
+
+BaseMenuAction::BaseMenuAction(BaseMenuSystem *menuSystem)
+ : _menuSystem(menuSystem) {
+}
+
+// MenuActionEnterMenu
+
+MenuActionEnterMenu::MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId)
+ : BaseMenuAction(menuSystem), _menuId(menuId) {
+}
+
+void MenuActionEnterMenu::execute() {
+ _menuSystem->enterSubMenuById(_menuId);
+}
+
+// MenuActionLeaveMenu
+
+MenuActionLeaveMenu::MenuActionLeaveMenu(BaseMenuSystem *menuSystem)
+ : BaseMenuAction(menuSystem) {
+}
+
+void MenuActionLeaveMenu::execute() {
+ _menuSystem->leaveMenu();
+}
+
+// MenuActionReturnChoice
+
+MenuActionReturnChoice::MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex)
+ : BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
+}
+
+void MenuActionReturnChoice::execute() {
+ _menuSystem->playSoundEffect13();
+ _menuSystem->selectMenuChoiceIndex(_choiceIndex);
+}
+
+// MenuActionEnterQueryMenu
+
+MenuActionEnterQueryMenu::MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex)
+ : BaseMenuAction(menuSystem), _menuId(menuId), _confirmationChoiceIndex(confirmationChoiceIndex) {
+}
+
+void MenuActionEnterQueryMenu::execute() {
+ _menuSystem->setQueryConfirmationChoiceIndex(_confirmationChoiceIndex);
+ _menuSystem->enterSubMenuById(_menuId);
+}
+
+} // End of namespace Illusions