diff options
Diffstat (limited to 'engines/supernova/screen.cpp')
-rw-r--r-- | engines/supernova/screen.cpp | 636 |
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); +} + +} |