aboutsummaryrefslogtreecommitdiff
path: root/engines/cine
diff options
context:
space:
mode:
authorJohannes Schickel2009-09-22 17:51:06 +0000
committerJohannes Schickel2009-09-22 17:51:06 +0000
commite591333d4b6aa60fcd58bbe0dce6d068e9258e60 (patch)
tree4ffb19ce69ae49ccb9ee1a86070a0e067db0f8a5 /engines/cine
parentdc8edb7203f0b1da1f01e058b71f7f719050ceed (diff)
downloadscummvm-rg350-e591333d4b6aa60fcd58bbe0dce6d068e9258e60.tar.gz
scummvm-rg350-e591333d4b6aa60fcd58bbe0dce6d068e9258e60.tar.bz2
scummvm-rg350-e591333d4b6aa60fcd58bbe0dce6d068e9258e60.zip
Implement menu stack to support all menu color effects, specific to the Amiga version of the game.
svn-id: r44263
Diffstat (limited to 'engines/cine')
-rw-r--r--engines/cine/gfx.cpp242
-rw-r--r--engines/cine/gfx.h73
-rw-r--r--engines/cine/various.cpp45
3 files changed, 220 insertions, 140 deletions
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<Menu *> _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<overlay>::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;