aboutsummaryrefslogtreecommitdiff
path: root/engines/supernova/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/supernova/screen.cpp')
-rw-r--r--engines/supernova/screen.cpp636
1 files changed, 636 insertions, 0 deletions
diff --git a/engines/supernova/screen.cpp b/engines/supernova/screen.cpp
new file mode 100644
index 0000000000..2c441acf7b
--- /dev/null
+++ b/engines/supernova/screen.cpp
@@ -0,0 +1,636 @@
+/* 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 "common/str.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+#include "supernova/imageid.h"
+#include "supernova/resman.h"
+#include "supernova/state.h"
+#include "supernova/screen.h"
+#include "supernova/supernova.h"
+
+#include "supernova/screenstatic.cpp"
+
+namespace Supernova {
+
+ScreenBuffer::ScreenBuffer()
+ : _x(0)
+ , _y(0)
+ , _width(0)
+ , _height(0)
+ , _pixels(nullptr) {
+}
+
+ScreenBufferStack::ScreenBufferStack()
+ : _last(_buffer) {
+}
+
+void ScreenBufferStack::push(int x, int y, int width, int height) {
+ if (_last == ARRAYEND(_buffer))
+ return;
+
+ Graphics::Surface *screenSurface = g_system->lockScreen();
+
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+
+ if (x + width > screenSurface->w)
+ width = screenSurface->w - x;
+
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+
+ if (y + height > screenSurface->h)
+ height = screenSurface->h - y;
+
+ _last->_pixels = new byte[width * height];
+ byte *pixels = _last->_pixels;
+ const byte *screen = static_cast<const byte *>(screenSurface->getBasePtr(x, y));
+ for (int i = 0; i < height; ++i) {
+ Common::copy(screen, screen + width, pixels);
+ screen += screenSurface->pitch;
+ pixels += width;
+ }
+ g_system->unlockScreen();
+
+ _last->_x = x;
+ _last->_y = y;
+ _last->_width = width;
+ _last->_height = height;
+
+ ++_last;
+}
+
+void ScreenBufferStack::restore() {
+ if (_last == _buffer)
+ return;
+
+ --_last;
+ g_system->lockScreen()->copyRectToSurface(_last->_pixels, _last->_width, _last->_x,
+ _last->_y, _last->_width, _last->_height);
+ g_system->unlockScreen();
+
+ delete[] _last->_pixels;
+}
+
+Marquee::Marquee(Screen *screen, MarqueeId id, const char *text)
+ : _text(text)
+ , _textBegin(text)
+ , _delay(0)
+ , _color(kColorLightBlue)
+ , _loop(false)
+ , _screen(screen) {
+ if (id == kMarqueeIntro) {
+ _y = 191;
+ _loop = true;
+ } else if (id == kMarqueeOutro) {
+ _y = 1;
+ }
+
+ _textWidth = Screen::textWidth(_text);
+ _x = kScreenWidth / 2 - _textWidth / 2;
+ _screen->_textCursorX = _x;
+ _screen->_textCursorY = _y;
+ _screen->_textColor = _color;
+}
+
+void Marquee::clearText() {
+ _screen->renderBox(_x, _y - 1, _textWidth + 1, 9, kColorBlack);
+}
+
+void Marquee::renderCharacter() {
+ if (_delay != 0) {
+ _delay--;
+ return;
+ }
+
+ switch (*_text) {
+ case '\233':
+ if (_loop) {
+ _loop = false;
+ _text = _textBegin;
+ clearText();
+ _textWidth = Screen::textWidth(_text);
+ _x = kScreenWidth / 2 - _textWidth / 2;
+ _screen->_textCursorX = _x;
+ }
+ break;
+ case '\0':
+ clearText();
+ _text++;
+ _textWidth = Screen::textWidth(_text);
+ _x = kScreenWidth / 2 - _textWidth / 2;
+ _screen->_textCursorX = _x;
+ _color = kColorLightBlue;
+ _screen->_textColor = _color;
+ break;
+ case '^':
+ _color = kColorLightYellow;
+ _screen->_textColor = _color;
+ _text++;
+ break;
+ case '#':
+ _delay = 50;
+ _text++;
+ break;
+ default:
+ _screen->renderText((uint16)*_text++);
+ _delay = 1;
+ break;
+ }
+}
+
+Screen::Screen(SupernovaEngine *vm, GameManager *gm, ResourceManager *resMan)
+ : _vm(vm)
+ , _gm(gm)
+ , _resMan(resMan)
+ , _currentImage(nullptr)
+ , _viewportBrightness(255)
+ , _guiBrightness(255)
+ , _screenWidth(320)
+ , _screenHeight(200)
+ , _textColor(kColorBlack)
+ , _textCursorX(0)
+ , _textCursorY(0)
+ , _messageShown(false) {
+
+ CursorMan.replaceCursor(_resMan->getImage(ResourceManager::kCursorNormal),
+ 16, 16, 0, 0, kColorCursorTransparent);
+ CursorMan.replaceCursorPalette(initVGAPalette, 0, 16);
+ CursorMan.showMouse(true);
+}
+
+int Screen::getGuiBrightness() const {
+ return _guiBrightness;
+}
+
+void Screen::setViewportBrightness(int brightness) {
+ _viewportBrightness = brightness;
+}
+
+int Screen::getViewportBrightness() const {
+ return _viewportBrightness;
+}
+
+void Screen::setGuiBrightness(int brightness) {
+ _guiBrightness = brightness;
+}
+
+const MSNImage *Screen::getCurrentImage() const {
+ return _currentImage;
+}
+
+bool Screen::isMessageShown() const {
+ return _messageShown;
+}
+
+Common::Point Screen::getTextCursorPos() {
+ return Common::Point(_textCursorX, _textCursorY);
+}
+
+void Screen::setTextCursorPos(int x, int y) {
+ _textCursorX = x;
+ _textCursorY = y;
+}
+
+byte Screen::getTextCursorColor() {
+ return _textColor;
+}
+
+void Screen::setTextCursorColor(byte color) {
+ _textColor = color;
+}
+
+void Screen::renderMessage(StringId stringId, MessagePosition position,
+ Common::String var1, Common::String var2) {
+ Common::String text = _vm->getGameString(stringId);
+
+ if (!var1.empty()) {
+ if (!var2.empty())
+ text = Common::String::format(text.c_str(), var1.c_str(), var2.c_str());
+ else
+ text = Common::String::format(text.c_str(), var1.c_str());
+ }
+
+ renderMessage(text, position);
+}
+
+void Screen::renderMessage(const Common::String &text, MessagePosition position) {
+ if (!text.empty())
+ renderMessage(text.c_str(), position);
+}
+
+void Screen::renderText(const uint16 character) {
+ char text[2];
+ text[0] = character & 0xFF;
+ text[1] = 0;
+ renderText(text, _textCursorX, _textCursorY, _textColor);
+}
+
+void Screen::renderText(const char *text) {
+ renderText(text, _textCursorX, _textCursorY, _textColor);
+}
+
+void Screen::renderText(StringId stringId) {
+ renderText(_vm->getGameString(stringId));
+}
+
+void Screen::renderText(const Common::String &text) {
+ if (!text.empty())
+ renderText(text.c_str());
+}
+
+void Screen::renderText(const GuiElement &guiElement) {
+ renderText(guiElement.getText(), guiElement.getTextPos().x,
+ guiElement.getTextPos().y, guiElement.getTextColor());
+}
+
+void Screen::renderText(const uint16 character, int x, int y, byte color) {
+ char text[2];
+ text[0] = character & 0xFF;
+ text[1] = 0;
+ renderText(text, x, y, color);
+}
+
+void Screen::renderText(const char *text, int x, int y, byte color) {
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+ byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y));
+ const byte *basePtr = cursor;
+
+ byte c;
+ while ((c = *text++) != '\0') {
+ if (c < 32) {
+ continue;
+ } else if (c == 225) {
+ c = 128;
+ }
+
+ for (uint i = 0; i < 5; ++i) {
+ if (font[c - 32][i] == 0xff) {
+ break;
+ }
+
+ byte *ascentLine = cursor;
+ for (byte j = font[c - 32][i]; j != 0; j >>= 1) {
+ if (j & 1) {
+ *cursor = color;
+ }
+ cursor += kScreenWidth;
+ }
+ cursor = ++ascentLine;
+ }
+ ++cursor;
+ }
+ _vm->_system->unlockScreen();
+
+ uint numChars = cursor - basePtr;
+ uint absPosition = y * kScreenWidth + x + numChars;
+ _textCursorX = absPosition % kScreenWidth;
+ _textCursorY = absPosition / kScreenWidth;
+ _textColor = color;
+}
+
+void Screen::renderText(const Common::String &text, int x, int y, byte color) {
+ if (!text.empty())
+ renderText(text.c_str(), x, y, color);
+}
+
+void Screen::renderText(StringId stringId, int x, int y, byte color) {
+ renderText(_vm->getGameString(stringId), x, y, color);
+}
+
+void Screen::renderImageSection(const MSNImage *image, int section) {
+ // Note: inverting means we are removing the section. So we should get the rect for that
+ // section but draw the background (section 0) instead.
+ bool invert = false;
+ if (section > 128) {
+ section -= 128;
+ invert = true;
+ }
+ if (section > image->_numSections - 1)
+ return;
+
+ Common::Rect sectionRect(image->_section[section].x1,
+ image->_section[section].y1,
+ image->_section[section].x2 + 1,
+ image->_section[section].y2 + 1);
+ if (image->_filenumber == 1 || image->_filenumber == 2) {
+ sectionRect.setWidth(640);
+ sectionRect.setHeight(480);
+ if (_screenWidth != 640) {
+ _screenWidth = 640;
+ _screenHeight = 480;
+ initGraphics(_screenWidth, _screenHeight);
+ }
+ } else {
+ if (_screenWidth != 320) {
+ _screenWidth = 320;
+ _screenHeight = 200;
+ initGraphics(_screenWidth, _screenHeight);
+ }
+ }
+
+ uint offset = 0;
+ int pitch = sectionRect.width();
+ if (invert) {
+ pitch = image->_pitch;
+ offset = image->_section[section].y1 * pitch +
+ image->_section[section].x1;
+ section = 0;
+ }
+
+ void *pixels = image->_sectionSurfaces[section]->getPixels();
+ _vm->_system->copyRectToScreen(static_cast<const byte *>(pixels) + offset,
+ pitch, sectionRect.left, sectionRect.top,
+ sectionRect.width(), sectionRect.height());
+}
+
+void Screen::renderImage(ImageId id, bool removeImage) {
+ ImageInfo info = imageInfo[id];
+ const MSNImage *image = _resMan->getImage(info.filenumber);
+
+ if (_currentImage != image) {
+ _currentImage = image;
+ _vm->_system->getPaletteManager()->setPalette(image->getPalette(), 16, 239);
+ }
+
+ do {
+ if (removeImage)
+ renderImageSection(image, info.section + 128);
+ else
+ renderImageSection(image, info.section);
+
+ info.section = image->_section[info.section].next;
+ } while (info.section != 0);
+}
+
+void Screen::renderImage(int section) {
+ if (!_currentImage)
+ return;
+
+ bool sectionVisible = true;
+
+ if (section > 128) {
+ sectionVisible = false;
+ section -= 128;
+ }
+
+ _gm->_currentRoom->setSectionVisible(section, sectionVisible);
+
+ do {
+ if (sectionVisible)
+ renderImageSection(_currentImage, section);
+ else
+ renderImageSection(_currentImage, section + 128);
+ section = _currentImage->_section[section].next;
+ } while (section != 0);
+}
+
+bool Screen::setCurrentImage(int filenumber) {
+ _currentImage = _resMan->getImage(filenumber);
+ _vm->_system->getPaletteManager()->setPalette(_currentImage->getPalette(), 16, 239);
+ paletteBrightness();
+
+ return true;
+}
+
+void Screen::saveScreen(int x, int y, int width, int height) {
+ _screenBuffer.push(x, y, width, height);
+}
+
+void Screen::saveScreen(const GuiElement &guiElement) {
+ saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height());
+}
+
+void Screen::restoreScreen() {
+ _screenBuffer.restore();
+}
+
+void Screen::renderRoom(Room &room) {
+ if (room.getId() == INTRO)
+ return;
+
+ if (setCurrentImage(room.getFileNumber())) {
+ for (int i = 0; i < _currentImage->_numSections; ++i) {
+ int section = i;
+ if (room.isSectionVisible(section)) {
+ do {
+ renderImageSection(_currentImage, section);
+ section = _currentImage->_section[section].next;
+ } while (section != 0);
+ }
+ }
+ }
+}
+
+int Screen::textWidth(const uint16 key) {
+ char text[2];
+ text[0] = key & 0xFF;
+ text[1] = 0;
+ return textWidth(text);
+}
+
+int Screen::textWidth(const char *text) {
+ int charWidth = 0;
+ while (*text != '\0') {
+ byte c = *text++;
+ if (c < 32) {
+ continue;
+ } else if (c == 225) {
+ c = 35;
+ }
+
+ for (uint i = 0; i < 5; ++i) {
+ if (font[c - 32][i] == 0xff) {
+ break;
+ }
+ ++charWidth;
+ }
+ ++charWidth;
+ }
+
+ return charWidth;
+}
+
+int Screen::textWidth(const Common::String &text) {
+ return Screen::textWidth(text.c_str());
+}
+
+void Screen::renderMessage(const char *text, MessagePosition position) {
+ Common::String t(text);
+ char *row[20];
+ Common::String::iterator p = t.begin();
+ uint numRows = 0;
+ int rowWidthMax = 0;
+ int x = 0;
+ int y = 0;
+ byte textColor = 0;
+
+ while (*p != '\0') {
+ row[numRows] = p;
+ ++numRows;
+ while ((*p != '\0') && (*p != '|')) {
+ ++p;
+ }
+ if (*p == '|') {
+ *p = '\0';
+ ++p;
+ }
+ }
+ for (uint i = 0; i < numRows; ++i) {
+ int rowWidth = textWidth(row[i]);
+ if (rowWidth > rowWidthMax)
+ rowWidthMax = rowWidth;
+ }
+
+ switch (position) {
+ case kMessageNormal:
+ x = 160 - rowWidthMax / 2;
+ textColor = kColorWhite99;
+ break;
+ case kMessageTop:
+ x = 160 - rowWidthMax / 2;
+ textColor = kColorLightYellow;
+ break;
+ case kMessageCenter:
+ x = 160 - rowWidthMax / 2;
+ textColor = kColorLightRed;
+ break;
+ case kMessageLeft:
+ x = 3;
+ textColor = kColorLightYellow;
+ break;
+ case kMessageRight:
+ x = 317 - rowWidthMax;
+ textColor = kColorLightGreen;
+ break;
+ }
+
+ if (position == kMessageNormal) {
+ y = 70 - ((numRows * 9) / 2);
+ } else if (position == kMessageTop) {
+ y = 5;
+ } else {
+ y = 142;
+ }
+
+ int message_columns = x - 3;
+ int message_rows = y - 3;
+ int message_width = rowWidthMax + 6;
+ int message_height = numRows * 9 + 5;
+ saveScreen(message_columns, message_rows, message_width, message_height);
+ renderBox(message_columns, message_rows, message_width, message_height, kColorWhite35);
+ for (uint i = 0; i < numRows; ++i) {
+ renderText(row[i], x, y, textColor);
+ y += 9;
+ }
+
+ _messageShown = true;
+ _gm->_messageDuration = (Common::strnlen(text, 512) + 20) * _vm->_textSpeed / 10;
+}
+
+void Screen::removeMessage() {
+ if (_messageShown) {
+ restoreScreen();
+ _messageShown = false;
+ }
+}
+
+void Screen::renderBox(int x, int y, int width, int height, byte color) {
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+ screen->fillRect(Common::Rect(x, y, x + width, y + height), color);
+ _vm->_system->unlockScreen();
+}
+
+void Screen::renderBox(const GuiElement &guiElement) {
+ renderBox(guiElement.left, guiElement.top, guiElement.width(),
+ guiElement.height(), guiElement.getBackgroundColor());
+}
+
+void Screen::initPalette() {
+ g_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
+}
+
+void Screen::paletteBrightness() {
+ byte palette[768];
+
+ _vm->_system->getPaletteManager()->grabPalette(palette, 0, 255);
+ for (uint i = 0; i < 48; ++i) {
+ palette[i] = (initVGAPalette[i] * _guiBrightness) >> 8;
+ }
+ for (uint i = 0; i < 717; ++i) {
+ const byte *imagePalette;
+ if (_currentImage && _currentImage->getPalette()) {
+ imagePalette = _currentImage->getPalette();
+ } else {
+ imagePalette = palette + 48;
+ }
+ palette[i + 48] = (imagePalette[i] * _viewportBrightness) >> 8;
+ }
+ _vm->_system->getPaletteManager()->setPalette(palette, 0, 255);
+}
+
+void Screen::paletteFadeOut() {
+ while (_guiBrightness > 10) {
+ _guiBrightness -= 10;
+ if (_viewportBrightness > _guiBrightness)
+ _viewportBrightness = _guiBrightness;
+ paletteBrightness();
+ _vm->_system->updateScreen();
+ _vm->_system->delayMillis(_vm->_delay);
+ }
+ _guiBrightness = 0;
+ _viewportBrightness = 0;
+ paletteBrightness();
+ _vm->_system->updateScreen();
+}
+
+void Screen::paletteFadeIn() {
+ while (_guiBrightness < 245) {
+ if (_viewportBrightness < _gm->_roomBrightness)
+ _viewportBrightness += 10;
+ _guiBrightness += 10;
+ paletteBrightness();
+ _vm->_system->updateScreen();
+ _vm->_system->delayMillis(_vm->_delay);
+ }
+ _guiBrightness = 255;
+ _viewportBrightness = _gm->_roomBrightness;
+ paletteBrightness();
+ _vm->_system->updateScreen();
+}
+
+void Screen::setColor63(byte value) {
+ byte color[3] = {value, value, value};
+ _vm->_system->getPaletteManager()->setPalette(color, 63, 1);
+}
+
+}