diff options
Diffstat (limited to 'engines/sword2/menu.cpp')
-rw-r--r-- | engines/sword2/menu.cpp | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/engines/sword2/menu.cpp b/engines/sword2/menu.cpp new file mode 100644 index 0000000000..07e00accb6 --- /dev/null +++ b/engines/sword2/menu.cpp @@ -0,0 +1,291 @@ +/* Copyright (C) 1994-1998 Revolution Software Ltd. + * Copyright (C) 2003-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" + +#include "sword2/sword2.h" +#include "sword2/defs.h" +#include "sword2/mouse.h" + +namespace Sword2 { + +#define MENUDEEP 40 +#define MAXMENUANIMS 8 + +void Mouse::clearIconArea(int menu, int pocket, Common::Rect *r) { + byte *buf = _vm->_screen->getScreen(); + int16 screenWide = _vm->_screen->getScreenWide(); + + r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2; + r->bottom = r->top + RDMENU_ICONDEEP; + r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING); + r->right = r->left + RDMENU_ICONWIDE; + + byte *dst = buf + r->top * screenWide + r->left; + + for (int i = 0; i < RDMENU_ICONDEEP; i++) { + memset(dst, 0, RDMENU_ICONWIDE); + dst += screenWide; + } +} + +/** + * This function should be called regularly to process the menubar system. The + * rate at which this function is called will dictate how smooth the menu + * system is. + */ + +void Mouse::processMenu() { + uint8 menu; + uint8 i, j; + uint8 frameCount; + Common::Rect r1, r2; + static int32 lastTime = 0; + + byte *buf = _vm->_screen->getScreen(); + int16 screenWide = _vm->_screen->getScreenWide(); + + if (lastTime == 0) { + lastTime = _vm->getMillis(); + frameCount = 1; + } else { + int32 delta = _vm->getMillis() - lastTime; + + if (delta > 250) { + lastTime += delta; + delta = 250; + frameCount = 1; + } else { + frameCount = (uint8) ((_iconCount + 8) * delta / 750); + lastTime += frameCount * 750 / (_iconCount + 8); + } + } + + // Note: The "almost hidden" menu state exists only so that the menu + // will be redrawn one last time before it's completely hidden. We do + // not need a corresponding "almost shown" state because the menu will + // always be redrawn while it's shown anyway. (We may want to change + // this later.) + + while (frameCount-- > 0) { + for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) { + if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_ALMOST_HIDDEN || _menuStatus[menu] == RDMENU_SHOWN) + continue; + + int target, direction, nextState; + + if (_menuStatus[menu] == RDMENU_OPENING) { + target = MAXMENUANIMS; + direction = 1; + nextState = RDMENU_SHOWN; + } else { + target = 0; + direction = -1; + nextState = RDMENU_ALMOST_HIDDEN; + } + + bool complete = true; + + // Propagate animation from the first icon... + for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) { + _pocketStatus[menu][i] = _pocketStatus[menu][i - 1]; + + if (_pocketStatus[menu][i] != target) + complete = false; + } + + if (_pocketStatus[menu][i] != target) + complete = false; + + // ...and animate the first icon + if (_pocketStatus[menu][0] != target) + _pocketStatus[menu][0] += direction; + + if (complete) + _menuStatus[menu] = nextState; + } + } + + for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) { + if (_menuStatus[menu] == RDMENU_HIDDEN) + continue; + + if (_menuStatus[menu] == RDMENU_ALMOST_HIDDEN) + _menuStatus[menu] = RDMENU_HIDDEN; + + // Draw the menu here. + int32 curx = RDMENU_ICONSTART + RDMENU_ICONWIDE / 2; + int32 cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu; + + for (i = 0; i < RDMENU_MAXPOCKETS; i++) { + if (_icons[menu][i]) { + int32 xoff, yoff; + + // Since we no longer clear the screen after + // each frame we need to clear the icon area. + + clearIconArea(menu, i, &r1); + + if (_pocketStatus[menu][i] == MAXMENUANIMS) { + xoff = (RDMENU_ICONWIDE / 2); + r2.left = curx - xoff; + r2.right = r2.left + RDMENU_ICONWIDE; + yoff = (RDMENU_ICONDEEP / 2); + r2.top = cury - yoff; + r2.bottom = r2.top + RDMENU_ICONDEEP; + } else { + xoff = (RDMENU_ICONWIDE / 2) * _pocketStatus[menu][i] / MAXMENUANIMS; + r2.left = curx - xoff; + r2.right = curx + xoff; + yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS; + r2.top = cury - yoff; + r2.bottom = cury + yoff; + } + + if (xoff != 0 && yoff != 0) { + byte *dst = buf + r2.top * screenWide + r2.left; + byte *src = _icons[menu][i]; + + if (_pocketStatus[menu][i] != MAXMENUANIMS) { + _vm->_screen->scaleImageFast( + dst, screenWide, r2.right - r2.left, r2.bottom - r2.top, + src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP); + } else { + for (j = 0; j < RDMENU_ICONDEEP; j++) { + memcpy(dst, src, RDMENU_ICONWIDE); + src += RDMENU_ICONWIDE; + dst += screenWide; + } + } + } + _vm->_screen->updateRect(&r1); + } + curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE); + } + } +} + +/** + * This function brings a specified menu into view. + * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to show + * @return RD_OK, or an error code + */ + +int32 Mouse::showMenu(uint8 menu) { + // Check for invalid menu parameter + if (menu > RDMENU_BOTTOM) + return RDERR_INVALIDMENU; + + // Check that the menu is not currently shown, or in the process of + // being shown. + if (_menuStatus[menu] == RDMENU_SHOWN || _menuStatus[menu] == RDMENU_OPENING) + return RDERR_INVALIDCOMMAND; + + _menuStatus[menu] = RDMENU_OPENING; + return RD_OK; +} + +/** + * This function hides a specified menu. + * @param menu RDMENU_TOP or RDMENU_BOTTOM depending on which menu to hide + * @return RD_OK, or an error code + */ + +int32 Mouse::hideMenu(uint8 menu) { + // Check for invalid menu parameter + if (menu > RDMENU_BOTTOM) + return RDERR_INVALIDMENU; + + // Check that the menu is not currently hidden, or in the process of + // being hidden. + if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_CLOSING) + return RDERR_INVALIDCOMMAND; + + _menuStatus[menu] = RDMENU_CLOSING; + return RD_OK; +} + +/** + * This function hides both menus immediately. + */ + +void Mouse::closeMenuImmediately() { + Common::Rect r; + int i; + + _menuStatus[RDMENU_TOP] = RDMENU_HIDDEN; + _menuStatus[RDMENU_BOTTOM] = RDMENU_HIDDEN; + + for (i = 0; i < RDMENU_MAXPOCKETS; i++) { + if (_icons[RDMENU_TOP][i]) { + clearIconArea(RDMENU_TOP, i, &r); + _vm->_screen->updateRect(&r); + } + if (_icons[RDMENU_BOTTOM][i]) { + clearIconArea(RDMENU_BOTTOM, i, &r); + _vm->_screen->updateRect(&r); + } + } + + memset(_pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS); +} + +/** + * This function sets a menubar icon. + * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to change + * @param pocket the menu pocket to change + * @param icon icon data, or NULL to clear the icon + * @return RD_OK, or an error code + */ + +int32 Mouse::setMenuIcon(uint8 menu, uint8 pocket, byte *icon) { + Common::Rect r; + + // Check for invalid menu parameter. + if (menu > RDMENU_BOTTOM) + return RDERR_INVALIDMENU; + + // Check for invalid pocket parameter + if (pocket >= RDMENU_MAXPOCKETS) + return RDERR_INVALIDPOCKET; + + // If there is an icon in the requested menu/pocket, clear it out. + if (_icons[menu][pocket]) { + _iconCount--; + free(_icons[menu][pocket]); + _icons[menu][pocket] = NULL; + clearIconArea(menu, pocket, &r); + _vm->_screen->updateRect(&r); + } + + // Only put the icon in the pocket if it is not NULL + if (icon != NULL) { + _iconCount++; + _icons[menu][pocket] = (byte *)malloc(RDMENU_ICONWIDE * RDMENU_ICONDEEP); + if (_icons[menu][pocket] == NULL) + return RDERR_OUTOFMEMORY; + memcpy(_icons[menu][pocket], icon, RDMENU_ICONWIDE * RDMENU_ICONDEEP); + } + + return RD_OK; +} + +} // End of namespace Sword2 |