/* 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 "graphics/palette.h" #include "prince/prince.h" #include "prince/animation.h" #include "prince/graphics.h" #include "prince/hero.h" #include "prince/script.h" namespace Prince { bool PrinceEngine::spriteCheck(int sprWidth, int sprHeight, int destX, int destY) { destX -= _picWindowX; destY -= _picWindowY; // if x1 is on visible part of screen if (destX < 0) { if (destX + sprWidth < 1) { //x2 is negative - out of window return false; } } // if x1 is outside of screen on right side if (destX >= kNormalWidth) { return false; } if (destY < 0) { if (destY + sprHeight < 1) { //y2 is negative - out of window return false; } } if (destY >= kNormalHeight) { return false; } return true; } // CheckNak void PrinceEngine::checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z) { int x2 = x1 + sprWidth - 1; int y2 = y1 + sprHeight - 1; if (x1 < 0) { x1 = 0; } for (uint i = 0; i < _maskList.size(); i++) { if (!_maskList[i]._state && !_maskList[i]._flags) { if (_maskList[i]._z > z) { if (_maskList[i]._x1 <= x2 && _maskList[i]._x2 >= x1) { if (_maskList[i]._y1 <= y2 && _maskList[i]._y2 >= y1) { _maskList[i]._state = 1; } } } } } } // ClsNak void PrinceEngine::clsMasks() { for (uint i = 0; i < _maskList.size(); i++) { if (_maskList[i]._state) { _maskList[i]._state = 0; } } } // InsertNakladki void PrinceEngine::insertMasks(Graphics::Surface *originalRoomSurface) { for (uint i = 0; i < _maskList.size(); i++) { if (_maskList[i]._state) { if (_maskList[i]._data != nullptr) { showMask(i, originalRoomSurface); } else { error("insertMasks() - Wrong mask data- nr %d", i); } } } } // ShowNak void PrinceEngine::showMask(int maskNr, Graphics::Surface *originalRoomSurface) { if (!_maskList[maskNr]._flags) { if (spriteCheck(_maskList[maskNr]._width, _maskList[maskNr]._height, _maskList[maskNr]._x1, _maskList[maskNr]._y1)) { int destX = _maskList[maskNr]._x1 - _picWindowX; int destY = _maskList[maskNr]._y1 - _picWindowY; DrawNode newDrawNode; newDrawNode.posX = destX; newDrawNode.posY = destY; newDrawNode.posZ = _maskList[maskNr]._z; newDrawNode.width = _maskList[maskNr]._width; newDrawNode.height = _maskList[maskNr]._height; newDrawNode.s = nullptr; newDrawNode.originalRoomSurface = originalRoomSurface; newDrawNode.data = _maskList[maskNr].getMask(); newDrawNode.drawFunction = &_graph->drawMaskDrawNode; _drawNodeList.push_back(newDrawNode); } } } void PrinceEngine::showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ) { if (spriteCheck(spriteSurface->w, spriteSurface->h, destX, destY)) { destX -= _picWindowX; destY -= _picWindowY; DrawNode newDrawNode; newDrawNode.posX = destX; newDrawNode.posY = destY; newDrawNode.posZ = destZ; newDrawNode.width = 0; newDrawNode.height = 0; newDrawNode.s = spriteSurface; newDrawNode.originalRoomSurface = nullptr; newDrawNode.data = _transTable; newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; _drawNodeList.push_back(newDrawNode); } } void PrinceEngine::showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ) { if (spriteCheck(shadowSurface->w, shadowSurface->h, destX, destY)) { destX -= _picWindowX; destY -= _picWindowY; DrawNode newDrawNode; newDrawNode.posX = destX; newDrawNode.posY = destY; newDrawNode.posZ = destZ; newDrawNode.width = 0; newDrawNode.height = 0; newDrawNode.s = shadowSurface; newDrawNode.originalRoomSurface = nullptr; newDrawNode.data = _graph->_shadowTable70; newDrawNode.drawFunction = &_graph->drawAsShadowDrawNode; _drawNodeList.push_back(newDrawNode); } } void PrinceEngine::showAnim(Anim &anim) { //ShowFrameCode //ShowAnimFrame int phase = anim._showFrame; int phaseFrameIndex = anim._animData->getPhaseFrameIndex(phase); int x = anim._x + anim._animData->getPhaseOffsetX(phase); int y = anim._y + anim._animData->getPhaseOffsetY(phase); int animFlag = anim._flags; int checkMaskFlag = (animFlag & 1); int maxFrontFlag = (animFlag & 2); int specialZFlag = anim._nextAnim; int z = anim._nextAnim; Graphics::Surface *animSurface = anim._animData->getFrame(phaseFrameIndex); int frameWidth = animSurface->w; int frameHeight = animSurface->h; int shadowZ = 0; if (checkMaskFlag) { if (!anim._nextAnim) { z = y + frameHeight - 1; } checkMasks(x, y, frameWidth, frameHeight, z); } if (specialZFlag) { z = specialZFlag; } else if (maxFrontFlag) { z = kMaxPicHeight + 1; } else { z = y + frameHeight - 1; } shadowZ = z; anim._currX = x; anim._currY = y; anim._currW = frameWidth; anim._currH = frameHeight; showSprite(animSurface, x, y, z); // make_special_shadow if ((anim._flags & 0x80)) { DrawNode newDrawNode; newDrawNode.posX = x; newDrawNode.posY = y + animSurface->h - anim._shadowBack; newDrawNode.posZ = Hero::kHeroShadowZ; newDrawNode.width = 0; newDrawNode.height = 0; newDrawNode.scaleValue = _scaleValue; newDrawNode.originalRoomSurface = nullptr; newDrawNode.data = this; newDrawNode.drawFunction = &Hero::showHeroShadow; newDrawNode.s = animSurface; _drawNodeList.push_back(newDrawNode); } //ShowFrameCodeShadow //ShowAnimFrameShadow if (anim._shadowData != nullptr) { int shadowPhaseFrameIndex = anim._shadowData->getPhaseFrameIndex(phase); int shadowX = anim._shadowData->getBaseX() + anim._shadowData->getPhaseOffsetX(phase); int shadowY = anim._shadowData->getBaseY() + anim._shadowData->getPhaseOffsetY(phase); Graphics::Surface *shadowSurface = anim._shadowData->getFrame(shadowPhaseFrameIndex); int shadowFrameWidth = shadowSurface->w; int shadowFrameHeight = shadowSurface->h; if (checkMaskFlag) { checkMasks(shadowX, shadowY, shadowFrameWidth, shadowFrameHeight, shadowY + shadowFrameWidth - 1); } if (!shadowZ) { if (maxFrontFlag) { shadowZ = kMaxPicHeight + 1; } else { shadowZ = shadowY + shadowFrameWidth - 1; } } showSpriteShadow(shadowSurface, shadowX, shadowY, shadowZ); } } void PrinceEngine::showNormAnims() { for (int i = 0; i < kMaxNormAnims; i++) { Anim &anim = _normAnimList[i]; if (anim._animData != nullptr) { int phaseCount = anim._animData->getPhaseCount(); if (!anim._state) { if (anim._frame == anim._lastFrame - 1) { if (anim._loopType) { if (anim._loopType == 1) { anim._frame = anim._loopFrame; } else { continue; } } } else { anim._frame++; } anim._showFrame = anim._frame; if (anim._showFrame >= phaseCount) { anim._showFrame = phaseCount - 1; } showAnim(anim); } } } } void PrinceEngine::setBackAnim(Anim &backAnim) { int start = backAnim._basaData._start; if (start != -1) { backAnim._frame = start; backAnim._showFrame = start; backAnim._loopFrame = start; } int end = backAnim._basaData._end; if (end != -1) { backAnim._lastFrame = end; } backAnim._state = 0; } void PrinceEngine::showBackAnims() { for (int i = 0; i < kMaxBackAnims; i++) { BAS &seq = _backAnimList[i]._seq; int activeSubAnim = seq._currRelative; if (!_backAnimList[i].backAnims.empty()) { if (_backAnimList[i].backAnims[activeSubAnim]._animData != nullptr) { if (!_backAnimList[i].backAnims[activeSubAnim]._state) { seq._counter++; if (seq._type == 2) { if (!seq._currRelative) { if (seq._counter >= seq._data) { if (seq._anims > 2) { seq._currRelative = _randomSource.getRandomNumber(seq._anims - 2) + 1; activeSubAnim = seq._currRelative; seq._current = _backAnimList[i].backAnims[activeSubAnim]._basaData._num; } setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); seq._counter = 0; } } } if (seq._type == 3) { if (!seq._currRelative) { if (seq._counter < seq._data2) { continue; } else { setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); } } } if (_backAnimList[i].backAnims[activeSubAnim]._frame == _backAnimList[i].backAnims[activeSubAnim]._lastFrame - 1) { _backAnimList[i].backAnims[activeSubAnim]._frame = _backAnimList[i].backAnims[activeSubAnim]._loopFrame; switch (seq._type) { case 1: if (seq._anims > 1) { int rnd; do { rnd = _randomSource.getRandomNumber(seq._anims - 1); } while (rnd == seq._currRelative); seq._currRelative = rnd; seq._current = _backAnimList[i].backAnims[rnd]._basaData._num; activeSubAnim = rnd; setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); seq._counter = 0; } break; case 2: if (seq._currRelative) { seq._currRelative = 0; seq._current = _backAnimList[i].backAnims[0]._basaData._num; activeSubAnim = 0; setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); seq._counter = 0; } break; case 3: seq._currRelative = 0; seq._current = _backAnimList[i].backAnims[0]._basaData._num; seq._counter = 0; seq._data2 = _randomSource.getRandomNumber(seq._data - 1); continue; // for bug in original game break; default: break; } } else { _backAnimList[i].backAnims[activeSubAnim]._frame++; } _backAnimList[i].backAnims[activeSubAnim]._showFrame = _backAnimList[i].backAnims[activeSubAnim]._frame; showAnim(_backAnimList[i].backAnims[activeSubAnim]); } } } } } void PrinceEngine::removeSingleBackAnim(int slot) { if (!_backAnimList[slot].backAnims.empty()) { for (uint j = 0; j < _backAnimList[slot].backAnims.size(); j++) { if (_backAnimList[slot].backAnims[j]._animData != nullptr) { delete _backAnimList[slot].backAnims[j]._animData; _backAnimList[slot].backAnims[j]._animData = nullptr; } if (_backAnimList[slot].backAnims[j]._shadowData != nullptr) { delete _backAnimList[slot].backAnims[j]._shadowData; _backAnimList[slot].backAnims[j]._shadowData = nullptr; } } _backAnimList[slot].backAnims.clear(); _backAnimList[slot]._seq._currRelative = 0; } } void PrinceEngine::clearBackAnimList() { for (int i = 0; i < kMaxBackAnims; i++) { removeSingleBackAnim(i); } } void PrinceEngine::grabMap() { _graph->_frontScreen->copyFrom(*_roomBmp->getSurface()); showObjects(); runDrawNodes(); _graph->_mapScreen->copyFrom(*_graph->_frontScreen); } void PrinceEngine::initZoomIn(int slot) { freeZoomObject(slot); Object *object = _objList[slot]; if (object != nullptr) { Graphics::Surface *zoomSource = object->getSurface(); if (zoomSource != nullptr) { object->_flags |= 0x8000; object->_zoomSurface = new Graphics::Surface(); object->_zoomSurface->create(zoomSource->w, zoomSource->h, Graphics::PixelFormat::createFormatCLUT8()); object->_zoomSurface->fillRect(Common::Rect(zoomSource->w, zoomSource->h), 0xFF); object->_zoomTime = 20; } } } void PrinceEngine::initZoomOut(int slot) { freeZoomObject(slot); Object *object = _objList[slot]; if (object != nullptr) { Graphics::Surface *zoomSource = object->getSurface(); if (zoomSource != nullptr) { object->_flags |= 0x4000; object->_zoomSurface = new Graphics::Surface(); object->_zoomSurface->copyFrom(*zoomSource); object->_zoomTime = 10; } } } void PrinceEngine::doZoomIn(int slot) { Object *object = _objList[slot]; if (object != nullptr) { Graphics::Surface *orgSurface = object->getSurface(); if (orgSurface != nullptr) { byte *src1 = (byte *)orgSurface->getBasePtr(0, 0); byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); int x = 0; int surfaceHeight = orgSurface->h; for (int y = 0; y < surfaceHeight; y++) { byte *src2 = src1; byte *dst2 = dst1; int w = orgSurface->w - x; src2 += x; dst2 += x; while (w > 0) { int randVal = _randomSource.getRandomNumber(zoomInStep - 1); if (randVal < w) { *(dst2 + randVal) = *(src2 + randVal); src2 += zoomInStep; dst2 += zoomInStep; } else if (y + 1 != surfaceHeight) { *(dst1 + orgSurface->pitch + randVal - w) = *(src1 + orgSurface->pitch + randVal - w); } w -= zoomInStep; } x = -1 * w; src1 += orgSurface->pitch; dst1 += orgSurface->pitch; } } } } void PrinceEngine::doZoomOut(int slot) { Object *object = _objList[slot]; if (object != nullptr) { Graphics::Surface *orgSurface = object->getSurface(); if (orgSurface != nullptr) { byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); int x = 0; int surfaceHeight = orgSurface->h; for (int y = 0; y < surfaceHeight; y++) { byte *dst2 = dst1; int w = orgSurface->w - x; dst2 += x; while (w > 0) { int randVal = _randomSource.getRandomNumber(zoomInStep - 1); if (randVal < w) { *(dst2 + randVal) = 255; dst2 += zoomInStep; } else if (y + 1 != surfaceHeight) { *(dst1 + orgSurface->pitch + randVal - w) = 255; } w -= zoomInStep; } x = -1 * w; dst1 += orgSurface->pitch; } } } } void PrinceEngine::freeZoomObject(int slot) { Object *object = _objList[slot]; if (object != nullptr) { if (object->_zoomSurface != nullptr) { object->_zoomSurface->free(); delete object->_zoomSurface; object->_zoomSurface = nullptr; } } } void PrinceEngine::showObjects() { for (int i = 0; i < kMaxObjects; i++) { int nr = _objSlot[i]; if (nr != 0xFF) { Graphics::Surface *objSurface = nullptr; if ((_objList[nr]->_flags & 0x8000)) { _objList[nr]->_zoomTime--; if (!_objList[nr]->_zoomTime) { freeZoomObject(nr); _objList[nr]->_flags &= 0x7FFF; objSurface = _objList[nr]->getSurface(); } else { doZoomIn(nr); objSurface = _objList[nr]->_zoomSurface; } } else if ((_objList[nr]->_flags & 0x4000)) { _objList[nr]->_zoomTime--; if (!_objList[nr]->_zoomTime) { freeZoomObject(nr); _objList[nr]->_flags &= 0xBFFF; objSurface = _objList[nr]->getSurface(); } else { doZoomOut(nr); objSurface = _objList[nr]->_zoomSurface; } } else { objSurface = _objList[nr]->getSurface(); } if (objSurface != nullptr) { if (spriteCheck(objSurface->w, objSurface->h, _objList[nr]->_x, _objList[nr]->_y)) { int destX = _objList[nr]->_x - _picWindowX; int destY = _objList[nr]->_y - _picWindowY; DrawNode newDrawNode; newDrawNode.posX = destX; newDrawNode.posY = destY; newDrawNode.posZ = _objList[nr]->_z; newDrawNode.width = 0; newDrawNode.height = 0; newDrawNode.s = objSurface; newDrawNode.originalRoomSurface = nullptr; if ((_objList[nr]->_flags & 0x2000)) { newDrawNode.data = nullptr; newDrawNode.drawFunction = &_graph->drawBackSpriteDrawNode; } else { newDrawNode.data = _transTable; if (_flags->getFlagValue(Flags::NOANTIALIAS)) { newDrawNode.drawFunction = &_graph->drawTransparentDrawNode; } else { newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; } } _drawNodeList.push_back(newDrawNode); } if ((_objList[nr]->_flags & 1)) { checkMasks(_objList[nr]->_x, _objList[nr]->_y, objSurface->w, objSurface->h, _objList[nr]->_z); } } } } } void PrinceEngine::showParallax() { if (!_pscrList.empty()) { for (uint i = 0; i < _pscrList.size(); i++) { Graphics::Surface *pscrSurface = _pscrList[i]->getSurface(); if (pscrSurface != nullptr) { int x = _pscrList[i]->_x - (_pscrList[i]->_step * _picWindowX / 4); int y = _pscrList[i]->_y; int z = PScr::kPScrZ; if (spriteCheck(pscrSurface->w, pscrSurface->h, x, y)) { showSprite(pscrSurface, x, y, z); } } } } } bool PrinceEngine::compareDrawNodes(DrawNode d1, DrawNode d2) { if (d1.posZ < d2.posZ) { return true; } return false; } void PrinceEngine::runDrawNodes() { Common::sort(_drawNodeList.begin(), _drawNodeList.end(), compareDrawNodes); for (uint i = 0; i < _drawNodeList.size(); i++) { (*_drawNodeList[i].drawFunction)(_graph->_frontScreen, &_drawNodeList[i]); } _graph->change(); } void PrinceEngine::drawScreen() { if (!_showInventoryFlag || _inventoryBackgroundRemember) { clsMasks(); _mainHero->showHero(); _mainHero->scrollHero(); _mainHero->drawHero(); _secondHero->showHero(); _secondHero->_drawX -= _picWindowX; _secondHero->drawHero(); const Graphics::Surface *roomSurface; if (_locationNr != 50) { roomSurface = _roomBmp->getSurface(); } else { roomSurface = _graph->_mapScreen; } Graphics::Surface visiblePart; if (roomSurface) { visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h)); _graph->draw(_graph->_frontScreen, &visiblePart); } showBackAnims(); showNormAnims(); playNextFLCFrame(); showObjects(); if (roomSurface) { insertMasks(&visiblePart); } showParallax(); runDrawNodes(); _drawNodeList.clear(); if (!_inventoryBackgroundRemember && !_dialogFlag) { if (!_optionsFlag) { _selectedMob = checkMob(_graph->_frontScreen, _mobList, true); } showTexts(_graph->_frontScreen); checkOptions(); } else { _inventoryBackgroundRemember = false; } showPower(); getDebugger()->onFrame(); } else { displayInventory(); } } void PrinceEngine::blackPalette() { byte *paletteBackup = (byte *)malloc(256 * 3); byte *blackPalette1 = (byte *)malloc(256 * 3); int fadeStep = kFadeStep - 1; for (int i = 0; i < kFadeStep; i++) { _system->getPaletteManager()->grabPalette(paletteBackup, 0, 256); for (int j = 0; j < 256; j++) { blackPalette1[3 * j] = paletteBackup[3 * j] * fadeStep / 4; blackPalette1[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4; blackPalette1[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4; } fadeStep--; _graph->setPalette(blackPalette1); _system->updateScreen(); Common::Event event; Common::EventManager *eventMan = _system->getEventManager(); eventMan->pollEvent(event); if (shouldQuit()) { free(paletteBackup); free(blackPalette1); return; } pausePrinceEngine(); } free(paletteBackup); free(blackPalette1); } void PrinceEngine::setPalette(const byte *palette) { if (palette != nullptr) { byte *blackPalette_ = (byte *)malloc(256 * 3); int fadeStep = 0; for (int i = 0; i <= kFadeStep; i++) { for (int j = 0; j < 256; j++) { blackPalette_[3 * j] = palette[3 * j] * fadeStep / 4; blackPalette_[3 * j + 1] = palette[3 * j + 1] * fadeStep / 4; blackPalette_[3 * j + 2] = palette[3 * j + 2] * fadeStep / 4; } fadeStep++; _graph->setPalette(blackPalette_); _system->updateScreen(); Common::Event event; Common::EventManager *eventMan = _system->getEventManager(); eventMan->pollEvent(event); if (shouldQuit()) { _graph->setPalette(palette); free(blackPalette_); return; } pausePrinceEngine(); } _graph->setPalette(palette); free(blackPalette_); } } void PrinceEngine::doTalkAnim(int animNumber, int slot, AnimType animType) { Text &text = _textSlots[slot]; int lines = calcTextLines((const char *)_interpreter->getString()); int time = lines * 30; if (animType == kNormalAnimation) { Anim &normAnim = _normAnimList[animNumber]; if (normAnim._animData != nullptr) { if (!normAnim._state) { if (normAnim._currW && normAnim._currH) { text._color = _flags->getFlagValue(Flags::KOLOR); text._x = normAnim._currX + normAnim._currW / 2; text._y = normAnim._currY - 10; } } } } else if (animType == kBackgroundAnimation) { if (!_backAnimList[animNumber].backAnims.empty()) { int currAnim = _backAnimList[animNumber]._seq._currRelative; Anim &backAnim = _backAnimList[animNumber].backAnims[currAnim]; if (backAnim._animData != nullptr) { if (!backAnim._state) { if (backAnim._currW && backAnim._currH) { text._color = _flags->getFlagValue(Flags::KOLOR); text._x = backAnim._currX + backAnim._currW / 2; text._y = backAnim._currY - 10; } } } } } else { error("doTalkAnim() - wrong animType: %d", animType); } text._time = time; if (getLanguage() == Common::DE_DEU) { correctStringDEU((char *)_interpreter->getString()); } text._str = (const char *)_interpreter->getString(); _interpreter->increaseString(); } void PrinceEngine::freeNormAnim(int slot) { if (!_normAnimList.empty()) { _normAnimList[slot]._state = 1; if (_normAnimList[slot]._animData != nullptr) { delete _normAnimList[slot]._animData; _normAnimList[slot]._animData = nullptr; } if (_normAnimList[slot]._shadowData != nullptr) { delete _normAnimList[slot]._shadowData; _normAnimList[slot]._shadowData = nullptr; } } } void PrinceEngine::freeAllNormAnims() { for (int i = 0; i < kMaxNormAnims; i++) { freeNormAnim(i); } } } // End of namespace Prince