scummvm/engines/sci/gui/gui_transitions.cpp

428 lines
15 KiB
C++

/* 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.
*
* $URL$
* $Id$
*
*/
#include "common/util.h"
#include "common/stack.h"
#include "graphics/primitives.h"
#include "sci/sci.h"
#include "sci/engine/state.h"
#include "sci/gui/gui.h"
#include "sci/gui/gui_screen.h"
#include "sci/gui/gui_palette.h"
#include "sci/gui/gui_transitions.h"
namespace Sci {
SciGuiTransitions::SciGuiTransitions(SciGui *gui, SciGuiScreen *screen, SciGuiPalette *palette, bool isVGA)
: _gui(gui), _screen(screen), _palette(palette), _isVGA(isVGA) {
init();
}
SciGuiTransitions::~SciGuiTransitions() {
delete[] _oldScreen;
}
// This table contains a mapping between oldIDs (prior SCI1LATE) and newIDs
static const GuiTransitionTranslateEntry oldTransitionIDs[] = {
{ 0, SCI_TRANSITIONS_VERTICALROLLFROMCENTER, false },
{ 1, SCI_TRANSITIONS_HORIZONTALROLLFROMCENTER, false },
{ 6, SCI_TRANSITIONS_DIAGONALROLLFROMCENTER, false },
{ 7, SCI_TRANSITIONS_DIAGONALROLLTOCENTER, false },
{ 8, SCI_TRANSITIONS_BLOCKS, false },
{ 9, SCI_TRANSITIONS_VERTICALROLLTOCENTER, false },
{ 10, SCI_TRANSITIONS_HORIZONTALROLLTOCENTER, false },
{ 15, SCI_TRANSITIONS_DIAGONALROLLFROMCENTER, true },
{ 16, SCI_TRANSITIONS_DIAGONALROLLTOCENTER, true },
{ 17, SCI_TRANSITIONS_BLOCKS, true },
{ 18, SCI_TRANSITIONS_PIXELATION, false },
{ 27, SCI_TRANSITIONS_PIXELATION , true },
{ 30, SCI_TRANSITIONS_FADEPALETTE, false },
{ 40, SCI_TRANSITIONS_SCROLLRIGHT, false },
{ 41, SCI_TRANSITIONS_SCROLLLEFT, false },
{ 42, SCI_TRANSITIONS_SCROLLUP, false },
{ 43, SCI_TRANSITIONS_SCROLLDOWN, false },
{ 100, SCI_TRANSITIONS_NONE, false },
{ 255, 255, false }
};
void SciGuiTransitions::init() {
_oldScreen = new byte[_screen->_displayHeight * _screen->_displayWidth];
if (getSciVersion() >= SCI_VERSION_1_LATE)
_translationTable = NULL;
else
_translationTable = oldTransitionIDs;
}
void SciGuiTransitions::setup(int16 number, bool blackoutFlag) {
_number = number;
_blackoutFlag = blackoutFlag;
}
void SciGuiTransitions::doit(Common::Rect picRect) {
const GuiTransitionTranslateEntry *translationEntry = _translationTable;
_picRect = picRect;
if (translationEntry) {
// We need to translate the ID
while (1) {
if (translationEntry->oldId == 255) {
warning("SciGuiTransitions: old ID %d not supported", _number);
setNewPalette(); setNewScreen();
_screen->_picNotValid = 0;
return;
}
if (translationEntry->oldId == _number) {
_number = translationEntry->realId;
_blackoutFlag = translationEntry->blackoutFlag;
break;
}
translationEntry++;
}
}
if (_blackoutFlag)
warning("SciGuiTransitions: blackout flag currently not supported");
switch (_number) {
case SCI_TRANSITIONS_VERTICALROLLFROMCENTER:
setNewPalette(); verticalRollFromCenter();
break;
case SCI_TRANSITIONS_VERTICALROLLTOCENTER:
setNewPalette(); verticalRollFromCenter();
break;
case SCI_TRANSITIONS_HORIZONTALROLLFROMCENTER:
setNewPalette(); horizontalRollFromCenter();
break;
case SCI_TRANSITIONS_HORIZONTALROLLTOCENTER:
setNewPalette(); horizontalRollToCenter();
break;
case SCI_TRANSITIONS_DIAGONALROLLTOCENTER:
setNewPalette(); diagonalRollToCenter();
case SCI_TRANSITIONS_DIAGONALROLLFROMCENTER:
setNewPalette(); diagonalRollFromCenter();
case SCI_TRANSITIONS_PIXELATION:
setNewPalette(); pixelation();
break;
case SCI_TRANSITIONS_BLOCKS:
setNewPalette(); blocks();
break;
case SCI_TRANSITIONS_FADEPALETTE:
fadeOut(); setNewScreen(); fadeIn();
break;
case SCI_TRANSITIONS_SCROLLRIGHT:
case SCI_TRANSITIONS_SCROLLLEFT:
case SCI_TRANSITIONS_SCROLLUP:
case SCI_TRANSITIONS_SCROLLDOWN:
setNewPalette(); scroll();
break;
case SCI_TRANSITIONS_NONE_LONGBOW:
case SCI_TRANSITIONS_NONE:
setNewPalette(); setNewScreen();
break;
default:
warning("SciGuiTransitions: ID %d not implemented", _number);
setNewPalette(); setNewScreen();
}
_screen->_picNotValid = 0;
}
void SciGuiTransitions::setNewPalette() {
if (_isVGA)
_palette->setOnScreen();
}
void SciGuiTransitions::setNewScreen() {
_screen->copyRectToScreen(_picRect);
g_system->updateScreen();
}
// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
void SciGuiTransitions::fadeOut() {
byte oldPalette[4 * 256], workPalette[4 * 256];
int16 stepNr, colorNr;
g_system->grabPalette(oldPalette, 0, 256);
for (stepNr = 100; stepNr >= 0; stepNr -= 10) {
for (colorNr = 1; colorNr < 255; colorNr++){
workPalette[colorNr * 4 + 0] = oldPalette[colorNr * 4] * stepNr / 100;
workPalette[colorNr * 4 + 1] = oldPalette[colorNr * 4 + 1] * stepNr / 100;
workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100;
}
g_system->setPalette(workPalette + 4, 1, 254);
_gui->wait(2);
}
}
// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
void SciGuiTransitions::fadeIn() {
int16 stepNr;
for (stepNr = 0; stepNr <= 100; stepNr += 10) {
_palette->setIntensity(1, 254, 100, true);
_gui->wait(2);
}
}
// pixelates the new picture over the old one - works against the whole screen
void SciGuiTransitions::pixelation () {
uint16 mask = 0x40, stepNr = 0;
Common::Rect pixelRect;
do {
mask = (mask & 1) ? (mask >> 1) ^ 0xB400 : mask >> 1;
if (mask >= 320 * 200)
continue;
pixelRect.left = mask % 320; pixelRect.right = pixelRect.left + 1;
pixelRect.top = mask / 320; pixelRect.bottom = pixelRect.top + 1;
_screen->copyRectToScreen(pixelRect);
if ((stepNr & 0x3FF) == 0) {
g_system->updateScreen();
g_system->delayMillis(5);
}
stepNr++;
} while (mask != 0x40);
}
// like pixelation but uses 8x8 blocks - works against the whole screen
void SciGuiTransitions::blocks() {
uint16 mask = 0x40, stepNr = 0;
Common::Rect blockRect;
do {
mask = (mask & 1) ? (mask >> 1) ^ 0x240 : mask >> 1;
if (mask >= 40 * 25)
continue;
blockRect.left = (mask % 40) << 3; blockRect.right = blockRect.left + 8;
blockRect.top = (mask / 40) << 3; blockRect.bottom = blockRect.top + 8;
_screen->copyRectToScreen(blockRect);
if ((stepNr & 7) == 0) {
g_system->updateScreen();
g_system->delayMillis(4);
}
stepNr++;
} while (mask != 0x40);
}
// scroll old screen (up/down/left/right) and insert new screen that way - works on _picRect area only
void SciGuiTransitions::scroll() {
int16 screenWidth, screenHeight;
byte *oldScreenPtr;
int16 stepNr = 0;
Common::Rect oldMoveRect = _picRect;
Common::Rect newMoveRect = _picRect;
Common::Rect newScreenRect = _picRect;
_screen->copyFromScreen(_oldScreen);
screenWidth = _screen->_displayWidth; screenHeight = _screen->_displayHeight;
oldScreenPtr = _oldScreen + _picRect.left + _picRect.top * screenWidth;
switch (_number) {
case SCI_TRANSITIONS_SCROLLLEFT:
newScreenRect.right = newScreenRect.left;
newMoveRect.left = newMoveRect.right;
while (oldMoveRect.left < oldMoveRect.right) {
oldScreenPtr++; oldMoveRect.right--;
if (oldMoveRect.right > oldMoveRect.left)
g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
newScreenRect.right++; newMoveRect.left--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
if ((stepNr & 1) == 0) {
g_system->updateScreen();
g_system->delayMillis(1);
}
stepNr++;
}
if ((stepNr & 1) == 0)
g_system->updateScreen();
break;
case SCI_TRANSITIONS_SCROLLRIGHT:
newScreenRect.left = newScreenRect.right;
while (oldMoveRect.left < oldMoveRect.right) {
oldMoveRect.left++;
if (oldMoveRect.right > oldMoveRect.left)
g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
newScreenRect.left--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
if ((stepNr & 1) == 0) {
g_system->updateScreen();
g_system->delayMillis(1);
}
stepNr++;
}
if ((stepNr & 1) == 0)
g_system->updateScreen();
break;
case SCI_TRANSITIONS_SCROLLUP:
newScreenRect.bottom = newScreenRect.top;
newMoveRect.top = newMoveRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
oldScreenPtr += screenWidth; oldMoveRect.top++;
if (oldMoveRect.top < oldMoveRect.bottom)
g_system->copyRectToScreen(oldScreenPtr, screenWidth, _picRect.left, _picRect.top, oldMoveRect.width(), oldMoveRect.height());
newScreenRect.bottom++; newMoveRect.top--;
_screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
g_system->updateScreen();
g_system->delayMillis(3);
}
break;
case SCI_TRANSITIONS_SCROLLDOWN:
newScreenRect.top = newScreenRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
oldMoveRect.top++;
if (oldMoveRect.top < oldMoveRect.bottom)
g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height());
newScreenRect.top--;
_screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top);
g_system->updateScreen();
g_system->delayMillis(3);
}
break;
}
}
// vertically displays new screen starting from center - works on _picRect area only
void SciGuiTransitions::verticalRollFromCenter() {
Common::Rect leftRect = Common::Rect(_picRect.left + (_picRect.width() / 2) -1, _picRect.top, _picRect.left + (_picRect.width() / 2), _picRect.bottom);
Common::Rect rightRect = Common::Rect(leftRect.right, _picRect.top, leftRect.right + 1, _picRect.bottom);
while ((leftRect.left >= _picRect.left) || (rightRect.right <= _picRect.right)) {
if (leftRect.left < _picRect.left)
leftRect.translate(1, 0);
if (rightRect.right > _picRect.right)
rightRect.translate(-1, 0);
_screen->copyRectToScreen(leftRect); leftRect.translate(-1, 0);
_screen->copyRectToScreen(rightRect); rightRect.translate(1, 0);
g_system->updateScreen();
g_system->delayMillis(2);
}
}
// vertically displays new screen starting from edges - works on _picRect area only
void SciGuiTransitions::verticalRollToCenter() {
Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
Common::Rect rightRect = Common::Rect(leftRect.right - 1, _picRect.top, leftRect.right, _picRect.bottom);
while (leftRect.left < rightRect.right) {
_screen->copyRectToScreen(leftRect); leftRect.translate(1, 0);
_screen->copyRectToScreen(rightRect); rightRect.translate(-1, 0);
g_system->updateScreen();
g_system->delayMillis(2);
}
}
// horizontally displays new screen starting from center - works on _picRect area only
void SciGuiTransitions::horizontalRollFromCenter() {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top + (_picRect.height() / 2) - 1, _picRect.right, _picRect.top + (_picRect.height() / 2));
Common::Rect lowerRect = Common::Rect(upperRect.left, upperRect.bottom, upperRect.right, upperRect.bottom + 1);
while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) {
if (upperRect.top < _picRect.top)
upperRect.translate(0, 1);
if (lowerRect.bottom > _picRect.bottom)
lowerRect.translate(0, -1);
_screen->copyRectToScreen(upperRect); upperRect.translate(0, -1);
_screen->copyRectToScreen(lowerRect); lowerRect.translate(0, 1);
g_system->updateScreen();
g_system->delayMillis(3);
}
}
// horizontally displays new screen starting from upper and lower edge - works on _picRect area only
void SciGuiTransitions::horizontalRollToCenter() {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect = Common::Rect(upperRect.left, _picRect.bottom - 1, upperRect.right, _picRect.bottom);
while (upperRect.top < lowerRect.bottom) {
_screen->copyRectToScreen(upperRect); upperRect.translate(0, 1);
_screen->copyRectToScreen(lowerRect); lowerRect.translate(0, -1);
g_system->updateScreen();
g_system->delayMillis(3);
}
}
// diagonally displays new screen starting from center - works on _picRect area only
// assumes that height of rect is larger than width, is also currently not optimized (TODO)
void SciGuiTransitions::diagonalRollFromCenter() {
int16 halfHeight = _picRect.height() / 2;
Common::Rect upperRect(_picRect.left + halfHeight - 2, _picRect.top + halfHeight, _picRect.right - halfHeight + 1, _picRect.top + halfHeight + 1);
Common::Rect lowerRect(upperRect.left, upperRect.top, upperRect.right, upperRect.bottom);
Common::Rect leftRect(upperRect.left, upperRect.top, upperRect.left + 1, lowerRect.bottom);
Common::Rect rightRect(upperRect.right, upperRect.top, upperRect.right + 1, lowerRect.bottom);
while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) {
if (upperRect.top < _picRect.top) {
upperRect.translate(0, 1); leftRect.top++; rightRect.top++;
}
if (lowerRect.bottom > _picRect.bottom) {
lowerRect.translate(0, -1); leftRect.bottom--; rightRect.bottom--;
}
if (leftRect.left < _picRect.left) {
leftRect.translate(1, 0); upperRect.left++; lowerRect.left++;
}
if (rightRect.right > _picRect.right) {
rightRect.translate(-1, 0); upperRect.right--; lowerRect.right--;
}
_screen->copyRectToScreen(upperRect); upperRect.translate(0, -1); upperRect.left--; upperRect.right++;
_screen->copyRectToScreen(lowerRect); lowerRect.translate(0, 1); lowerRect.left--; lowerRect.right++;
_screen->copyRectToScreen(leftRect); leftRect.translate(-1, 0); leftRect.top--; leftRect.bottom++;
_screen->copyRectToScreen(rightRect); rightRect.translate(1, 0); rightRect.top--; rightRect.bottom++;
g_system->updateScreen();
g_system->delayMillis(3);
}
}
// diagonally displays new screen starting from edges - works on _picRect area only
// assumes that height of rect is larger than width
void SciGuiTransitions::diagonalRollToCenter() {
Common::Rect upperRect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect(_picRect.left, _picRect.bottom - 1, _picRect.right, _picRect.bottom);
Common::Rect leftRect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
Common::Rect rightRect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom);
while (upperRect.top < lowerRect.bottom) {
_screen->copyRectToScreen(upperRect); upperRect.translate(0, 1); upperRect.left++; upperRect.right--;
_screen->copyRectToScreen(lowerRect); lowerRect.translate(0, -1); lowerRect.left++; lowerRect.right--;
_screen->copyRectToScreen(leftRect); leftRect.translate(1, 0);
_screen->copyRectToScreen(rightRect); rightRect.translate(-1, 0);
g_system->updateScreen();
g_system->delayMillis(3);
}
}
} // End of namespace Sci