/* 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 "made/screen.h" #include "made/made.h" #include "made/screenfx.h" #include "made/database.h" #include "common/system.h" #include "graphics/surface.h" #include "graphics/palette.h" #include "graphics/cursorman.h" namespace Made { Screen::Screen(MadeEngine *vm) : _vm(vm) { _palette = new byte[768]; _newPalette = new byte[768]; _backgroundScreen = new Graphics::Surface(); _backgroundScreen->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); _workScreen = new Graphics::Surface(); _workScreen->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); _backgroundScreenDrawCtx.clipRect = Common::Rect(320, 200); _workScreenDrawCtx.clipRect = Common::Rect(320, 200); _backgroundScreenDrawCtx.destSurface = _backgroundScreen; _workScreenDrawCtx.destSurface = _workScreen; _clipArea.destSurface = _workScreen; // Screen mask is only needed in v2 games if (_vm->getGameID() != GID_RTZ) { _screenMask = new Graphics::Surface(); _screenMask->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); _maskDrawCtx.clipRect = Common::Rect(320, 200); _maskDrawCtx.destSurface = _screenMask; } for (int i = 0; i <= 3; i++) _excludeClipAreaEnabled[i] = false; _screenLock = false; _paletteLock = false; _paletteInitialized = false; _needPalette = false; _oldPaletteColorCount = 256; _paletteColorCount = 256; memset(_newPalette, 0, 768); memset(_palette, 0, 768); _ground = 1; _clip = 0; _exclude = 0; _mask = 0; _visualEffectNum = 0; _fx = new ScreenEffects(this); _textX = 0; _textY = 0; _textColor = 0; _textRect.left = 0; _textRect.top = 0; _textRect.right = 320; _textRect.bottom = 200; _font = NULL; _currentFontNum = 0; _fontDrawCtx.clipRect = Common::Rect(320, 200); _fontDrawCtx.destSurface = _backgroundScreen; _outlineColor = 0; _dropShadowColor = 0; clearChannels(); } Screen::~Screen() { delete[] _palette; delete[] _newPalette; delete _backgroundScreen; delete _workScreen; if (_vm->getGameID() != GID_RTZ) delete _screenMask; delete _fx; } void Screen::clearScreen() { _backgroundScreen->fillRect(Common::Rect(0, 0, 320, 200), 0); _workScreen->fillRect(Common::Rect(0, 0, 320, 200), 0); if (_vm->getGameID() != GID_RTZ) _screenMask->fillRect(Common::Rect(0, 0, 320, 200), 0); _mask = 0; _needPalette = true; } void Screen::setExcludeArea(uint16 x1, uint16 y1, uint16 x2, uint16 y2) { _excludeClipAreaEnabled[0] = false; _excludeClipAreaEnabled[1] = false; _excludeClipAreaEnabled[2] = false; _excludeClipAreaEnabled[3] = false; if (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0) { _excludeClipArea[0].clipRect = Common::Rect(320, 200); _excludeClipAreaEnabled[0] = true; return; } if (y1 > 0 && y2 > 0) { _excludeClipArea[0].clipRect = Common::Rect(320, y1); _excludeClipAreaEnabled[0] = true; } if (y1 < 200 && y2 < 200) { _excludeClipArea[1].clipRect = Common::Rect(0, y2, 320, 200); _excludeClipAreaEnabled[1] = true; } if (x1 > 0 && x2 > 0) { _excludeClipArea[2].clipRect = Common::Rect(0, y1, x1, y2); _excludeClipAreaEnabled[2] = true; } if (x1 < 320 && x2 < 320) { _excludeClipArea[3].clipRect = Common::Rect(x2, y1, 320, y2); _excludeClipAreaEnabled[3] = true; } } void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo) { byte *source, *dest, *maskp = 0; int startX = 0; int startY = 0; int clipWidth = sourceSurface->w; int clipHeight = sourceSurface->h; if (x < clipInfo.clipRect.left) { startX = clipInfo.clipRect.left - x; clipWidth -= startX; x = clipInfo.clipRect.left; } if (y < clipInfo.clipRect.top) { startY = clipInfo.clipRect.top - y; clipHeight -= startY; y = clipInfo.clipRect.top; } if (x + clipWidth > clipInfo.clipRect.right) { clipWidth = clipInfo.clipRect.right - x; } if (y + clipHeight > clipInfo.clipRect.bottom) { clipHeight = clipInfo.clipRect.bottom - y; } source = (byte *)sourceSurface->getBasePtr(0, startY); dest = (byte *)clipInfo.destSurface->getBasePtr(x, y); if (_vm->getGameID() != GID_RTZ) maskp = (byte *)_maskDrawCtx.destSurface->getBasePtr(x, y); int32 sourcePitch, linePtrAdd, sourceAdd; byte *linePtr; if (flipX) { linePtrAdd = -1; sourceAdd = sourceSurface->w - startX - 1; } else { linePtrAdd = 1; sourceAdd = startX; } if (flipY) { sourcePitch = -sourceSurface->pitch; source += (clipHeight - 1) * sourceSurface->pitch; } else { sourcePitch = sourceSurface->pitch; } for (int16 yc = 0; yc < clipHeight; yc++) { linePtr = source + sourceAdd; for (int16 xc = 0; xc < clipWidth; xc++) { if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || (maskp && maskp[xc] == 0)))) { if (*linePtr) dest[xc] = *linePtr; } linePtr += linePtrAdd; } source += sourcePitch; dest += clipInfo.destSurface->pitch; if (_vm->getGameID() != GID_RTZ) maskp += _maskDrawCtx.destSurface->pitch; } } void Screen::setRGBPalette(byte *palRGB, int start, int count) { _vm->_system->getPaletteManager()->setPalette(palRGB, start, count); } uint16 Screen::updateChannel(uint16 channelIndex) { return channelIndex; } void Screen::deleteChannel(uint16 channelIndex) { if (channelIndex < 1 || channelIndex >= 100) return; _channels[channelIndex - 1].type = 0; _channels[channelIndex - 1].state = 0; _channels[channelIndex - 1].index = 0; } int16 Screen::getChannelType(uint16 channelIndex) { if (channelIndex < 1 || channelIndex >= 100) return -1; return _channels[channelIndex - 1].type; } int16 Screen::getChannelState(uint16 channelIndex) { if (channelIndex < 1 || channelIndex >= 100) return -1; return _channels[channelIndex - 1].state; } void Screen::setChannelState(uint16 channelIndex, int16 state) { if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0) return; _channels[channelIndex - 1].state = state; } uint16 Screen::setChannelLocation(uint16 channelIndex, int16 x, int16 y) { if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0) return 0; _channels[channelIndex - 1].x = x; _channels[channelIndex - 1].y = y; return updateChannel(channelIndex - 1) + 1; } uint16 Screen::setChannelContent(uint16 channelIndex, uint16 index) { if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0) return 0; _channels[channelIndex - 1].index = index; return updateChannel(channelIndex - 1) + 1; } void Screen::setChannelUseMask(uint16 channelIndex) { if (channelIndex < 1 || channelIndex >= 100) return; _channels[channelIndex - 1].mask = _mask; } void Screen::drawSpriteChannels(const ClipInfo &clipInfo, int16 includeStateMask, int16 excludeStateMask) { for (int i = 0; i <= 3; i++) _excludeClipArea[i].destSurface = clipInfo.destSurface; _clipArea.destSurface = clipInfo.destSurface; for (uint16 i = 0; i < _channelsUsedCount; i++) { debug(2, "drawSpriteChannels() i = %d\n", i); if (((_channels[i].state & includeStateMask) == includeStateMask) && (_channels[i].state & excludeStateMask) == 0) { int16 flipX = _channels[i].state & 0x10; int16 flipY = _channels[i].state & 0x20; debug(2, "drawSpriteChannels() type = %d; index = %04X\n", _channels[i].type, _channels[i].index); switch (_channels[i].type) { case 1: // drawFlex if (_channels[i].state & 4) { drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, _clipArea); } else if (_channels[i].state & 8) { for (int excludeIndex = 0; excludeIndex < 4; excludeIndex++) { if (_excludeClipAreaEnabled[excludeIndex]) { drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, _excludeClipArea[excludeIndex]); } } } else { drawFlex(_channels[i].index, _channels[i].x, _channels[i].y, flipX, flipY, _channels[i].mask, clipInfo); } break; case 2: // drawObjectText printObjectText(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].fontNum, _channels[i].textColor, _channels[i].outlineColor, clipInfo); break; case 3: // drawAnimFrame if (_channels[i].state & 4) { drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, _clipArea); } else if (_channels[i].state & 8) { for (int excludeIndex = 0; excludeIndex < 4; excludeIndex++) { if (_excludeClipAreaEnabled[excludeIndex]) { drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, _excludeClipArea[excludeIndex]); } } } else { drawAnimFrame(_channels[i].index, _channels[i].x, _channels[i].y, _channels[i].frameNum, flipX, flipY, clipInfo); } break; case 4: // drawMenuText // Never used in any game break; default: break; } } } } void Screen::updateSprites() { // TODO: This needs some more work, dirty rectangles are currently not used memcpy(_workScreen->getPixels(), _backgroundScreen->getPixels(), 64000); drawSpriteChannels(_backgroundScreenDrawCtx, 3, 0); drawSpriteChannels(_workScreenDrawCtx, 1, 2); _vm->_system->copyRectToScreen(_workScreen->getPixels(), _workScreen->pitch, 0, 0, _workScreen->w, _workScreen->h); _vm->_screen->updateScreenAndWait(10); } void Screen::clearChannels() { for (uint16 i = 0; i < ARRAYSIZE(_channels); i++) { _channels[i].type = 0; _channels[i].index = 0; _channels[i].mask = 0; } _channelsUsedCount = 0; } uint16 Screen::drawFlex(uint16 flexIndex, int16 x, int16 y, int16 flipX, int16 flipY, int16 mask, const ClipInfo &clipInfo) { if (flexIndex == 0) return 0; PictureResource *flex = _vm->_res->getPicture(flexIndex); if (!flex) error("Failed to find picture %d", flexIndex); Graphics::Surface *sourceSurface = flex->getPicture(); drawSurface(sourceSurface, x, y, flipX, flipY, mask, clipInfo); // Palette is set in showPage if (flex->hasPalette() && !_paletteLock && _needPalette) { byte *flexPalette = flex->getPalette(); _oldPaletteColorCount = _paletteColorCount; _paletteColorCount = flex->getPaletteColorCount(); memcpy(_newPalette, _palette, _oldPaletteColorCount * 3); memcpy(_palette, flexPalette, _paletteColorCount * 3); _needPalette = false; } _vm->_res->freeResource(flex); return 0; } void Screen::drawAnimFrame(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY, const ClipInfo &clipInfo) { if (frameNum < 0) return; AnimationResource *anim = _vm->_res->getAnimation(animIndex); Graphics::Surface *sourceSurface = anim->getFrame(frameNum); drawSurface(sourceSurface, x, y, flipX, flipY, 0, clipInfo); _vm->_res->freeResource(anim); } uint16 Screen::drawPic(uint16 index, int16 x, int16 y, int16 flipX, int16 flipY) { drawFlex(index, x, y, flipX, flipY, 0, _backgroundScreenDrawCtx); return 0; } uint16 Screen::drawMask(uint16 index, int16 x, int16 y) { drawFlex(index, x, y, 0, 0, 0, _maskDrawCtx); return 0; } uint16 Screen::drawAnimPic(uint16 animIndex, int16 x, int16 y, int16 frameNum, int16 flipX, int16 flipY) { drawAnimFrame(animIndex, x, y, frameNum, flipX, flipY, _backgroundScreenDrawCtx); return 0; } void Screen::addSprite(uint16 spriteIndex) { bool oldScreenLock = _screenLock; drawFlex(spriteIndex, 0, 0, 0, 0, 0, _backgroundScreenDrawCtx); _screenLock = oldScreenLock; } uint16 Screen::drawSprite(uint16 flexIndex, int16 x, int16 y) { return placeSprite(_channelsUsedCount + 1, flexIndex, x, y); } uint16 Screen::placeSprite(uint16 channelIndex, uint16 flexIndex, int16 x, int16 y) { debug(2, "placeSprite(%d, %04X, %d, %d)\n", channelIndex, flexIndex, x, y); if (channelIndex < 1 || channelIndex >= 100) return 0; channelIndex--; PictureResource *flex = _vm->_res->getPicture(flexIndex); if (flex) { //Graphics::Surface *surf = flex->getPicture(); int16 state = 1; /*int16 x1, y1, x2, y2; x1 = x; y1 = y; x2 = x + surf->w + 1; y2 = y + surf->h + 1;*/ if (_ground == 0) state |= 2; if (_clip != 0) state |= 4; if (_exclude != 0) state |= 8; _channels[channelIndex].state = state; _channels[channelIndex].type = 1; _channels[channelIndex].index = flexIndex; _channels[channelIndex].x = x; _channels[channelIndex].y = y; if (_channelsUsedCount <= channelIndex) _channelsUsedCount = channelIndex + 1; _vm->_res->freeResource(flex); } else { _channels[channelIndex].type = 0; _channels[channelIndex].state = 0; } return channelIndex + 1; } uint16 Screen::placeAnim(uint16 channelIndex, uint16 animIndex, int16 x, int16 y, int16 frameNum) { if (channelIndex < 1 || channelIndex >= 100) return 0; channelIndex--; AnimationResource *anim = _vm->_res->getAnimation(animIndex); if (anim) { int16 state = 1; /*int16 x1, y1, x2, y2; x1 = x; y1 = y; x2 = x + anim->getWidth(); y2 = y + anim->getHeight();*/ if (anim->getFlags() == 1 || _ground == 0) state |= 2; if (_clip != 0) state |= 4; if (_exclude != 0) state |= 8; _channels[channelIndex].state = state; _channels[channelIndex].type = 3; _channels[channelIndex].index = animIndex; _channels[channelIndex].frameNum = frameNum; _channels[channelIndex].x = x; _channels[channelIndex].y = y; if (_channelsUsedCount <= channelIndex) _channelsUsedCount = channelIndex + 1; _vm->_res->freeResource(anim); } else { _channels[channelIndex].type = 0; _channels[channelIndex].state = 0; } return channelIndex + 1; } int16 Screen::setAnimFrame(uint16 channelIndex, int16 frameNum) { if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0) return 0; channelIndex--; _channels[channelIndex].frameNum = frameNum; return updateChannel(channelIndex) + 1; } int16 Screen::getAnimFrame(uint16 channelIndex) { if (channelIndex < 1 || channelIndex >= 100 || _channels[channelIndex - 1].type == 0) return -1; return _channels[channelIndex - 1].frameNum; } uint16 Screen::placeText(uint16 channelIndex, uint16 textObjectIndex, int16 x, int16 y, uint16 fontNum, int16 textColor, int16 outlineColor) { if (channelIndex < 1 || channelIndex >= 100 || textObjectIndex == 0 || fontNum == 0) return 0; channelIndex--; Object *obj = _vm->_dat->getObject(textObjectIndex); const char *text = obj->getString(); //int16 x1, y1, x2, y2; setFont(fontNum); int textWidth = _font->getTextWidth(text); int textHeight = _font->getHeight(); if (outlineColor != -1) { textWidth += 2; textHeight += 2; x--; y--; } /*x1 = x; y1 = y; x2 = x + textWidth; y2 = y + textHeight;*/ if (textWidth > 0 && outlineColor != -1) { x++; y++; } int16 state = 1; if (_ground == 0) state |= 2; _channels[channelIndex].state = state; _channels[channelIndex].type = 2; _channels[channelIndex].index = textObjectIndex; _channels[channelIndex].x = x; _channels[channelIndex].y = y; _channels[channelIndex].textColor = textColor; _channels[channelIndex].fontNum = fontNum; _channels[channelIndex].outlineColor = outlineColor; if (_channelsUsedCount <= channelIndex) _channelsUsedCount = channelIndex + 1; return channelIndex + 1; } void Screen::show() { if (_screenLock) return; drawSpriteChannels(_backgroundScreenDrawCtx, 3, 0); memcpy(_workScreen->getPixels(), _backgroundScreen->getPixels(), 64000); drawSpriteChannels(_workScreenDrawCtx, 1, 2); _fx->run(_visualEffectNum, _workScreen, _palette, _newPalette, _paletteColorCount); _visualEffectNum = 0; if (!_paletteInitialized) { memcpy(_newPalette, _palette, _paletteColorCount * 3); _oldPaletteColorCount = _paletteColorCount; _paletteInitialized = true; } updateScreenAndWait(10); } void Screen::flash(int flashCount) { _fx->flash(flashCount, _palette, _paletteColorCount); } void Screen::setFont(int16 fontNum) { if (fontNum == _currentFontNum) return; if (_font) _vm->_res->freeResource(_font); _font = _vm->_res->getFont(fontNum); _currentFontNum = fontNum; } void Screen::printChar(uint c, int16 x, int16 y, byte color) { if (!_font) return; uint width = 8, height = _font->getHeight(); byte *charData = _font->getChar(c); if (!charData) return; byte p; byte *dest = (byte *)_fontDrawCtx.destSurface->getBasePtr(x, y); for (uint yc = 0; yc < height; yc++) { p = charData[yc]; for (uint xc = 0; xc < width; xc++) { if (p & 0x80) dest[xc] = color; p <<= 1; } dest += _fontDrawCtx.destSurface->pitch; } } void Screen::printText(const char *text) { const int tabWidth = 5; if (!_font) return; int textLen = strlen(text); int textHeight = _font->getHeight(); int linePos = 1; int16 x = _textX; int16 y = _textY; for (int textPos = 0; textPos < textLen; textPos++) { uint c = ((const byte*)text)[textPos]; int charWidth = _font->getCharWidth(c); if (c == 9) { linePos = ((linePos / tabWidth) + 1) * tabWidth; x = _textRect.left + linePos * _font->getCharWidth(32); } else if (c == 10) { linePos = 1; x = _textRect.left; y += textHeight; } else if (c == 13) { linePos = 1; x = _textRect.left; } else if (c == 32) { int wrapPos = textPos + 1; int wrapX = x + charWidth; while (wrapPos < textLen && text[wrapPos] != 0 && text[wrapPos] != 32 && text[wrapPos] >= 28) { wrapX += _font->getCharWidth(text[wrapPos]); wrapPos++; } if (wrapX >= _textRect.right) { linePos = 1; x = _textRect.left; y += textHeight; charWidth = 0; // TODO: text[textPos] = '\x01'; } } if (x + charWidth > _textRect.right) { linePos = 1; x = _textRect.left; y += textHeight; } if (y + textHeight > _textRect.bottom) { // TODO } if (c >= 28 && c <= 255) { if (_dropShadowColor != -1) { printChar(c, x + 1, y + 1, _dropShadowColor); } if (_outlineColor != -1) { printChar(c, x, y - 1, _outlineColor); printChar(c, x, y + 1, _outlineColor); printChar(c, x - 1, y, _outlineColor); printChar(c, x + 1, y, _outlineColor); printChar(c, x - 1, y - 1, _outlineColor); printChar(c, x - 1, y + 1, _outlineColor); printChar(c, x + 1, y - 1, _outlineColor); printChar(c, x + 1, y + 1, _outlineColor); } printChar(c, x, y, _textColor); x += charWidth; linePos++; } } _textX = x; _textY = y; } void Screen::printTextEx(const char *text, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo) { if (*text == 0 || x < 0 || y < 0) return; int16 oldFontNum = _currentFontNum; Common::Rect oldTextRect; ClipInfo oldFontDrawCtx = _fontDrawCtx; _fontDrawCtx = clipInfo; getTextRect(oldTextRect); setFont(fontNum); setTextColor(textColor); setOutlineColor(outlineColor); setTextXY(x, y); printText(text); setTextRect(oldTextRect); setFont(oldFontNum); _fontDrawCtx = oldFontDrawCtx; } void Screen::printObjectText(int16 objectIndex, int16 x, int16 y, int16 fontNum, int16 textColor, int16 outlineColor, const ClipInfo &clipInfo) { if (objectIndex == 0) return; Object *obj = _vm->_dat->getObject(objectIndex); const char *text = obj->getString(); printTextEx(text, x, y, fontNum, textColor, outlineColor, clipInfo); } int16 Screen::getTextWidth(int16 fontNum, const char *text) { setFont(fontNum); return _font->getTextWidth(text); } Graphics::Surface *Screen::lockScreen() { return _vm->_system->lockScreen(); } void Screen::unlockScreen() { _vm->_system->unlockScreen(); } void Screen::showWorkScreen() { _vm->_system->copyRectToScreen(_workScreen->getPixels(), _workScreen->pitch, 0, 0, _workScreen->w, _workScreen->h); } void Screen::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) { _vm->_system->copyRectToScreen(buf, pitch, x, y, w, h); } void Screen::updateScreenAndWait(int delay) { _vm->_system->updateScreen(); uint32 startTime = _vm->_system->getMillis(); while (_vm->_system->getMillis() < startTime + delay) { _vm->handleEvents(); _vm->_system->delayMillis(5); } } int16 Screen::addToSpriteList(int16 index, int16 xofs, int16 yofs) { SpriteListItem item; item.index = index; item.xofs = xofs; item.yofs = yofs; _spriteList.push_back(item); return _spriteList.size(); } SpriteListItem Screen::getFromSpriteList(int16 index) { if (((uint) index) > _spriteList.size()) { SpriteListItem emptyItem; emptyItem.index = 0; emptyItem.xofs = 0; emptyItem.yofs = 0; return emptyItem; } else { return _spriteList[index - 1]; } } void Screen::clearSpriteList() { _spriteList.clear(); } void Screen::setDefaultMouseCursor() { CursorMan.replaceCursor(defaultMouseCursor, 16, 16, 9, 2, 0); } } // End of namespace Made