From e591333d4b6aa60fcd58bbe0dce6d068e9258e60 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 22 Sep 2009 17:51:06 +0000 Subject: Implement menu stack to support all menu color effects, specific to the Amiga version of the game. svn-id: r44263 --- engines/cine/gfx.cpp | 242 +++++++++++++++++++++++++---------------------- engines/cine/gfx.h | 73 ++++++++++++-- engines/cine/various.cpp | 45 +++++---- 3 files changed, 220 insertions(+), 140 deletions(-) (limited to 'engines/cine') diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index 61c60bfd17..142a1f3a58 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -92,7 +92,7 @@ static const byte cursorPalette[] = { */ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""), _cmdY(0), _messageBg(0), _backBuffer(new byte[_screenSize]), - _screenBackUp(0), _activePal(), _changePal(0), _showCollisionPage(false) { + _activePal(), _changePal(0), _showCollisionPage(false) { assert(_backBuffer); @@ -105,7 +105,6 @@ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""), FWRenderer::~FWRenderer() { delete[] _background; delete[] _backBuffer; - delete[] _screenBackUp; } bool FWRenderer::initialize() { @@ -273,7 +272,7 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color ty += 9; if (color >= 0) { if (isAmiga) - drawTransparentBox(x, ty, width, 4); + drawTransparentBox(x, ty, width, 9); else drawPlainBox(x, ty, width, 9, color); } @@ -367,17 +366,13 @@ void FWRenderer::drawTransparentBox(int x, int y, int width, int height) { boxRect.clip(screenRect); byte *dest = _backBuffer + boxRect.top * 320 + boxRect.left; - const byte *src = _screenBackUp + boxRect.top * 320 + boxRect.left; const int lineAdd = 320 - boxRect.width(); for (int i = 0; i < boxRect.height(); ++i) { - for (int j = 0; j < boxRect.width(); ++j) { - if (*src < 16) - *dest++ = *src++ + 16; - else - *dest++ = *src++; + for (int j = 0; j < boxRect.width(); ++j, ++dest) { + if (*dest < 16) + *dest += 16; } dest += lineAdd; - src += lineAdd; } } @@ -564,6 +559,10 @@ void FWRenderer::drawFrame() { refreshPalette(); } + const int menus = _menuStack.size(); + for (int i = 0; i < menus; ++i) + _menuStack[i]->drawMenu(*this, (i == menus - 1)); + blit(); } @@ -793,87 +792,117 @@ void FWRenderer::transformPalette(int first, int last, int r, int g, int b) { refreshPalette(); } -void FWRenderer::prepareMenu() { - if (g_cine->getPlatform() != Common::kPlatformAmiga) - return; +/*! \brief Fade to black + * \bug Operation Stealth sometimes seems to fade to black using + * transformPalette resulting in double fadeout + */ +void FWRenderer::fadeToBlack() { + assert(_activePal.isValid() && !_activePal.empty()); - if (!_screenBackUp) { - _screenBackUp = new uint8[_screenSize]; - assert(_screenBackUp); + for (int i = 0; i < 8; i++) { + // Fade out the whole palette by 1/7th + // (Operation Stealth used 36 / 252, which is 1 / 7. Future Wars used 1 / 7 directly). + _activePal.saturatedAddNormalizedGray(_activePal, 0, _activePal.colorCount() - 1, -1, 7); + + refreshPalette(); + g_system->updateScreen(); + g_system->delayMillis(50); } - memcpy(_screenBackUp, _backBuffer, _screenSize); } -void FWRenderer::discardMenu() { - delete[] _screenBackUp; - _screenBackUp = 0; +// Menu implementation + +void FWRenderer::pushMenu(Menu *menu) { + _menuStack.push(menu); +} + +Menu *FWRenderer::popMenu() { + if (_menuStack.empty()) + return 0; + + Menu *menu = _menuStack.top(); + _menuStack.pop(); + return menu; } -/*! \brief Draw menu box, one item per line with possible highlight - * \param items Menu items - * \param height Item count - * \param x Top left menu corner coordinate - * \param y Top left menu corner coordinate - * \param width Menu box width - * \param selected Index of highlighted item (no highlight if less than 0) - */ -void FWRenderer::drawMenu(const CommandeType *items, unsigned int height, int x, int y, int width, int selected) { - int tx, ty, th = height * 9 + 10; - unsigned int i, j; +void FWRenderer::clearMenuStack() { + Menu *menu = 0; + while ((menu = popMenu()) != 0) + delete menu; +} - if (x + width > 319) { - x = 319 - width; - } +SelectionMenu::SelectionMenu(Common::Point p, int width, Common::StringList elements) + : Menu(kSelectionMenu), _pos(p), _width(width), _elements(elements), _selection(-1) { +} - if (y + th > 199) { - y = 199 - th; +void SelectionMenu::setSelection(int selection) { + if (selection >= getElementCount() || selection < -1) { + warning("Invalid selection %d", selection); + selection = -1; } + _selection = selection; +} + +void SelectionMenu::drawMenu(FWRenderer &r, bool top) { + const int height = getElementCount() * 9 + 10; + int x = _pos.x; + int y = _pos.y; + + if (x + _width > 319) + x = 319 - _width; + + if (y + height > 199) + y = 199 - height; + const bool isAmiga = (g_cine->getPlatform() == Common::kPlatformAmiga); - if (isAmiga) - drawTransparentBox(x, y, width, 4); - else - drawPlainBox(x, y, width, 4, _messageBg); - - ty = y + 4; - - for (i = 0; i < height; i++, ty += 9) { - if (isAmiga) { - // The original Amiga version is using a different highlight color here, - // but with our current code it is not possible to change the text color - // thus we can not use it, since otherwise the text wouldn't be visible - // anymore. - if ((int)i == selected) - drawPlainBox(x, ty, width, 9, 0/*2*/); - else - drawTransparentBox(x, ty, width, 9); - } else { - drawPlainBox(x, ty, width, 9, (int)i == selected ? 0 : _messageBg); - } - tx = x + 4; + if (isAmiga) { + r.drawTransparentBox(x, y, _width, height); + r.drawDoubleBorder(x, y, _width, height, 18); + } else { + r.drawPlainBox(x, y, _width, height, r._messageBg); + r.drawDoubleBorder(x, y, _width, height, 2); + } + + int lineY = y + 4; + int charX; + + const int elemCount = getElementCount(); + for (int i = 0; i < elemCount; ++i, lineY += 9) { + charX = x + 4; - for (j = 0; items[i][j]; j++) { - tx = drawChar(items[i][j], tx, ty); + if (i == _selection) { + if (isAmiga) { + // The original Amiga version is using a different highlight color here, + // but with our current code it is not possible to change the text color, + // thus we can not use the Amiga's color, since otherwise the text + // wouldn't be visible anymore. + r.drawPlainBox(charX, lineY, _width - 8, FONT_HEIGHT, top ? r._messageBg/*2*/ : 18); + } else { + r.drawPlainBox(charX, lineY, _width - 8, 9, r._messageBg); + } } + + const int size = _elements[i].size(); + for (int j = 0; j < size; ++j) + charX = r.drawChar(_elements[i][j], charX, lineY); } +} - if (isAmiga) - drawTransparentBox(x, ty, width, 4); - else - drawPlainBox(x, ty, width, 4, _messageBg); - drawDoubleBorder(x, y, width, ty - y + 4, isAmiga ? 18 : 2); +TextInputMenu::TextInputMenu(Common::Point p, int width, const char *info) + : Menu(kTextInputMenu), _pos(p), _width(width), _info(info), _input(), _cursor(0) { } -/*! \brief Draw text input box - * \param info Input box message - * \param input Text entered in the input area - * \param cursor Cursor position in the input area - * \param x Top left input box corner coordinate - * \param y Top left input box corner coordinate - * \param width Input box width - */ -void FWRenderer::drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width) { +void TextInputMenu::setInput(const char *input, int cursor) { + _input = input; + _cursor = cursor; +} + +void TextInputMenu::drawMenu(FWRenderer &r, bool top) { + const int x = _pos.x; + const int y = _pos.y; + int i, tx, ty, tw; int line = 0, words = 0, cw = 0; int space = 0, extraSpace = 0; @@ -881,20 +910,22 @@ void FWRenderer::drawInputBox(const char *info, const char *input, int cursor, i const bool isAmiga = (g_cine->getPlatform() == Common::kPlatformAmiga); if (isAmiga) - drawTransparentBox(x, y, width, 4); + r.drawTransparentBox(x, y, _width, 4); else - drawPlainBox(x, y, width, 4, _messageBg); + r.drawPlainBox(x, y, _width, 4, r._messageBg); tx = x + 4; - ty = info[0] ? y - 5 : y + 4; - tw = width - 8; + ty = _info[0] ? y - 5 : y + 4; + tw = _width - 8; + + const int infoSize = _info.size(); // input box info message - for (i = 0; info[i]; i++, line--) { + for (i = 0; i < infoSize; i++, line--) { // fit line of text if (!line) { - line = fitLine(info + i, tw, words, cw); + line = fitLine(_info.c_str() + i, tw, words, cw); - if ( info[i + line] != '\0' && words) { + if (i + line < infoSize && words) { space = (tw - cw) / words; extraSpace = (tw - cw) % words; } else { @@ -904,71 +935,56 @@ void FWRenderer::drawInputBox(const char *info, const char *input, int cursor, i ty += 9; if (isAmiga) - drawTransparentBox(x, ty, width, 9); + r.drawTransparentBox(x, ty, _width, 9); else - drawPlainBox(x, ty, width, 9, _messageBg); + r.drawPlainBox(x, ty, _width, 9, r._messageBg); tx = x + 4; } // draw characters - if (info[i] == ' ') { + if (_info[i] == ' ') { tx += space + extraSpace; if (extraSpace) { extraSpace = 0; } } else { - tx = drawChar(info[i], tx, ty); + tx = r.drawChar(_info[i], tx, ty); } } // input area background ty += 9; if (isAmiga) - drawTransparentBox(x, ty, width, 9); + r.drawTransparentBox(x, ty, _width, 9); else - drawPlainBox(x, ty, width, 9, _messageBg); - drawPlainBox(x + 16, ty - 1, width - 32, 9, 0); + r.drawPlainBox(x, ty, _width, 9, r._messageBg); + r.drawPlainBox(x + 16, ty - 1, _width - 32, 9, 0); tx = x + 20; // text in input area - for (i = 0; input[i]; i++) { - tx = drawChar(input[i], tx, ty); + const int inputSize = _input.size(); + for (i = 0; i < inputSize; i++) { + tx = r.drawChar(_input[i], tx, ty); - if (cursor == i + 2) { - drawLine(tx, ty - 1, 1, 9, 2); + if (_cursor == i + 2) { + r.drawLine(tx, ty - 1, 1, 9, 2); } } - if (!input[0] || cursor == 1) { - drawLine(x + 20, ty - 1, 1, 9, 2); + if (_input.empty() || _cursor == 1) { + r.drawLine(x + 20, ty - 1, 1, 9, 2); } ty += 9; if (isAmiga) - drawTransparentBox(x, ty, width, 4); + r.drawTransparentBox(x, ty, _width, 4); else - drawPlainBox(x, ty, width, 4, _messageBg); - drawDoubleBorder(x, y, width, ty - y + 4, isAmiga ? 18 : 2); + r.drawPlainBox(x, ty, _width, 4, r._messageBg); + r.drawDoubleBorder(x, y, _width, ty - y + 4, isAmiga ? 18 : 2); } -/*! \brief Fade to black - * \bug Operation Stealth sometimes seems to fade to black using - * transformPalette resulting in double fadeout - */ -void FWRenderer::fadeToBlack() { - assert(_activePal.isValid() && !_activePal.empty()); - - for (int i = 0; i < 8; i++) { - // Fade out the whole palette by 1/7th - // (Operation Stealth used 36 / 252, which is 1 / 7. Future Wars used 1 / 7 directly). - _activePal.saturatedAddNormalizedGray(_activePal, 0, _activePal.colorCount() - 1, -1, 7); - - refreshPalette(); - g_system->updateScreen(); - g_system->delayMillis(50); - } -} +// ------------------- /*! \brief Initialize Operation Stealth renderer */ diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 54542f31cd..528b8c8588 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -27,6 +27,8 @@ #define CINE_GFX_H #include "common/noncopyable.h" +#include "common/rect.h" +#include "common/stack.h" #include "cine/object.h" namespace Cine { @@ -61,13 +63,66 @@ struct palBg { } }; +class FWRenderer; + +class Menu { +public: + enum Type { + kSelectionMenu, + kTextInputMenu + }; + + Menu(Type t) : _type(t) {} + virtual ~Menu() {} + + Type getType() const { return _type; } + + virtual void drawMenu(FWRenderer &r, bool top) = 0; +private: + const Type _type; +}; + +class SelectionMenu : public Menu { +public: + SelectionMenu(Common::Point p, int width, Common::StringList elements); + + int getElementCount() const { return _elements.size(); } + + void setSelection(int selection); + + void drawMenu(FWRenderer &r, bool top); +private: + const Common::Point _pos; + const int _width; + const Common::StringList _elements; + + int _selection; +}; + +class TextInputMenu : public Menu { +public: + TextInputMenu(Common::Point p, int width, const char *info); + + void setInput(const char *input, int cursor); + + void drawMenu(FWRenderer &r, bool top); +private: + const Common::Point _pos; + const int _width; + const Common::String _info; + + Common::String _input; + int _cursor; +}; + /*! \brief Future Wars renderer * - * Screen backbuffer is not cleared between frames, you can draw menus etc. - * without calling drawFrame() all the time + * Screen backbuffer is not cleared between frames. */ class FWRenderer : public Common::NonCopyable { -protected: + // TODO: Consider getting rid of this + friend class SelectionMenu; + friend class TextInputMenu; private: byte *_background; ///< Current background char _bgName[13]; ///< Background filename @@ -80,9 +135,9 @@ protected: static const int _screenHeight = 200; ///< Screen height byte *_backBuffer; ///< Screen backbuffer - byte *_screenBackUp; ///< Screen backbuffer's backup for the transparent menu code in the Amiga version Cine::Palette _backupPal; ///< The backup color palette Cine::Palette _activePal; ///< The active color palette + Common::Stack _menuStack; ///< All displayed menus int _changePal; ///< Load active palette to video backend on next frame bool _showCollisionPage; ///< Should we show the collision page instead of the back buffer? Used for debugging. @@ -104,6 +159,7 @@ protected: virtual void renderOverlay(const Common::List::iterator &it); void drawOverlays(); + void blit(); public: uint16 _messageBg; ///< Message box background color uint16 _cmdY; ///< Player command string position on screen @@ -119,7 +175,6 @@ public: virtual void clear(); void drawFrame(); - void blit(); void setCommand(Common::String cmd); virtual void incrustMask(const ObjectStruct &obj, uint8 color = 0); @@ -144,11 +199,9 @@ public: virtual void rotatePalette(int a, int b, int c); virtual void transformPalette(int first, int last, int r, int g, int b); - void prepareMenu(); - void discardMenu(); - - void drawMenu(const CommandeType *items, unsigned int height, int x, int y, int width, int selected); - void drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width); + void pushMenu(Menu *menu); + Menu *popMenu(); + void clearMenuStack(); virtual void fadeToBlack(); void showCollisionPage(bool state); diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 2fc48f20f0..78a347b321 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -492,10 +492,15 @@ void processInventory(int16 x, int16 y) { if (!listSize) return; - renderer->prepareMenu(); - renderer->drawMenu(objectListCommand, listSize, x, y, menuWidth, -1); - renderer->blit(); - renderer->discardMenu(); + Common::StringList list; + for (int i = 0; i < listSize; ++i) + list.push_back(objectListCommand[i]); + SelectionMenu *menu = new SelectionMenu(Common::Point(x, y), menuWidth, list); + renderer->pushMenu(menu); + renderer->drawFrame(); + renderer->popMenu(); + delete menu; + menu = 0; do { manageEvents(); @@ -676,6 +681,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, int16 var_14; int16 currentSelection, oldSelection; int16 var_4; + SelectionMenu *menu; if (disableSystemMenu) return -1; @@ -690,9 +696,12 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, Y = 199 - paramY; } - renderer->prepareMenu(); - renderer->drawMenu(commandList, height, X, Y, width, -1); - renderer->blit(); + Common::StringList list; + for (uint16 i = 0; i < height; ++i) + list.push_back(commandList[i]); + menu = new SelectionMenu(Common::Point(X, Y), width, list); + renderer->pushMenu(menu); + renderer->drawFrame(); do { manageEvents(); @@ -705,8 +714,8 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, di = currentSelection * 9 + Y + 4; - renderer->drawMenu(commandList, height, X, Y, width, currentSelection); - renderer->blit(); + menu->setSelection(currentSelection); + renderer->drawFrame(); manageEvents(); getMouseData(mouseUpdateStatus, &button, (uint16 *)&mouseX, (uint16 *)&mouseY); @@ -759,8 +768,8 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, di = currentSelection * 9 + Y + 4; - renderer->drawMenu(commandList, height, X, Y, width, currentSelection); - renderer->blit(); + menu->setSelection(currentSelection); + renderer->drawFrame(); // if (needMouseSave) { // gfxRedrawMouseCursor(); @@ -769,8 +778,6 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, } while (!var_A && !g_cine->shouldQuit()); - renderer->discardMenu(); - assert(!needMouseSave); var_4 = button; @@ -1096,6 +1103,8 @@ uint16 executePlayerInput(void) { } } + renderer->clearMenuStack(); + // Update Operation Stealth specific global variables. // This fixes swimming at the bottom of the ocean after // having been thrown into it with the girl. @@ -1607,12 +1616,13 @@ bool makeTextEntryMenu(const char *messagePtr, char *inputString, int stringMaxL int inputLength = strlen(inputString); int inputPos = inputLength + 1; - renderer->prepareMenu(); + TextInputMenu *inputBox = new TextInputMenu(Common::Point(x - 16, y), width + 32, messagePtr); + renderer->pushMenu(inputBox); while (!quit) { if (redraw) { - renderer->drawInputBox(messagePtr, inputString, inputPos, x - 16, y, width + 32); - renderer->blit(); + inputBox->setInput(inputString, inputPos); + renderer->drawFrame(); redraw = false; } @@ -1691,7 +1701,8 @@ bool makeTextEntryMenu(const char *messagePtr, char *inputString, int stringMaxL } } - renderer->discardMenu(); + renderer->popMenu(); + delete inputBox; if (quit == 2) return false; -- cgit v1.2.3