aboutsummaryrefslogtreecommitdiff
path: root/engines/prince/prince.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/prince/prince.cpp')
-rw-r--r--engines/prince/prince.cpp4737
1 files changed, 4737 insertions, 0 deletions
diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp
new file mode 100644
index 0000000000..51748b30d9
--- /dev/null
+++ b/engines/prince/prince.cpp
@@ -0,0 +1,4737 @@
+/* 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/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/debug.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/random.h"
+#include "common/fs.h"
+#include "common/keyboard.h"
+#include "common/substream.h"
+#include "common/str.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "graphics/pixelformat.h"
+
+#include "engines/util.h"
+#include "engines/advancedDetector.h"
+
+#include "audio/audiostream.h"
+
+#include "prince/prince.h"
+#include "prince/font.h"
+#include "prince/graphics.h"
+#include "prince/script.h"
+#include "prince/debugger.h"
+#include "prince/object.h"
+#include "prince/mob.h"
+#include "prince/sound.h"
+#include "prince/variatxt.h"
+#include "prince/flags.h"
+#include "prince/font.h"
+#include "prince/mhwanh.h"
+#include "prince/cursor.h"
+#include "prince/archive.h"
+#include "prince/hero.h"
+#include "prince/resource.h"
+#include "prince/animation.h"
+#include "prince/option_text.h"
+#include "prince/curve_values.h"
+
+namespace Prince {
+
+void PrinceEngine::debugEngine(const char *s, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, STRINGBUFLEN, s, va);
+ va_end(va);
+
+ debug("Prince::Engine %s", buf);
+}
+
+PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) :
+ Engine(syst), _gameDescription(gameDesc), _graph(nullptr), _script(nullptr), _interpreter(nullptr), _flags(nullptr),
+ _locationNr(0), _debugger(nullptr), _midiPlayer(nullptr), _room(nullptr),
+ _cursor1(nullptr), _cursor2(nullptr), _cursor3(nullptr), _font(nullptr),
+ _suitcaseBmp(nullptr), _roomBmp(nullptr), _cursorNr(0), _picWindowX(0), _picWindowY(0), _randomSource("prince"),
+ _invLineX(134), _invLineY(176), _invLine(5), _invLines(3), _invLineW(70), _invLineH(76), _maxInvW(72), _maxInvH(76),
+ _invLineSkipX(2), _invLineSkipY(3), _showInventoryFlag(false), _inventoryBackgroundRemember(false),
+ _mst_shadow(0), _mst_shadow2(0), _candleCounter(0), _invX1(53), _invY1(18), _invWidth(536), _invHeight(438),
+ _invCurInside(false), _optionsFlag(false), _optionEnabled(0), _invExamY(120), _invMaxCount(2), _invCounter(0),
+ _optionsMob(-1), _currentPointerNumber(1), _selectedMob(-1), _selectedItem(0), _selectedMode(0),
+ _optionsWidth(210), _optionsHeight(170), _invOptionsWidth(210), _invOptionsHeight(130), _optionsStep(20),
+ _invOptionsStep(20), _optionsNumber(7), _invOptionsNumber(5), _optionsColor1(236), _optionsColor2(252),
+ _dialogWidth(600), _dialogHeight(0), _dialogLineSpace(10), _dialogColor1(220), _dialogColor2(223),
+ _dialogFlag(false), _dialogLines(0), _dialogText(nullptr), _mouseFlag(1),
+ _roomPathBitmap(nullptr), _roomPathBitmapTemp(nullptr), _coordsBufEnd(nullptr), _coordsBuf(nullptr), _coords(nullptr),
+ _traceLineLen(0), _rembBitmapTemp(nullptr), _rembBitmap(nullptr), _rembMask(0), _rembX(0), _rembY(0), _fpX(0), _fpY(0),
+ _checkBitmapTemp(nullptr), _checkBitmap(nullptr), _checkMask(0), _checkX(0), _checkY(0), _traceLineFirstPointFlag(false),
+ _tracePointFirstPointFlag(false), _coordsBuf2(nullptr), _coords2(nullptr), _coordsBuf3(nullptr), _coords3(nullptr),
+ _shanLen(0), _directionTable(nullptr), _currentMidi(0), _lightX(0), _lightY(0), _curveData(nullptr), _curvPos(0),
+ _creditsData(nullptr), _creditsDataSize(0), _currentTime(0), _zoomBitmap(nullptr), _shadowBitmap(nullptr), _transTable(nullptr),
+ _flcFrameSurface(nullptr), _shadScaleValue(0), _shadLineLen(0), _scaleValue(0), _dialogImage(nullptr) {
+
+ // Debug/console setup
+ DebugMan.addDebugChannel(DebugChannel::kScript, "script", "Prince Script debug channel");
+ DebugMan.addDebugChannel(DebugChannel::kEngine, "engine", "Prince Engine debug channel");
+
+ DebugMan.enableDebugChannel("script");
+
+ memset(_audioStream, 0, sizeof(_audioStream));
+
+ gDebugLevel = 10;
+}
+
+PrinceEngine::~PrinceEngine() {
+ DebugMan.clearAllDebugChannels();
+
+ delete _rnd;
+ delete _debugger;
+ delete _cursor1;
+ delete _cursor3;
+ delete _midiPlayer;
+ delete _script;
+ delete _flags;
+ delete _interpreter;
+ delete _font;
+ delete _roomBmp;
+ delete _suitcaseBmp;
+ delete _variaTxt;
+ free(_talkTxt);
+ free(_invTxt);
+ free(_dialogDat);
+ delete _graph;
+ delete _room;
+
+ if (_cursor2 != nullptr) {
+ _cursor2->free();
+ delete _cursor2;
+ }
+
+ for (uint i = 0; i < _objList.size(); i++) {
+ delete _objList[i];
+ }
+ _objList.clear();
+
+ free(_objSlot);
+
+ for (uint32 i = 0; i < _pscrList.size(); i++) {
+ delete _pscrList[i];
+ }
+ _pscrList.clear();
+
+ for (uint i = 0; i < _maskList.size(); i++) {
+ free(_maskList[i]._data);
+ }
+ _maskList.clear();
+
+ _drawNodeList.clear();
+
+ clearBackAnimList();
+ _backAnimList.clear();
+
+ freeAllNormAnims();
+ _normAnimList.clear();
+
+ for (uint i = 0; i < _allInvList.size(); i++) {
+ _allInvList[i]._surface->free();
+ delete _allInvList[i]._surface;
+ }
+ _allInvList.clear();
+
+ _optionsPic->free();
+ delete _optionsPic;
+
+ _optionsPicInInventory->free();
+ delete _optionsPicInInventory;
+
+ for (uint i = 0; i < _mainHero->_moveSet.size(); i++) {
+ delete _mainHero->_moveSet[i];
+ }
+
+ for (uint i = 0; i < _secondHero->_moveSet.size(); i++) {
+ delete _secondHero->_moveSet[i];
+ }
+
+ delete _mainHero;
+ delete _secondHero;
+
+ free(_roomPathBitmap);
+ free(_roomPathBitmapTemp);
+ free(_coordsBuf);
+
+ _mobPriorityList.clear();
+
+ freeAllSamples();
+
+ free(_zoomBitmap);
+ free(_shadowBitmap);
+ free(_transTable);
+
+ free(_curveData);
+
+ free(_shadowLine);
+
+ free(_creditsData);
+
+ if (_dialogImage != nullptr) {
+ _dialogImage->free();
+ delete _dialogImage;
+ }
+}
+
+GUI::Debugger *PrinceEngine::getDebugger() {
+ return _debugger;
+}
+
+void PrinceEngine::init() {
+
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+
+ debugEngine("Adding all path: %s", gameDataDir.getPath().c_str());
+
+ PtcArchive *all = new PtcArchive();
+ if (!all->open("all/databank.ptc"))
+ error("Can't open all/databank.ptc");
+
+ PtcArchive *voices = new PtcArchive();
+ if (!voices->open("data/voices/databank.ptc"))
+ error("Can't open data/voices/databank.ptc");
+
+ PtcArchive *sound = new PtcArchive();
+ if (!sound->open("sound/databank.ptc"))
+ error("Can't open sound/databank.ptc");
+
+ SearchMan.addSubDirectoryMatching(gameDataDir, "all");
+
+ SearchMan.add("all", all);
+ SearchMan.add("voices", voices);
+ SearchMan.add("sound", sound);
+
+ _graph = new GraphicsMan(this);
+
+ _rnd = new Common::RandomSource("prince");
+
+ _midiPlayer = new MusicPlayer(this);
+
+ if (getLanguage() == Common::DE_DEU) {
+ _font = new Font();
+ Resource::loadResource(_font, "font3.raw", true);
+ } else {
+ _font = new Font();
+ Resource::loadResource(_font, "font1.raw", true);
+ }
+
+ _suitcaseBmp = new MhwanhDecoder();
+ Resource::loadResource(_suitcaseBmp, "walizka", true);
+
+ _script = new Script(this);
+ Resource::loadResource(_script, "skrypt.dat", true);
+
+ _flags = new InterpreterFlags();
+ _interpreter = new Interpreter(this, _script, _flags);
+
+ _debugger = new Debugger(this, _flags);
+
+ _variaTxt = new VariaTxt();
+ Resource::loadResource(_variaTxt, "variatxt.dat", true);
+
+ _cursor1 = new Cursor();
+ Resource::loadResource(_cursor1, "mouse1.cur", true);
+
+ _cursor3 = new Cursor();
+ Resource::loadResource(_cursor3, "mouse2.cur", true);
+
+ Common::SeekableReadStream *talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat");
+ if (!talkTxtStream) {
+ error("Can't load talkTxtStream");
+ return;
+ }
+ _talkTxtSize = talkTxtStream->size();
+ _talkTxt = (byte *)malloc(_talkTxtSize);
+ talkTxtStream->read(_talkTxt, _talkTxtSize);
+
+ delete talkTxtStream;
+
+ Common::SeekableReadStream *invTxtStream = SearchMan.createReadStreamForMember("invtxt.dat");
+ if (!invTxtStream) {
+ error("Can't load invTxtStream");
+ return;
+ }
+ _invTxtSize = invTxtStream->size();
+ _invTxt = (byte *)malloc(_invTxtSize);
+ invTxtStream->read(_invTxt, _invTxtSize);
+
+ delete invTxtStream;
+
+ loadAllInv();
+
+ Common::SeekableReadStream *dialogDatStream = SearchMan.createReadStreamForMember("dialog.dat");
+ if (!dialogDatStream) {
+ error("Can't load dialogDatStream");
+ return;
+ }
+ _dialogDatSize = dialogDatStream->size();
+ _dialogDat = (byte *)malloc(_dialogDatSize);
+ dialogDatStream->read(_dialogDat, _dialogDatSize);
+
+ delete dialogDatStream;
+
+ _optionsPic = new Graphics::Surface();
+ _optionsPic->create(_optionsWidth, _optionsHeight, Graphics::PixelFormat::createFormatCLUT8());
+ Common::Rect picRect(0, 0, _optionsWidth, _optionsHeight);
+ _optionsPic->fillRect(picRect, _graph->kShadowColor);
+
+ _optionsPicInInventory = new Graphics::Surface();
+ _optionsPicInInventory->create(_invOptionsWidth, _invOptionsHeight, Graphics::PixelFormat::createFormatCLUT8());
+ Common::Rect invPicRect(0, 0, _invOptionsWidth, _invOptionsHeight);
+ _optionsPicInInventory->fillRect(invPicRect, _graph->kShadowColor);
+
+ _roomBmp = new Image::BitmapDecoder();
+
+ _room = new Room();
+
+ _mainHero = new Hero(this, _graph);
+ _secondHero = new Hero(this, _graph);
+ _secondHero->_maxBoredom = 140;
+ _secondHero->loadAnimSet(3);
+
+ _roomPathBitmap = (byte *)malloc(kPathBitmapLen);
+ _roomPathBitmapTemp = (byte *)malloc(kPathBitmapLen);
+ _coordsBuf = (byte *)malloc(kTracePts * 4);
+ _coords = _coordsBuf;
+ _coordsBufEnd = _coordsBuf + kTracePts * 4 - 4;
+
+ BackgroundAnim tempBackAnim;
+ tempBackAnim._seq._currRelative = 0;
+ for (int i = 0; i < kMaxBackAnims; i++) {
+ _backAnimList.push_back(tempBackAnim);
+ }
+
+ Anim tempAnim;
+ tempAnim._animData = nullptr;
+ tempAnim._shadowData = nullptr;
+ for (int i = 0; i < kMaxNormAnims; i++) {
+ _normAnimList.push_back(tempAnim);
+ }
+
+ _objSlot = (uint16 *)malloc(kMaxObjects * sizeof(uint16));
+ for (int i = 0; i < kMaxObjects; i++) {
+ _objSlot[i] = 0xFF;
+ }
+
+ _zoomBitmap = (byte *)malloc(kZoomBitmapLen);
+ _shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize);
+ _transTable = (byte *)malloc(kTransTableSize);
+
+ _curveData = (int16 *)malloc(2 * kCurveLen * sizeof(int16));
+
+ _shadowLine = (byte *)malloc(kShadowLineArraySize);
+
+ Common::SeekableReadStream *creditsDataStream = SearchMan.createReadStreamForMember("credits.dat");
+ if (!creditsDataStream) {
+ error("Can't load creditsDataStream");
+ return;
+ }
+ _creditsDataSize = creditsDataStream->size();
+ _creditsData = (byte *)malloc(_creditsDataSize);
+ creditsDataStream->read(_creditsData, _creditsDataSize);
+ delete creditsDataStream;
+}
+
+void PrinceEngine::showLogo() {
+ MhwanhDecoder logo;
+ _system->delayMillis(1000 / kFPS * 20);
+ if (Resource::loadResource(&logo, "logo.raw", true)) {
+ loadSample(0, "LOGO.WAV");
+ playSample(0, 0);
+ _graph->draw(_graph->_frontScreen, logo.getSurface());
+ _graph->change();
+ _graph->update(_graph->_frontScreen);
+ setPalette(logo.getPalette());
+ _system->delayMillis(1000 / kFPS * 70);
+ }
+}
+
+Common::Error PrinceEngine::run() {
+
+ init();
+
+ showLogo();
+
+ mainLoop();
+
+ return Common::kNoError;
+}
+
+bool AnimListItem::loadFromStream(Common::SeekableReadStream &stream) {
+ int32 pos = stream.pos();
+
+ uint16 type = stream.readUint16LE();
+ if (type == 0xFFFF) {
+ return false;
+ }
+ _type = type;
+ _fileNumber = stream.readUint16LE();
+ _startPhase = stream.readUint16LE();
+ _endPhase = stream.readUint16LE();
+ _loopPhase = stream.readUint16LE();
+ _x = stream.readSint16LE();
+ _y = stream.readSint16LE();
+ _loopType = stream.readUint16LE();
+ _nextAnim = stream.readUint16LE();
+ _flags = stream.readUint16LE();
+
+ //debug("AnimListItem type %d, fileNumber %d, x %d, y %d, flags %d", _type, _fileNumber, _x, _y, _flags);
+ //debug("startPhase %d, endPhase %d, loopPhase %d", _startPhase, _endPhase, _loopPhase);
+
+ // 32 byte aligment
+ stream.seek(pos + 32);
+
+ return true;
+}
+
+bool PrinceEngine::loadLocation(uint16 locationNr) {
+
+ blackPalette();
+
+ _flicPlayer.close();
+
+ memset(_textSlots, 0, sizeof(_textSlots));
+ freeAllSamples();
+
+ debugEngine("PrinceEngine::loadLocation %d", locationNr);
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.remove(Common::String::format("%02d", _locationNr));
+
+ _locationNr = locationNr;
+ _debugger->_locationNr = locationNr;
+
+ _flags->setFlagValue(Flags::CURRROOM, _locationNr);
+ _interpreter->stopBg();
+
+ changeCursor(0);
+
+ const Common::String locationNrStr = Common::String::format("%02d", _locationNr);
+ debugEngine("loadLocation %s", locationNrStr.c_str());
+
+ PtcArchive *locationArchive = new PtcArchive();
+ if (!locationArchive->open(locationNrStr + "/databank.ptc"))
+ error("Can't open location %s", locationNrStr.c_str());
+
+ SearchMan.add(locationNrStr, locationArchive);
+
+ loadMusic(_locationNr);
+
+ // load location background, replace old one
+ Resource::loadResource(_roomBmp, "room", true);
+ if (_roomBmp->getSurface()) {
+ _sceneWidth = _roomBmp->getSurface()->w;
+ }
+
+ loadZoom(_zoomBitmap, kZoomBitmapLen, "zoom");
+ loadShadow(_shadowBitmap, kShadowBitmapSize, "shadow", "shadow2");
+ loadTrans(_transTable, "trans");
+ loadPath("path");
+
+ for (uint32 i = 0; i < _pscrList.size(); i++) {
+ delete _pscrList[i];
+ }
+ _pscrList.clear();
+ Resource::loadResource(_pscrList, "pscr.lst", false);
+
+ loadMobPriority("mobpri");
+
+ _mobList.clear();
+ if (getLanguage() == Common::DE_DEU) {
+ const Common::String mobLstName = Common::String::format("mob%02d.lst", _locationNr);
+ debug("name: %s", mobLstName.c_str());
+ Resource::loadResource(_mobList, mobLstName.c_str(), false);
+ } else {
+ Resource::loadResource(_mobList, "mob.lst", false);
+ }
+
+ _animList.clear();
+ Resource::loadResource(_animList, "anim.lst", false);
+
+ for (uint32 i = 0; i < _objList.size(); i++) {
+ delete _objList[i];
+ }
+ _objList.clear();
+ Resource::loadResource(_objList, "obj.lst", false);
+
+ _room->loadRoom(_script->getRoomOffset(_locationNr));
+
+ for (uint i = 0; i < _maskList.size(); i++) {
+ free(_maskList[i]._data);
+ }
+ _maskList.clear();
+ _script->loadAllMasks(_maskList, _room->_nak);
+
+ _picWindowX = 0;
+
+ _lightX = _script->getLightX(_locationNr);
+ _lightY = _script->getLightY(_locationNr);
+ setShadowScale(_script->getShadowScale(_locationNr));
+
+ for (uint i = 0; i < _mobList.size(); i++) {
+ _mobList[i]._visible = _script->getMobVisible(_room->_mobs, i);
+ }
+
+ _script->installObjects(_room->_obj);
+
+ freeAllNormAnims();
+
+ clearBackAnimList();
+ _script->installBackAnims(_backAnimList, _room->_backAnim);
+
+ _graph->makeShadowTable(70, _graph->_shadowTable70);
+ _graph->makeShadowTable(50, _graph->_shadowTable50);
+
+ _mainHero->freeOldMove();
+ _secondHero->freeOldMove();
+
+ _mainHero->scrollHero();
+
+ return true;
+}
+
+void PrinceEngine::setShadowScale(int32 shadowScale) {
+ shadowScale = 100 - shadowScale;
+ if (!shadowScale) {
+ _shadScaleValue = 10000;
+ } else {
+ _shadScaleValue = 10000 / shadowScale;
+ }
+}
+
+void PrinceEngine::plotShadowLinePoint(int x, int y, int color, void *data) {
+ PrinceEngine *vm = (PrinceEngine *)data;
+ WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4], x);
+ WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4 + 2], y);
+ vm->_shadLineLen++;
+}
+
+void PrinceEngine::changeCursor(uint16 curId) {
+ _debugger->_cursorNr = curId;
+ _mouseFlag = curId;
+ _flags->setFlagValue(Flags::MOUSEENABLED, curId);
+
+ const Graphics::Surface *curSurface = nullptr;
+
+ switch (curId) {
+ case 0:
+ CursorMan.showMouse(false);
+ _optionsFlag = 0;
+ _selectedMob = -1;
+ return;
+ case 1:
+ curSurface = _cursor1->getSurface();
+ break;
+ case 2:
+ curSurface = _cursor2;
+ break;
+ case 3:
+ curSurface = _cursor3->getSurface();
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639);
+ mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170);
+ _system->warpMouse(mousePos.x, mousePos.y);
+ break;
+ }
+
+ CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255);
+ CursorMan.replaceCursor(
+ curSurface->getBasePtr(0, 0),
+ curSurface->w, curSurface->h,
+ 0, 0,
+ 255, false,
+ &curSurface->format
+ );
+ CursorMan.showMouse(true);
+}
+
+void PrinceEngine::makeInvCursor(int itemNr) {
+ const Graphics::Surface *cur1Surface = _cursor1->getSurface();
+ int cur1W = cur1Surface->w;
+ int cur1H = cur1Surface->h;
+ const Common::Rect cur1Rect(0, 0, cur1W, cur1H);
+
+ const Graphics::Surface *itemSurface = _allInvList[itemNr].getSurface();
+ int itemW = itemSurface->w;
+ int itemH = itemSurface->h;
+
+ int cur2W = cur1W + itemW / 2;
+ int cur2H = cur1H + itemH / 2;
+
+ if (_cursor2 != nullptr) {
+ _cursor2->free();
+ delete _cursor2;
+ }
+ _cursor2 = new Graphics::Surface();
+ _cursor2->create(cur2W, cur2H, Graphics::PixelFormat::createFormatCLUT8());
+ Common::Rect cur2Rect(0, 0, cur2W, cur2H);
+ _cursor2->fillRect(cur2Rect, 255);
+ _cursor2->copyRectToSurface(*cur1Surface, 0, 0, cur1Rect);
+
+ byte *src1 = (byte *)itemSurface->getBasePtr(0, 0);
+ byte *dst1 = (byte *)_cursor2->getBasePtr(cur1W, cur1H);
+
+ if (itemH % 2) {
+ itemH--;
+ }
+ if (itemW % 2) {
+ itemW--;
+ }
+
+ for (int y = 0; y < itemH; y++) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ if (y % 2 == 0) {
+ for (int x = 0; x < itemW; x++, src2++) {
+ if (x % 2 == 0) {
+ if (*src2) {
+ *dst2 = *src2;
+ } else {
+ *dst2 = 255;
+ }
+ dst2++;
+ }
+ }
+ dst1 += _cursor2->pitch;
+ }
+ src1 += itemSurface->pitch;
+ }
+}
+
+bool PrinceEngine::loadMusic(int musNumber) {
+ uint8 midiNumber = MusicPlayer::_musRoomTable[musNumber];
+ if (midiNumber) {
+ if (midiNumber != 100) {
+ if (_currentMidi != midiNumber) {
+ _currentMidi = midiNumber;
+ const char *musName = MusicPlayer::_musTable[_currentMidi];
+ _midiPlayer->loadMidi(musName);
+ }
+ }
+ } else {
+ stopMusic();
+ }
+ return true;
+}
+
+void PrinceEngine::stopMusic() {
+ if (_midiPlayer->isPlaying()) {
+ _midiPlayer->stop();
+ }
+}
+
+bool PrinceEngine::playNextFLCFrame() {
+ if (!_flicPlayer.isVideoLoaded())
+ return false;
+
+ const Graphics::Surface *s = _flicPlayer.decodeNextFrame();
+ if (s) {
+ _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, s, 255);
+ _graph->change();
+ _flcFrameSurface = s;
+ } else if (_flicLooped) {
+ _flicPlayer.rewind();
+ playNextFLCFrame();
+ } else if (_flcFrameSurface) {
+ _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, _flcFrameSurface, 255);
+ _graph->change();
+ }
+
+ return true;
+}
+
+void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) {
+ if (_audioStream[sampleId]) {
+ if (_mixer->isSoundIDActive(sampleId)) {
+ return;
+ }
+ _audioStream[sampleId]->rewind();
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+ }
+}
+
+void PrinceEngine::stopSample(uint16 sampleId) {
+ _mixer->stopID(sampleId);
+}
+
+void PrinceEngine::stopAllSamples() {
+ _mixer->stopAll();
+}
+
+void PrinceEngine::freeSample(uint16 sampleId) {
+ stopSample(sampleId);
+ if (_audioStream[sampleId] != nullptr) {
+ delete _audioStream[sampleId];
+ _audioStream[sampleId] = nullptr;
+ }
+}
+
+void PrinceEngine::freeAllSamples() {
+ for (int sampleId = 0; sampleId < kMaxSamples; sampleId++) {
+ freeSample(sampleId);
+ }
+}
+
+bool PrinceEngine::loadSample(uint32 sampleSlot, const Common::String &streamName) {
+ // FIXME: This is just a workaround streamName is a path
+ // SOUND\\SCIERKA1.WAV for now only last path component is used
+ Common::String normalizedPath = lastPathComponent(streamName, '\\');
+
+ // WALKAROUND: Wrong name in script, not existing sound in data files
+ if (!normalizedPath.compareTo("9997BEKA.WAV")) {
+ return 0;
+ }
+
+ debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str());
+
+ freeSample(sampleSlot);
+ Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(normalizedPath);
+ if (sampleStream == nullptr) {
+ delete sampleStream;
+ error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot);
+ }
+ _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO);
+ delete sampleStream;
+ return true;
+}
+
+bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) {
+ debugEngine("Loading wav %s slot %d", streamName.c_str(), slot);
+
+ if (slot > kMaxTexts) {
+ error("Text slot bigger than MAXTEXTS %d", kMaxTexts);
+ return false;
+ }
+
+ freeSample(sampleSlot);
+ Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(streamName);
+ if (sampleStream == nullptr) {
+ debug("Can't open %s", streamName.c_str());
+ return false;
+ }
+
+ uint32 id = sampleStream->readUint32LE();
+ if (id != MKTAG('F', 'F', 'I', 'R')) {
+ error("It's not RIFF file %s", streamName.c_str());
+ return false;
+ }
+
+ sampleStream->skip(0x20);
+ id = sampleStream->readUint32LE();
+ if (id != MKTAG('a', 't', 'a', 'd')) {
+ error("No data section in %s id %04x", streamName.c_str(), id);
+ return false;
+ }
+
+ id = sampleStream->readUint32LE();
+ debugEngine("SetVoice slot %d time %04x", slot, id);
+ id <<= 3;
+ id /= 22050;
+ id += 2;
+
+ _textSlots[slot]._time = id;
+ if (!slot) {
+ _mainHero->_talkTime = id;
+ } else if (slot == 1) {
+ _secondHero->_talkTime = id;
+ }
+
+ debugEngine("SetVoice slot %d time %04x", slot, id);
+ sampleStream->seek(SEEK_SET);
+ _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO);
+ delete sampleStream;
+ return true;
+}
+
+void PrinceEngine::setVoice(uint16 slot, uint32 sampleSlot, uint16 flag) {
+ Common::String sampleName;
+ uint32 currentString = _interpreter->getCurrentString();
+
+ if (currentString >= 80000) {
+ uint32 nr = currentString - 80000;
+ sampleName = Common::String::format("%02d0%02d-%02d.WAV", nr / 100, nr % 100, flag);
+ } else if (currentString >= 70000) {
+ sampleName = Common::String::format("inv%02d-01.WAV", currentString - 70000);
+ } else if (currentString >= 60000) {
+ sampleName = Common::String::format("M%04d-%02d.WAV", currentString - 60000, flag);
+ } else if (currentString >= 2000) {
+ return;
+ } else if (flag >= 100) {
+ sampleName = Common::String::format("%03d-%03d.WAV", currentString, flag);
+ } else {
+ sampleName = Common::String::format("%03d-%02d.WAV", currentString, flag);
+ }
+
+ loadVoice(slot, sampleSlot, sampleName);
+}
+
+bool PrinceEngine::loadAnim(uint16 animNr, bool loop) {
+ Common::String streamName = Common::String::format("AN%02d", animNr);
+ Common::SeekableReadStream *flicStream = SearchMan.createReadStreamForMember(streamName);
+
+ if (!flicStream) {
+ error("Can't open %s", streamName.c_str());
+ return false;
+ }
+
+ if (!_flicPlayer.loadStream(flicStream)) {
+ error("Can't load flic stream %s", streamName.c_str());
+ }
+
+ debugEngine("%s loaded", streamName.c_str());
+ _flicLooped = loop;
+ _flicPlayer.start();
+ playNextFLCFrame();
+ return true;
+}
+
+bool PrinceEngine::loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
+ if (!stream) {
+ delete stream;
+ return false;
+ }
+ if (stream->read(zoomBitmap, dataSize) != dataSize) {
+ free(zoomBitmap);
+ delete stream;
+ return false;
+ }
+ delete stream;
+ return true;
+}
+
+bool PrinceEngine::loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName1);
+ if (!stream) {
+ delete stream;
+ return false;
+ }
+
+ if (stream->read(shadowBitmap, dataSize) != dataSize) {
+ free(shadowBitmap);
+ delete stream;
+ return false;
+ }
+
+ Common::SeekableReadStream *stream2 = SearchMan.createReadStreamForMember(resourceName2);
+ if (!stream2) {
+ delete stream;
+ delete stream2;
+ return false;
+ }
+
+ byte *shadowBitmap2 = shadowBitmap + dataSize;
+ if (stream2->read(shadowBitmap2, dataSize) != dataSize) {
+ free(shadowBitmap);
+ delete stream;
+ delete stream2;
+ return false;
+ }
+
+ delete stream;
+ delete stream2;
+ return true;
+}
+
+bool PrinceEngine::loadTrans(byte *transTable, const char *resourceName) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
+ if (!stream) {
+ delete stream;
+ for (int i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ transTable[i * 256 + j] = j;
+ }
+ }
+ return true;
+ }
+ if (stream->read(transTable, kTransTableSize) != kTransTableSize) {
+ delete stream;
+ return false;
+ }
+ delete stream;
+ return true;
+}
+
+bool PrinceEngine::loadPath(const char *resourceName) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
+ if (!stream) {
+ delete stream;
+ return false;
+ }
+ if (stream->read(_roomPathBitmap, kPathBitmapLen) != kPathBitmapLen) {
+ delete stream;
+ return false;
+ }
+ delete stream;
+ return true;
+}
+
+bool PrinceEngine::loadAllInv() {
+ for (int i = 0; i < kMaxInv; i++) {
+ InvItem tempInvItem;
+
+ const Common::String invStreamName = Common::String::format("INV%02d", i);
+ Common::SeekableReadStream *invStream = SearchMan.createReadStreamForMember(invStreamName);
+ if (!invStream) {
+ delete invStream;
+ return true;
+ }
+
+ tempInvItem._x = invStream->readUint16LE();
+ tempInvItem._y = invStream->readUint16LE();
+ int width = invStream->readUint16LE();
+ int height = invStream->readUint16LE();
+ tempInvItem._surface = new Graphics::Surface();
+ tempInvItem._surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int h = 0; h < tempInvItem._surface->h; h++) {
+ invStream->read(tempInvItem._surface->getBasePtr(0, h), tempInvItem._surface->w);
+ }
+
+ _allInvList.push_back(tempInvItem);
+ delete invStream;
+ }
+
+ return true;
+}
+
+bool PrinceEngine::loadMobPriority(const char *resourceName) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
+ if (!stream) {
+ delete stream;
+ return false;
+ }
+
+ _mobPriorityList.clear();
+ int mobId;
+ while (1) {
+ mobId = stream->readUint32LE();
+ if (mobId == 0xFFFFFFFF) {
+ break;
+ }
+ _mobPriorityList.push_back(mobId);
+ }
+ delete stream;
+ return true;
+}
+
+void PrinceEngine::keyHandler(Common::Event event) {
+ uint16 nChar = event.kbd.keycode;
+ switch (nChar) {
+ case Common::KEYCODE_d:
+ if (event.kbd.hasFlags(Common::KBD_CTRL)) {
+ getDebugger()->attach();
+ }
+ break;
+ case Common::KEYCODE_z:
+ if (_flags->getFlagValue(Flags::POWERENABLED)) {
+ _flags->setFlagValue(Flags::MBFLAG, 1);
+ }
+ break;
+ case Common::KEYCODE_x:
+ if (_flags->getFlagValue(Flags::POWERENABLED)) {
+ _flags->setFlagValue(Flags::MBFLAG, 2);
+ }
+ break;
+ case Common::KEYCODE_ESCAPE:
+ _flags->setFlagValue(Flags::ESCAPED2, 1);
+ break;
+ }
+}
+
+int PrinceEngine::getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY) {
+
+ Common::Point pointPos(posX, posY);
+
+ int mobListSize;
+ if (usePriorityList) {
+ mobListSize = _mobPriorityList.size();
+ } else {
+ mobListSize = mobList.size();
+ }
+
+ for (int mobNumber = 0; mobNumber < mobListSize; mobNumber++) {
+ Mob *mob = nullptr;
+ if (usePriorityList) {
+ mob = &mobList[_mobPriorityList[mobNumber]];
+ } else {
+ mob = &mobList[mobNumber];
+ }
+
+ if (mob->_visible) {
+ continue;
+ }
+
+ int type = mob->_type & 7;
+ switch (type) {
+ case 0:
+ case 1:
+ //normal_mob
+ if (!mob->_rect.contains(pointPos)) {
+ continue;
+ }
+ break;
+ case 3:
+ //mob_obj
+ if (mob->_mask < kMaxObjects) {
+ int nr = _objSlot[mob->_mask];
+ if (nr != 0xFF) {
+ Object &obj = *_objList[nr];
+ Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height);
+ if (objectRect.contains(pointPos)) {
+ Graphics::Surface *objSurface = obj.getSurface();
+ byte *pixel = (byte *)objSurface->getBasePtr(posX - obj._x, posY - obj._y);
+ if (*pixel != 255) {
+ break;
+ }
+ }
+ }
+ }
+ continue;
+ break;
+ case 2:
+ case 5:
+ //check_ba_mob
+ if (!_backAnimList[mob->_mask].backAnims.empty()) {
+ int currentAnim = _backAnimList[mob->_mask]._seq._currRelative;
+ Anim &backAnim = _backAnimList[mob->_mask].backAnims[currentAnim];
+ if (backAnim._animData != nullptr) {
+ if (!backAnim._state) {
+ Common::Rect backAnimRect(backAnim._currX, backAnim._currY, backAnim._currX + backAnim._currW, backAnim._currY + backAnim._currH);
+ if (backAnimRect.contains(pointPos)) {
+ int phase = backAnim._showFrame;
+ int phaseFrameIndex = backAnim._animData->getPhaseFrameIndex(phase);
+ Graphics::Surface *backAnimSurface = backAnim._animData->getFrame(phaseFrameIndex);
+ byte pixel = *(byte *)backAnimSurface->getBasePtr(posX - backAnim._currX, posY - backAnim._currY);
+ if (pixel != 255) {
+ if (type == 5) {
+ if (mob->_rect.contains(pointPos)) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ continue;
+ break;
+ default:
+ //not_part_ba
+ continue;
+ break;
+ }
+
+ if (usePriorityList) {
+ return _mobPriorityList[mobNumber];
+ } else {
+ return mobNumber;
+ }
+ }
+ return -1;
+}
+
+int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList) {
+ if (_mouseFlag == 0 || _mouseFlag == 3) {
+ return -1;
+ }
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ int mobNumber = getMob(mobList, usePriorityList, mousePos.x + _picWindowX, mousePos.y);
+
+ if (mobNumber != -1) {
+ Common::String mobName = mobList[mobNumber]._name;
+
+ if (getLanguage() == Common::DE_DEU) {
+ for (uint i = 0; i < mobName.size(); i++) {
+ switch (mobName[i]) {
+ case '\xc4':
+ mobName.setChar('\x83', i);
+ break;
+ case '\xd6':
+ mobName.setChar('\x84', i);
+ break;
+ case '\xdc':
+ mobName.setChar('\x85', i);
+ break;
+ case '\xdf':
+ mobName.setChar('\x7f', i);
+ break;
+ case '\xe4':
+ mobName.setChar('\x80', i);
+ break;
+ case '\xf6':
+ mobName.setChar('\x81', i);
+ break;
+ case '\xfc':
+ mobName.setChar('\x82', i);
+ break;
+ }
+ }
+ }
+
+ uint16 textW = getTextWidth(mobName.c_str());
+
+ uint16 x = mousePos.x - textW / 2;
+ if (x > screen->w) {
+ x = 0;
+ }
+
+ if (x + textW > screen->w) {
+ x = screen->w - textW;
+ }
+
+ uint16 y = mousePos.y - _font->getFontHeight();
+ if (y > screen->h) {
+ y = _font->getFontHeight() - 2;
+ }
+
+ _font->drawString(screen, mobName, x, y, screen->w, 216);
+ }
+
+ return mobNumber;
+}
+
+void PrinceEngine::printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y) {
+ debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s);
+ if (getLanguage() == Common::DE_DEU) {
+ correctStringDEU(s);
+ }
+ Text &text = _textSlots[slot];
+ text._str = s;
+ text._x = x;
+ text._y = y;
+ text._color = color;
+ int lines = calcTextLines(s);
+ text._time = calcTextTime(lines);
+}
+
+int PrinceEngine::calcTextLines(const char *s) {
+ int lines = 1;
+ while (*s) {
+ if (*s == '\n') {
+ lines++;
+ }
+ s++;
+ }
+ return lines;
+}
+
+int PrinceEngine::calcTextTime(int numberOfLines) {
+ return numberOfLines * 30;
+}
+
+void PrinceEngine::correctStringDEU(char *s) {
+ while (*s) {
+ switch (*s) {
+ case '\xc4':
+ *s = '\x83';
+ break;
+ case '\xd6':
+ *s = '\x84';
+ break;
+ case '\xdc':
+ *s = '\x85';
+ break;
+ case '\xdf':
+ *s = '\x7f';
+ break;
+ case '\xe4':
+ *s = '\x80';
+ break;
+ case '\xf6':
+ *s = '\x81';
+ break;
+ case '\xfc':
+ *s = '\x82';
+ break;
+ }
+ s++;
+ }
+}
+
+uint32 PrinceEngine::getTextWidth(const char *s) {
+ uint16 textW = 0;
+ while (*s) {
+ textW += _font->getCharWidth(*s) + _font->getKerningOffset(0, 0);
+ s++;
+ }
+ return textW;
+}
+
+void PrinceEngine::showTexts(Graphics::Surface *screen) {
+ for (uint32 slot = 0; slot < kMaxTexts; slot++) {
+
+ if (_showInventoryFlag && slot) {
+ // only slot 0 for inventory
+ break;
+ }
+
+ Text& text = _textSlots[slot];
+ if (!text._str && !text._time) {
+ continue;
+ }
+
+ int x = text._x;
+ int y = text._y;
+
+ if (!_showInventoryFlag) {
+ x -= _picWindowX;
+ y -= _picWindowY;
+ }
+
+ Common::Array<Common::String> lines;
+ _font->wordWrapText(text._str, _graph->_frontScreen->w, lines);
+
+ int wideLine = 0;
+ for (uint i = 0; i < lines.size(); i++) {
+ int textLen = getTextWidth(lines[i].c_str());
+ if (textLen > wideLine) {
+ wideLine = textLen;
+ }
+ }
+
+ int leftBorderText = 6;
+ if (x + wideLine / 2 > kNormalWidth - leftBorderText) {
+ x = kNormalWidth - leftBorderText - wideLine / 2;
+ }
+
+ if (x - wideLine / 2 < leftBorderText) {
+ x = leftBorderText + wideLine / 2;
+ }
+
+ int textSkip = 2;
+ for (uint i = 0; i < lines.size(); i++) {
+ int drawX = x - getTextWidth(lines[i].c_str()) / 2;
+ int drawY = y - 10 - (lines.size() - i) * (_font->getFontHeight() - textSkip);
+ if (drawX < 0) {
+ drawX = 0;
+ }
+ if (drawY < 0) {
+ drawY = 0;
+ }
+ _font->drawString(screen, lines[i], drawX, drawY, screen->w, text._color);
+ }
+
+ text._time--;
+ if (!text._time) {
+ text._str = nullptr;
+ }
+ }
+}
+
+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)) {
+ if (animSurface) {
+ 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 (uint 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;
+ }
+ } 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 w, rand;
+ int surfaceHeight = orgSurface->h;
+ for (int y = 0; y < surfaceHeight; y++) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ w = orgSurface->w - x;
+ src2 += x;
+ dst2 += x;
+ while (w > 0) {
+ rand = _randomSource.getRandomNumber(zoomInStep - 1);
+ if (rand < w) {
+ *(dst2 + rand) = *(src2 + rand);
+ src2 += zoomInStep;
+ dst2 += zoomInStep;
+ } else if (y + 1 != surfaceHeight) {
+ *(dst1 + orgSurface->pitch + rand - w) = *(src1 + orgSurface->pitch + rand - 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 w, rand;
+ int surfaceHeight = orgSurface->h;
+ for (int y = 0; y < surfaceHeight; y++) {
+ byte *dst2 = dst1;
+ w = orgSurface->w - x;
+ dst2 += x;
+ while (w > 0) {
+ rand = _randomSource.getRandomNumber(zoomInStep - 1);
+ if (rand < w) {
+ *(dst2 + rand) = 255;
+ dst2 += zoomInStep;
+ } else if (y + 1 != surfaceHeight) {
+ *(dst1 + orgSurface->pitch + rand - 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 *blackPalette = (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++) {
+ blackPalette[3 * j] = paletteBackup[3 * j] * fadeStep / 4;
+ blackPalette[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4;
+ blackPalette[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4;
+ }
+ fadeStep--;
+ _graph->setPalette(blackPalette);
+ _system->updateScreen();
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ free(paletteBackup);
+ free(blackPalette);
+ return;
+ }
+ pause();
+ }
+ free(paletteBackup);
+ free(blackPalette);
+}
+
+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;
+ }
+ pause();
+ }
+ _graph->setPalette(palette);
+ free(blackPalette);
+ }
+}
+
+void PrinceEngine::pause() {
+ int delay = 1000 / kFPS - int32(_system->getMillis() - _currentTime);
+ delay = delay < 0 ? 0 : delay;
+ _system->delayMillis(delay);
+ _currentTime = _system->getMillis();
+}
+
+void PrinceEngine::pause2() {
+ int delay = 1000 / (kFPS * 2) - int32(_system->getMillis() - _currentTime);
+ delay = delay < 0 ? 0 : delay;
+ _system->delayMillis(delay);
+ _currentTime = _system->getMillis();
+}
+
+void PrinceEngine::addInv(int heroId, int item, bool addItemQuiet) {
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _mainHero;
+ } else if (heroId == 1) {
+ hero = _secondHero;
+ }
+ if (hero != nullptr) {
+ if (hero->_inventory.size() < kMaxItems) {
+ if (item != 0x7FFF) {
+ hero->_inventory.push_back(item);
+ }
+ if (!addItemQuiet) {
+ addInvObj();
+ }
+ _interpreter->setResult(0);
+ } else {
+ _interpreter->setResult(1);
+ }
+ }
+}
+
+void PrinceEngine::remInv(int heroId, int item) {
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _mainHero;
+ } else if (heroId == 1) {
+ hero = _secondHero;
+ }
+ if (hero != nullptr) {
+ for (uint i = 0; i < hero->_inventory.size(); i++) {
+ if (hero->_inventory[i] == item) {
+ hero->_inventory.remove_at(i);
+ _interpreter->setResult(0);
+ return;
+ }
+ }
+ }
+ _interpreter->setResult(1);
+}
+
+void PrinceEngine::clearInv(int heroId) {
+ switch (heroId) {
+ case 0:
+ _mainHero->_inventory.clear();
+ break;
+ case 1:
+ _secondHero->_inventory.clear();
+ break;
+ default:
+ error("clearInv() - wrong hero slot");
+ break;
+ }
+}
+
+void PrinceEngine::swapInv(int heroId) {
+ Common::Array<int> tempInv;
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _mainHero;
+ } else if (heroId == 1) {
+ hero = _secondHero;
+ }
+ if (hero != nullptr) {
+ for (uint i = 0; i < hero->_inventory.size(); i++) {
+ tempInv.push_back(hero->_inventory[i]);
+ }
+ hero->_inventory.clear();
+ for (uint i = 0; i < hero->_inventory2.size(); i++) {
+ hero->_inventory.push_back(hero->_inventory2[i]);
+ }
+ hero->_inventory2.clear();
+ for (uint i = 0; i < tempInv.size(); i++) {
+ hero->_inventory2.push_back(tempInv[i]);
+ }
+ tempInv.clear();
+ }
+}
+
+void PrinceEngine::addInvObj() {
+ changeCursor(0);
+ prepareInventoryToView();
+
+ _inventoryBackgroundRemember = true;
+ drawScreen();
+
+ Graphics::Surface *suitcase = _suitcaseBmp->getSurface();
+
+ if (!_flags->getFlagValue(Flags::CURSEBLINK)) {
+
+ loadSample(27, "PRZEDMIO.WAV");
+ playSample(27, 0);
+
+ _mst_shadow2 = 1;
+
+ while (_mst_shadow2 < 512) {
+ rememberScreenInv();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+ drawInvItems();
+ _graph->update(_graph->_screenForInventory);
+ _mst_shadow2 += 50;
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ return;
+ }
+ pause();
+ }
+ while (_mst_shadow2 > 256) {
+ rememberScreenInv();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+ drawInvItems();
+ _graph->update(_graph->_screenForInventory);
+ _mst_shadow2 -= 42;
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ return;
+ }
+ pause();
+ }
+ } else {
+ //CURSEBLINK:
+ for (int i = 0; i < 3; i++) {
+ _mst_shadow2 = 256;
+ while (_mst_shadow2 < 512) {
+ rememberScreenInv();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+ drawInvItems();
+ _graph->update(_graph->_screenForInventory);
+ _mst_shadow2 += 50;
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ return;
+ }
+ pause();
+ }
+ while (_mst_shadow2 > 256) {
+ rememberScreenInv();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+ drawInvItems();
+ _graph->update(_graph->_screenForInventory);
+ _mst_shadow2 -= 50;
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ return;
+ }
+ pause();
+ }
+ }
+ }
+ _mst_shadow2 = 0;
+ for (int i = 0; i < 20; i++) {
+ rememberScreenInv();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+ drawInvItems();
+ _graph->update(_graph->_screenForInventory);
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ eventMan->pollEvent(event);
+ if (shouldQuit()) {
+ return;
+ }
+ pause();
+ }
+}
+
+void PrinceEngine::rememberScreenInv() {
+ _graph->_screenForInventory->copyFrom(*_graph->_frontScreen);
+}
+
+void PrinceEngine::inventoryFlagChange(bool inventoryState) {
+ if (inventoryState) {
+ _showInventoryFlag = true;
+ _inventoryBackgroundRemember = true;
+ } else {
+ _showInventoryFlag = false;
+ }
+}
+
+void PrinceEngine::prepareInventoryToView() {
+ _invMobList.clear();
+ int invItem = _mainHero->_inventory.size();
+ _invLine = invItem / 3;
+ if (invItem % 3) {
+ _invLine++;
+ }
+ if (_invLine < 4) {
+ _invLine = 4;
+ }
+ _maxInvW = (374 - 2 * _invLine) / _invLine;
+ _invLineW = _maxInvW - 2;
+
+ int currInvX = _invLineX;
+ int currInvY = _invLineY;
+
+ Common::MemoryReadStream stream(_invTxt, _invTxtSize);
+ byte c;
+
+ uint item = 0;
+ for (int i = 0; i < _invLines; i++) {
+ for (int j = 0; j < _invLine; j++) {
+ Mob tempMobItem;
+ if (item < _mainHero->_inventory.size()) {
+ int itemNr = _mainHero->_inventory[item];
+ tempMobItem._visible = 0;
+ tempMobItem._mask = itemNr;
+ tempMobItem._rect = Common::Rect(currInvX + _picWindowX, currInvY, currInvX + _picWindowX + _invLineW - 1, currInvY + _invLineH - 1);
+ tempMobItem._type = 0; // to work with checkMob()
+
+ tempMobItem._name = "";
+ tempMobItem._examText = "";
+ int txtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8]);
+ int examTxtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8 + 4]);
+
+ stream.seek(txtOffset);
+ while ((c = stream.readByte())) {
+ tempMobItem._name += c;
+ }
+
+ stream.seek(examTxtOffset);
+ while ((c = stream.readByte())) {
+ tempMobItem._examText += c;
+ }
+ _invMobList.push_back(tempMobItem);
+ }
+ currInvX += _invLineW + _invLineSkipX;
+ item++;
+ }
+ currInvX = _invLineX;
+ currInvY += _invLineSkipY + _invLineH;
+ }
+}
+
+void PrinceEngine::drawInvItems() {
+ int currInvX = _invLineX;
+ int currInvY = _invLineY;
+ uint item = 0;
+ for (int i = 0; i < _invLines; i++) {
+ for (int j = 0; j < _invLine; j++) {
+ if (item < _mainHero->_inventory.size()) {
+ int itemNr = _mainHero->_inventory[item];
+ _mst_shadow = 0;
+ if (_mst_shadow2) {
+ if (!_flags->getFlagValue(Flags::CURSEBLINK)) {
+ if (item + 1 == _mainHero->_inventory.size()) { // last item in inventory
+ _mst_shadow = 1;
+ }
+ } else if (itemNr == 1 || itemNr == 3 || itemNr == 4 || itemNr == 7) {
+ _mst_shadow = 1;
+ }
+ }
+
+ int drawX = currInvX;
+ int drawY = currInvY;
+ Graphics::Surface *itemSurface = nullptr;
+ if (itemNr != 68) {
+ itemSurface = _allInvList[itemNr].getSurface();
+ if (itemSurface->h < _maxInvH) {
+ drawY += (_maxInvH - itemSurface->h) / 2;
+ }
+ } else {
+ // candle item:
+ if (_candleCounter == 8) {
+ _candleCounter = 0;
+ }
+ itemNr = _candleCounter;
+ _candleCounter++;
+ itemNr &= 7;
+ itemNr += 71;
+ itemSurface = _allInvList[itemNr].getSurface();
+ drawY += _allInvList[itemNr]._y + (_maxInvH - 76) / 2 - 200;
+ }
+ if (itemSurface->w < _maxInvW) {
+ drawX += (_maxInvW - itemSurface->w) / 2;
+ }
+ if (!_mst_shadow) {
+ _graph->drawTransparentSurface(_graph->_screenForInventory, drawX, drawY, itemSurface);
+ } else {
+ _mst_shadow = _mst_shadow2;
+ _graph->drawTransparentWithBlendSurface(_graph->_screenForInventory, drawX, drawY, itemSurface);
+ }
+ }
+ currInvX += _invLineW + _invLineSkipX;
+ item++;
+ }
+ currInvX = _invLineX;
+ currInvY += _invLineSkipY + _invLineH;
+ }
+}
+
+void PrinceEngine::walkTo() {
+ if (_mainHero->_visible) {
+ _mainHero->freeHeroAnim();
+ _mainHero->freeOldMove();
+ _interpreter->storeNewPC(_script->_scriptInfo.usdCode);
+ int destX, destY;
+ if (_optionsMob != -1) {
+ destX = _mobList[_optionsMob]._examPosition.x;
+ destY = _mobList[_optionsMob]._examPosition.y;
+ _mainHero->_destDirection = _mobList[_optionsMob]._examDirection;
+ } else {
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ destX = mousePos.x + _picWindowX;
+ destY = mousePos.y + _picWindowY;
+ _mainHero->_destDirection = 0;
+ }
+ _mainHero->_coords = makePath(kMainHero, _mainHero->_middleX, _mainHero->_middleY, destX, destY);
+ if (_mainHero->_coords != nullptr) {
+ _mainHero->_currCoords = _mainHero->_coords;
+ _mainHero->_dirTab = _directionTable;
+ _mainHero->_currDirTab = _directionTable;
+ _directionTable = nullptr;
+ _mainHero->_state = Hero::kHeroStateMove;
+ moveShandria();
+ }
+ }
+}
+
+void PrinceEngine::moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag) {
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _mainHero;
+ } else if (heroId == 1) {
+ hero = _secondHero;
+ }
+
+ if (hero != nullptr) {
+ if (dir) {
+ hero->_destDirection = dir;
+ }
+ if (x || y) {
+ hero->freeOldMove();
+ hero->_coords = makePath(heroId, hero->_middleX, hero->_middleY, x, y);
+ if (hero->_coords != nullptr) {
+ hero->_currCoords = hero->_coords;
+ hero->_dirTab = _directionTable;
+ hero->_currDirTab = _directionTable;
+ _directionTable = nullptr;
+ if (runHeroFlag) {
+ hero->_state = Hero::kHeroStateRun;
+ } else {
+ hero->_state = Hero::kHeroStateMove;
+ }
+ if (heroId == kMainHero && _mouseFlag) {
+ moveShandria();
+ }
+ }
+ } else {
+ hero->freeOldMove();
+ hero->_state = Hero::kHeroStateTurn;
+ }
+ hero->freeHeroAnim();
+ hero->_visible = 1;
+ }
+}
+
+void PrinceEngine::leftMouseButton() {
+ _flags->setFlagValue(Flags::LMOUSE, 1);
+ if (_flags->getFlagValue(Flags::POWERENABLED)) {
+ _flags->setFlagValue(Flags::MBFLAG, 1);
+ }
+ if (_mouseFlag) {
+ int option = 0;
+ int optionEvent = -1;
+
+ if (_optionsFlag) {
+ if (_optionEnabled < _optionsNumber && _optionEnabled != -1) {
+ option = _optionEnabled;
+ _optionsFlag = 0;
+ } else {
+ return;
+ }
+ } else {
+ _optionsMob = _selectedMob;
+ if (_optionsMob == -1) {
+ walkTo();
+ return;
+ }
+ option = 0;
+ }
+ //do_option
+ if (_currentPointerNumber != 2) {
+ //skip_use_code
+ int optionScriptOffset = _room->getOptionOffset(option);
+ if (optionScriptOffset != 0) {
+ optionEvent = _script->scanMobEvents(_optionsMob, optionScriptOffset);
+ }
+ if (optionEvent == -1) {
+ if (!option) {
+ walkTo();
+ return;
+ } else {
+ optionEvent = _script->getOptionStandardOffset(option);
+ }
+ }
+ } else if (_selectedMode) {
+ //give_item
+ if (_room->_itemGive) {
+ optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemGive, _selectedItem);
+ }
+ if (optionEvent == -1) {
+ //standard_giveitem
+ optionEvent = _script->_scriptInfo.stdGiveItem;
+ }
+ } else {
+ if (_room->_itemUse) {
+ optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemUse, _selectedItem);
+ _flags->setFlagValue(Flags::SELITEM, _selectedItem);
+ }
+ if (optionEvent == -1) {
+ //standard_useitem
+ optionEvent = _script->_scriptInfo.stdUseItem;
+ }
+ }
+ _interpreter->storeNewPC(optionEvent);
+ _flags->setFlagValue(Flags::CURRMOB, _selectedMob);
+ _selectedMob = -1;
+ _optionsMob = -1;
+ } else {
+ if (!_flags->getFlagValue(Flags::POWERENABLED)) {
+ if (!_flags->getFlagValue(Flags::NOCLSTEXT)) {
+ for (int slot = 0; slot < kMaxTexts; slot++) {
+ if (slot != 9) {
+ Text& text = _textSlots[slot];
+ if (!text._str) {
+ continue;
+ }
+ text._str = 0;
+ text._time = 0;
+ }
+ }
+ _mainHero->_talkTime = 0;
+ _secondHero->_talkTime = 0;
+ }
+ }
+ }
+}
+
+void PrinceEngine::rightMouseButton() {
+ if (_flags->getFlagValue(Flags::POWERENABLED)) {
+ _flags->setFlagValue(Flags::MBFLAG, 2);
+ }
+ if (_mouseFlag && _mouseFlag != 3) {
+ _mainHero->freeOldMove();
+ _secondHero->freeOldMove();
+ _interpreter->storeNewPC(0);
+ if (_currentPointerNumber < 2) {
+ enableOptions(true);
+ } else {
+ _currentPointerNumber = 1;
+ changeCursor(1);
+ }
+ }
+}
+
+void PrinceEngine::inventoryLeftMouseButton() {
+ if (!_mouseFlag) {
+ _textSlots[0]._time = 0;
+ _textSlots[0]._str = nullptr;
+ stopSample(28);
+ }
+
+ if (_optionsFlag == 1) {
+ if (_selectedMob != -1) {
+ if (_optionEnabled < _invOptionsNumber) {
+ _optionsFlag = 0;
+ } else {
+ return;
+ }
+ } else {
+ error("PrinceEngine::inventoryLeftMouseButton() - optionsFlag = 1, selectedMob = 0");
+ if (_currentPointerNumber == 2) {
+ changeCursor(1);
+ _currentPointerNumber = 1;
+ _selectedMob = -1;
+ _optionsMob = -1;
+ return;
+ } else {
+ return;
+ }
+ }
+ } else {
+ if (_selectedMob != -1) {
+ if (_currentPointerNumber != 2) {
+ if (_invMobList[_selectedMob]._mask != 29) {
+ _optionEnabled = 0;
+ } else {
+ // map item
+ _optionEnabled = 1;
+ }
+ } else {
+ //use_item_on_item
+ int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem);
+ if (invObjUU == -1) {
+ int textNr = 80011; // "I can't do it."
+ if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) {
+ textNr = 80020; // "Nothing is happening."
+ }
+ _interpreter->setCurrentString(textNr);
+ printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100);
+ setVoice(0, 28, 1);
+ playSample(28, 0);
+ _selectedMob = -1;
+ _optionsMob = -1;
+ return;
+ } else {
+ _interpreter->storeNewPC(invObjUU);
+ _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
+ _showInventoryFlag = false;
+ }
+ }
+ } else {
+ return;
+ }
+ }
+ //do_option
+ if (_optionEnabled == 0) {
+ int invObjExamEvent = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjExam);
+ if (invObjExamEvent == -1) {
+ // do_standard
+ printAt(0, 216, (char *)_invMobList[_selectedMob]._examText.c_str(), kNormalWidth / 2, _invExamY);
+ _interpreter->setCurrentString(_invMobList[_selectedMob]._mask + 70000);
+ setVoice(0, 28, 1);
+ playSample(28, 0);
+ // disableuseuse
+ changeCursor(0);
+ _currentPointerNumber = 1;
+ } else {
+ _interpreter->storeNewPC(invObjExamEvent);
+ _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
+ _showInventoryFlag = false;
+ }
+ } else if (_optionEnabled == 1) {
+ // not_examine
+ int invObjUse = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUse);
+ if (invObjUse == -1) {
+ // do_standard_use
+ _selectedMode = 0;
+ _selectedItem = _invMobList[_selectedMob]._mask;
+ makeInvCursor(_invMobList[_selectedMob]._mask);
+ _currentPointerNumber = 2;
+ changeCursor(2);
+ } else {
+ _interpreter->storeNewPC(invObjUse);
+ _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
+ _showInventoryFlag = false;
+ }
+ } else if (_optionEnabled == 4) {
+ // do_standard_give
+ _selectedMode = 1;
+ _selectedItem = _invMobList[_selectedMob]._mask;
+ makeInvCursor(_invMobList[_selectedMob]._mask);
+ _currentPointerNumber = 2;
+ changeCursor(2);
+ } else {
+ // use_item_on_item
+ int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem);
+ if (invObjUU == -1) {
+ int textNr = 80011; // "I can't do it."
+ if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) {
+ textNr = 80020; // "Nothing is happening."
+ }
+ _interpreter->setCurrentString(textNr);
+ printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100);
+ setVoice(0, 28, 1);
+ playSample(28, 0);
+ } else {
+ _interpreter->storeNewPC(invObjUU);
+ _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
+ _showInventoryFlag = false;
+ }
+ }
+ _selectedMob = -1;
+ _optionsMob = -1;
+}
+
+void PrinceEngine::inventoryRightMouseButton() {
+ if (_textSlots[0]._str == nullptr) {
+ enableOptions(false);
+ }
+}
+
+void PrinceEngine::enableOptions(bool checkType) {
+ if (_optionsFlag != 1) {
+ changeCursor(1);
+ _currentPointerNumber = 1;
+ if (_selectedMob != -1) {
+ if (checkType) {
+ if (_mobList[_selectedMob]._type & 0x100) {
+ return;
+ }
+ }
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ int x1 = mousePos.x - _optionsWidth / 2;
+ int x2 = mousePos.x + _optionsWidth / 2;
+ if (x1 < 0) {
+ x1 = 0;
+ x2 = _optionsWidth;
+ } else if (x2 >= kNormalWidth) {
+ x1 = kNormalWidth - _optionsWidth;
+ x2 = kNormalWidth;
+ }
+ int y1 = mousePos.y - 10;
+ if (y1 < 0) {
+ y1 = 0;
+ }
+ if (y1 + _optionsHeight >= kNormalHeight) {
+ y1 = kNormalHeight - _optionsHeight;
+ }
+ _optionsMob = _selectedMob;
+ _optionsX = x1;
+ _optionsY = y1;
+ _optionsFlag = 1;
+ }
+ }
+}
+
+void PrinceEngine::checkOptions() {
+ if (_optionsFlag) {
+ Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _optionsWidth, _optionsY + _optionsHeight);
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ if (!optionsRect.contains(mousePos)) {
+ _optionsFlag = 0;
+ _selectedMob = -1;
+ return;
+ }
+ _graph->drawAsShadowSurface(_graph->_frontScreen, _optionsX, _optionsY, _optionsPic, _graph->_shadowTable50);
+
+ _optionEnabled = -1;
+ int optionsYCord = mousePos.y - (_optionsY + 16);
+ if (optionsYCord >= 0) {
+ int selectedOptionNr = optionsYCord / _optionsStep;
+ if (selectedOptionNr < _optionsNumber) {
+ _optionEnabled = selectedOptionNr;
+ }
+ }
+ int optionsColor;
+ int textY = _optionsY + 16;
+ for (int i = 0; i < _optionsNumber; i++) {
+ if (i != _optionEnabled) {
+ optionsColor = _optionsColor1;
+ } else {
+ optionsColor = _optionsColor2;
+ }
+ Common::String optText;
+ switch(getLanguage()) {
+ case Common::PL_POL:
+ optText = optionsTextPL[i];
+ break;
+ case Common::DE_DEU:
+ optText = optionsTextDE[i];
+ break;
+ case Common::EN_ANY:
+ optText = optionsTextEN[i];
+ break;
+ };
+ uint16 textW = getTextWidth(optText.c_str());
+ uint16 textX = _optionsX + _optionsWidth / 2 - textW / 2;
+ _font->drawString(_graph->_frontScreen, optText, textX, textY, textW, optionsColor);
+ textY += _optionsStep;
+ }
+ }
+}
+
+void PrinceEngine::checkInvOptions() {
+ if (_optionsFlag) {
+ Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _invOptionsWidth, _optionsY + _invOptionsHeight);
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ if (!optionsRect.contains(mousePos)) {
+ _optionsFlag = 0;
+ _selectedMob = -1;
+ return;
+ }
+ _graph->drawAsShadowSurface(_graph->_screenForInventory, _optionsX, _optionsY, _optionsPicInInventory, _graph->_shadowTable50);
+
+ _optionEnabled = -1;
+ int optionsYCord = mousePos.y - (_optionsY + 16);
+ if (optionsYCord >= 0) {
+ int selectedOptionNr = optionsYCord / _invOptionsStep;
+ if (selectedOptionNr < _invOptionsNumber) {
+ _optionEnabled = selectedOptionNr;
+ }
+ }
+ int optionsColor;
+ int textY = _optionsY + 16;
+ for (int i = 0; i < _invOptionsNumber; i++) {
+ if (i != _optionEnabled) {
+ optionsColor = _optionsColor1;
+ } else {
+ optionsColor = _optionsColor2;
+ }
+ Common::String invText;
+ switch(getLanguage()) {
+ case Common::PL_POL:
+ invText = invOptionsTextPL[i];
+ break;
+ case Common::DE_DEU:
+ invText = invOptionsTextDE[i];
+ break;
+ case Common::EN_ANY:
+ invText = invOptionsTextEN[i];
+ break;
+ };
+ uint16 textW = getTextWidth(invText.c_str());
+ uint16 textX = _optionsX + _invOptionsWidth / 2 - textW / 2;
+ _font->drawString(_graph->_screenForInventory, invText, textX, textY, _graph->_screenForInventory->w, optionsColor);
+ textY += _invOptionsStep;
+ }
+ }
+}
+
+void PrinceEngine::displayInventory() {
+
+ _mainHero->freeOldMove();
+ _secondHero->freeOldMove();
+
+ _interpreter->setFgOpcodePC(0);
+
+ stopAllSamples();
+
+ prepareInventoryToView();
+
+ while (!shouldQuit()) {
+
+ if (_textSlots[0]._str != nullptr) {
+ changeCursor(0);
+ } else {
+ changeCursor(_currentPointerNumber);
+
+ Common::Rect inventoryRect(_invX1, _invY1, _invX1 + _invWidth, _invY1 + _invHeight);
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+
+ if (!_invCurInside && inventoryRect.contains(mousePos)) {
+ _invCurInside = true;
+ }
+
+ if (_invCurInside && !inventoryRect.contains(mousePos)) {
+ inventoryFlagChange(false);
+ _invCurInside = false;
+ break;
+ }
+ }
+
+ rememberScreenInv();
+
+ Graphics::Surface *suitcase = _suitcaseBmp->getSurface();
+ _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
+
+ drawInvItems();
+
+ showTexts(_graph->_screenForInventory);
+
+ if (!_optionsFlag && _textSlots[0]._str == nullptr) {
+ _selectedMob = checkMob(_graph->_screenForInventory, _invMobList, false);
+ }
+
+ checkInvOptions();
+
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ keyHandler(event);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ inventoryLeftMouseButton();
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ inventoryRightMouseButton();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!_showInventoryFlag) {
+ break;
+ }
+
+ if (shouldQuit())
+ return;
+
+ getDebugger()->onFrame();
+ _graph->update(_graph->_screenForInventory);
+ pause();
+ }
+
+ if (_currentPointerNumber == 2) {
+ _flags->setFlagValue(Flags::SELITEM, _selectedItem);
+ } else {
+ _flags->setFlagValue(Flags::SELITEM, 0);
+ }
+}
+
+void PrinceEngine::createDialogBox(int dialogBoxNr) {
+ _dialogLines = 0;
+ int amountOfDialogOptions = 0;
+ int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
+
+ byte c;
+ int sentenceNumber;
+ _dialogText = _dialogBoxAddr[dialogBoxNr];
+ byte *dialogText = _dialogText;
+
+ while ((sentenceNumber = *dialogText) != 0xFF) {
+ dialogText++;
+ if (!(dialogDataValue & (1 << sentenceNumber))) {
+ _dialogLines += calcTextLines((const char *)dialogText);
+ amountOfDialogOptions++;
+ }
+ do {
+ c = *dialogText;
+ dialogText++;
+ } while (c);
+ }
+
+ _dialogHeight = _font->getFontHeight() * _dialogLines + _dialogLineSpace * (amountOfDialogOptions + 1);
+ _dialogImage = new Graphics::Surface();
+ _dialogImage->create(_dialogWidth, _dialogHeight, Graphics::PixelFormat::createFormatCLUT8());
+ Common::Rect dBoxRect(0, 0, _dialogWidth, _dialogHeight);
+ _dialogImage->fillRect(dBoxRect, _graph->kShadowColor);
+}
+
+void PrinceEngine::runDialog() {
+
+ _dialogFlag = true;
+
+ while (!shouldQuit()) {
+
+ _interpreter->stepBg();
+ drawScreen();
+
+ int dialogX = (640 - _dialogWidth) / 2;
+ int dialogY = 460 - _dialogHeight;
+ _graph->drawAsShadowSurface(_graph->_frontScreen, dialogX, dialogY, _dialogImage, _graph->_shadowTable50);
+
+ int dialogSkipLeft = 14;
+ int dialogSkipUp = 10;
+
+ int dialogTextX = dialogX + dialogSkipLeft;
+ int dialogTextY = dialogY + dialogSkipUp;
+
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+
+ byte c;
+ int sentenceNumber;
+ byte *dialogText = _dialogText;
+ byte *dialogCurrentText = nullptr;
+ int dialogSelected = -1;
+ int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
+
+ while ((sentenceNumber = *dialogText) != 0xFF) {
+ dialogText++;
+ int actualColor = _dialogColor1;
+
+ if (!(dialogDataValue & (1 << sentenceNumber))) {
+ if (getLanguage() == Common::DE_DEU) {
+ correctStringDEU((char *)dialogText);
+ }
+ Common::Array<Common::String> lines;
+ _font->wordWrapText((const char *)dialogText, _graph->_frontScreen->w, lines);
+
+ Common::Rect dialogOption(dialogTextX, dialogTextY - dialogSkipUp / 2, dialogX + _dialogWidth - dialogSkipLeft, dialogTextY + lines.size() * _font->getFontHeight() + dialogSkipUp / 2 - 1);
+ if (dialogOption.contains(mousePos)) {
+ actualColor = _dialogColor2;
+ dialogSelected = sentenceNumber;
+ dialogCurrentText = dialogText;
+ }
+
+ for (uint j = 0; j < lines.size(); j++) {
+ _font->drawString(_graph->_frontScreen, lines[j], dialogTextX, dialogTextY, _graph->_frontScreen->w, actualColor);
+ dialogTextY += _font->getFontHeight();
+ }
+ dialogTextY += _dialogLineSpace;
+ }
+ do {
+ c = *dialogText;
+ dialogText++;
+ } while (c);
+ }
+
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ keyHandler(event);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ if (dialogSelected != -1) {
+ dialogLeftMouseButton(dialogCurrentText, dialogSelected);
+ _dialogFlag = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (shouldQuit()) {
+ return;
+ }
+
+ if (!_dialogFlag) {
+ break;
+ }
+
+ getDebugger()->onFrame();
+ _graph->update(_graph->_frontScreen);
+ pause();
+ }
+ _dialogImage->free();
+ delete _dialogImage;
+ _dialogImage = nullptr;
+ _dialogFlag = false;
+}
+
+void PrinceEngine::dialogLeftMouseButton(byte *string, int dialogSelected) {
+ _interpreter->setString(string);
+ talkHero(0);
+
+ int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
+ dialogDataValue |= (1u << dialogSelected);
+ WRITE_LE_UINT32(_dialogData, dialogDataValue);
+
+ _flags->setFlagValue(Flags::BOXSEL, dialogSelected + 1);
+ setVoice(0, 28, dialogSelected + 1);
+
+ _flags->setFlagValue(Flags::VOICE_H_LINE, _dialogOptLines[dialogSelected * 4]);
+ _flags->setFlagValue(Flags::VOICE_A_LINE, _dialogOptLines[dialogSelected * 4 + 1]);
+ _flags->setFlagValue(Flags::VOICE_B_LINE, _dialogOptLines[dialogSelected * 4 + 2]);
+
+ _interpreter->setString(_dialogOptAddr[dialogSelected]);
+}
+
+void PrinceEngine::talkHero(int slot) {
+ // heroSlot = textSlot (slot 0 or 1)
+ Text &text = _textSlots[slot];
+ int lines = calcTextLines((const char *)_interpreter->getString());
+ int time = lines * 30;
+
+ if (slot == 0) {
+ text._color = 220; // TODO - test this
+ _mainHero->_state = Hero::kHeroStateTalk;
+ _mainHero->_talkTime = time;
+ text._x = _mainHero->_middleX;
+ text._y = _mainHero->_middleY - _mainHero->_scaledFrameYSize;
+ } else {
+ text._color = _flags->getFlagValue(Flags::KOLOR); // TODO - test this
+ _secondHero->_state = Hero::kHeroStateTalk;
+ _secondHero->_talkTime = time;
+ text._x = _secondHero->_middleX;
+ text._y = _secondHero->_middleY - _secondHero->_scaledFrameYSize;
+ }
+ text._time = time;
+ if (getLanguage() == Common::DE_DEU) {
+ correctStringDEU((char *)_interpreter->getString());
+ }
+ text._str = (const char *)_interpreter->getString();
+ _interpreter->increaseString();
+}
+
+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);
+ }
+}
+
+void PrinceEngine::getCurve() {
+ _flags->setFlagValue(Flags::TORX1, _curveData[_curvPos]);
+ _flags->setFlagValue(Flags::TORY1, _curveData[_curvPos + 1]);
+ _curvPos += 2;
+}
+
+void PrinceEngine::makeCurve() {
+ _curvPos = 0;
+ int x1 = _flags->getFlagValue(Flags::TORX1);
+ int y1 = _flags->getFlagValue(Flags::TORY1);
+ int x2 = _flags->getFlagValue(Flags::TORX2);
+ int y2 = _flags->getFlagValue(Flags::TORY2);
+
+ for (int i = 0; i < kCurveLen; i++) {
+ int sum1 = x1 * curveValues[i][0];
+ sum1 += (x2 + (x1 - x2) / 2) * curveValues[i][1];
+ sum1 += x2 * curveValues[i][2];
+ sum1 += x2 * curveValues[i][3];
+
+ int sum2 = y1 * curveValues[i][0];
+ sum2 += (y2 - 20) * curveValues[i][1];
+ sum2 += (y2 - 10) * curveValues[i][2];
+ sum2 += y2 * curveValues[i][3];
+
+ _curveData[i * 2] = (sum1 >> 15);
+ _curveData[i * 2 + 1] = (sum2 >> 15);
+ }
+}
+
+void PrinceEngine::mouseWeirdo() {
+ if (_mouseFlag == 3) {
+ int weirdDir = _randomSource.getRandomNumber(3);
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ switch (weirdDir) {
+ case 0:
+ mousePos.x += kCelStep;
+ break;
+ case 1:
+ mousePos.x -= kCelStep;
+ break;
+ case 2:
+ mousePos.y += kCelStep;
+ break;
+ case 3:
+ mousePos.y -= kCelStep;
+ break;
+ }
+ mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639);
+ _flags->setFlagValue(Flags::MXFLAG, mousePos.x);
+ mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170);
+ _flags->setFlagValue(Flags::MYFLAG, mousePos.y);
+ _system->warpMouse(mousePos.x, mousePos.y);
+ }
+}
+
+void PrinceEngine::showPower() {
+ if (_flags->getFlagValue(Flags::POWERENABLED)) {
+ int power = _flags->getFlagValue(Flags::POWER);
+
+ byte *dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarPosY);
+ for (int y = 0; y < kPowerBarHeight; y++) {
+ byte *dst2 = dst;
+ for (int x = 0; x < kPowerBarWidth; x++, dst2++) {
+ *dst2 = kPowerBarBackgroundColor;
+ }
+ dst += _graph->_frontScreen->pitch;
+ }
+
+ if (power) {
+ byte *dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarGreenPosY);
+ for (int y = 0; y < kPowerBarGreenHeight; y++) {
+ byte *dst2 = dst;
+ for (int x = 0; x < power + 1; x++, dst2++) {
+ if (x < 58) {
+ *dst2 = kPowerBarGreenColor1;
+ } else {
+ *dst2 = kPowerBarGreenColor2;
+ }
+ }
+ dst += _graph->_frontScreen->pitch;
+ }
+ }
+
+ _graph->change();
+ }
+}
+
+void PrinceEngine::scrollCredits() {
+ byte *scrollAdress = _creditsData;
+ while (!shouldQuit()) {
+ for (int scrollPos = 0; scrollPos > -23; scrollPos--) {
+ const Graphics::Surface *roomSurface = _roomBmp->getSurface();
+ if (roomSurface) {
+ _graph->draw(_graph->_frontScreen, roomSurface);
+ }
+ char *s = (char *)scrollAdress;
+ int drawY = scrollPos;
+ for (int i = 0; i < 22; i++) {
+ Common::String line;
+ char *linePos = s;
+ while ((*linePos != 13)) {
+ line += *linePos;
+ linePos++;
+ }
+ if (!line.empty()) {
+ int drawX = (kNormalWidth - getTextWidth(line.c_str())) / 2;
+ _font->drawString(_graph->_frontScreen, line, drawX, drawY, _graph->_frontScreen->w, 217);
+ }
+
+ char letter1;
+ bool gotIt1 = false;
+ do {
+ letter1 = *s;
+ s++;
+ if (letter1 == 13) {
+ if (*s == 10) {
+ s++;
+ }
+ if (*s != 35) {
+ gotIt1 = true;
+ }
+ break;
+ }
+ } while (letter1 != 35);
+
+ if (gotIt1) {
+ drawY += 23;
+ } else {
+ break;
+ }
+ }
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN) {
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
+ blackPalette();
+ return;
+ }
+ }
+ }
+ if (shouldQuit()) {
+ return;
+ }
+ _graph->change();
+ _graph->update(_graph->_frontScreen);
+ pause2();
+ }
+ char letter2;
+ byte *scan2 = scrollAdress;
+ bool gotIt2 = false;
+ do {
+ letter2 = *scan2;
+ scan2++;
+ if (letter2 == 13) {
+ if (*scan2 == 10) {
+ scan2++;
+ }
+ if (*scan2 != 35) {
+ gotIt2 = true;
+ }
+ break;
+ }
+ } while (letter2 != 35);
+ if (gotIt2) {
+ scrollAdress = scan2;
+ } else {
+ break;
+ }
+ }
+ blackPalette();
+}
+
+// Modified version of Graphics::drawLine() to allow breaking the loop and return value
+int PrinceEngine::drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data) {
+ // Bresenham's line algorithm, as described by Wikipedia
+ const bool steep = ABS(y1 - y0) > ABS(x1 - x0);
+
+ if (steep) {
+ SWAP(x0, y0);
+ SWAP(x1, y1);
+ }
+
+ const int delta_x = ABS(x1 - x0);
+ const int delta_y = ABS(y1 - y0);
+ const int delta_err = delta_y;
+ int x = x0;
+ int y = y0;
+ int err = 0;
+
+ const int x_step = (x0 < x1) ? 1 : -1;
+ const int y_step = (y0 < y1) ? 1 : -1;
+
+ int stopFlag = 0;
+ if (steep)
+ stopFlag = (*plotProc)(y, x, data);
+ else
+ stopFlag = (*plotProc)(x, y, data);
+
+ while (x != x1 && !stopFlag) {
+ x += x_step;
+ err += delta_err;
+ if (2 * err > delta_x) {
+ y += y_step;
+ err -= delta_x;
+ }
+ if (steep)
+ stopFlag = (*plotProc)(y, x, data);
+ else
+ stopFlag = (*plotProc)(x, y, data);
+ }
+ return stopFlag;
+}
+
+int PrinceEngine::getPixelAddr(byte *pathBitmap, int x, int y) {
+ int mask = 128 >> (x & 7);
+ byte value = pathBitmap[x / 8 + y * 80];
+ return (mask & value);
+}
+
+void PrinceEngine::findPoint(int x, int y) {
+ _fpX = x;
+ _fpY = y;
+
+ if (getPixelAddr(_roomPathBitmap, x, y)) {
+ return;
+ }
+
+ int fpL = x;
+ int fpU = y;
+ int fpR = x;
+ int fpD = y;
+
+ while (1) {
+ if (fpD != kMaxPicHeight) {
+ if (getPixelAddr(_roomPathBitmap, x, fpD)) {
+ _fpX = x;
+ _fpY = fpD;
+ break;
+ }
+ fpD++;
+ }
+ if (fpU) {
+ if (getPixelAddr(_roomPathBitmap, x, fpU)) {
+ _fpX = x;
+ _fpY = fpU;
+ break;
+ }
+ fpU--;
+ }
+ if (fpL) {
+ if (getPixelAddr(_roomPathBitmap, fpL, y)) {
+ _fpX = fpL;
+ _fpY = y;
+ break;
+ }
+ fpL--;
+ }
+ if (fpR != _sceneWidth) {
+ if (getPixelAddr(_roomPathBitmap, fpR, y)) {
+ _fpX = fpR;
+ _fpY = y;
+ break;
+ }
+ fpR++;
+ }
+ if (!fpU && (fpD == kMaxPicHeight)) {
+ if (!fpL && (fpR == _sceneWidth)) {
+ break;
+ }
+ }
+ }
+}
+
+Direction PrinceEngine::makeDirection(int x1, int y1, int x2, int y2) {
+ if (x1 != x2) {
+ if (y1 != y2) {
+ if (x1 > x2) {
+ if (y1 > y2) {
+ if (x1 - x2 >= y1 - y2) {
+ return kDirLU;
+ } else {
+ return kDirUL;
+ }
+ } else {
+ if (x1 - x2 >= y2 - y1) {
+ return kDirLD;
+ } else {
+ return kDirDL;
+ }
+ }
+ } else {
+ if (y1 > y2) {
+ if (x2 - x1 >= y1 - y2) {
+ return kDirRU;
+ } else {
+ return kDirUR;
+ }
+ } else {
+ if (x2 - x1 >= y2 - y1) {
+ return kDirRD;
+ } else {
+ return kDirDR;
+ }
+ }
+ }
+ } else {
+ if (x1 >= x2) {
+ return kDirL;
+ } else {
+ return kDirR;
+ }
+ }
+ } else {
+ if (y1 >= y2) {
+ return kDirU;
+ } else {
+ return kDirD;
+ }
+ }
+}
+
+void PrinceEngine::specialPlot(int x, int y) {
+ if (_coords < _coordsBufEnd) {
+ WRITE_LE_UINT16(_coords, x);
+ _coords += 2;
+ WRITE_LE_UINT16(_coords, y);
+ _coords += 2;
+ specialPlot2(x, y);
+ }
+}
+
+void PrinceEngine::specialPlot2(int x, int y) {
+ int mask = 128 >> (x & 7);
+ _roomPathBitmapTemp[x / 8 + y * 80] |= mask;
+}
+
+void PrinceEngine::specialPlotInside(int x, int y) {
+ if (_coords < _coordsBufEnd) {
+ WRITE_LE_UINT16(_coords, x);
+ _coords += 2;
+ WRITE_LE_UINT16(_coords, y);
+ _coords += 2;
+ }
+}
+
+int PrinceEngine::plotTraceLine(int x, int y, void *data) {
+ PrinceEngine *traceLine = (PrinceEngine *)data;
+ if (!traceLine->_traceLineFirstPointFlag) {
+ if (!traceLine->getPixelAddr(traceLine->_roomPathBitmapTemp, x, y)) {
+ if (traceLine->getPixelAddr(traceLine->_roomPathBitmap, x, y)) {
+ traceLine->specialPlotInside(x, y);
+ traceLine->_traceLineLen++;
+ return 0;
+ } else {
+ return -1;
+ }
+ } else {
+ return 1;
+ }
+ } else {
+ traceLine->_traceLineFirstPointFlag = false;
+ return 0;
+ }
+}
+
+int PrinceEngine::leftDownDir() {
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::leftDir() {
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::leftUpDir() {
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::rightDownDir() {
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::rightDir() {
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::rightUpDir() {
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::upLeftDir() {
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::upDir() {
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::upRightDir() {
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::downLeftDir() {
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::downDir() {
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::downRightDir() {
+ if (!checkRightDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDownDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkRightUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ if (!checkLeftUpDir()) {
+ specialPlot(_checkX, _checkY);
+ return 0;
+ }
+ return -1;
+}
+
+int PrinceEngine::cpe() {
+ int value;
+ if ((*(_checkBitmap - kPBW) & _checkMask)) {
+ if ((*(_checkBitmap + kPBW) & _checkMask)) {
+ switch (_checkMask) {
+ case 128:
+ value = READ_LE_UINT16(_checkBitmap - 1);
+ value &= 0x4001;
+ if (value != 0x4001) {
+ return 0;
+ }
+ break;
+ case 64:
+ value = *_checkBitmap;
+ value &= 0xA0;
+ if (value != 0xA0) {
+ return 0;
+ }
+ break;
+ case 32:
+ value = *_checkBitmap;
+ value &= 0x50;
+ if (value != 0x50) {
+ return 0;
+ }
+ break;
+ case 16:
+ value = *_checkBitmap;
+ value &= 0x28;
+ if (value != 0x28) {
+ return 0;
+ }
+ break;
+ case 8:
+ value = *_checkBitmap;
+ value &= 0x14;
+ if (value != 0x14) {
+ return 0;
+ }
+ break;
+ case 4:
+ value = *_checkBitmap;
+ value &= 0xA;
+ if (value != 0xA) {
+ return 0;
+ }
+ break;
+ case 2:
+ value = *_checkBitmap;
+ value &= 0x5;
+ if (value != 0x5) {
+ return 0;
+ }
+ break;
+ case 1:
+ value = READ_LE_UINT16(_checkBitmap);
+ value &= 0x8002;
+ if (value != 0x8002) {
+ return 0;
+ }
+ break;
+ default:
+ error("Wrong _checkMask value - cpe()");
+ break;
+ }
+ _checkX = _rembX;
+ _checkY = _rembY;
+ _checkBitmapTemp = _rembBitmapTemp;
+ _checkBitmap = _rembBitmap;
+ _checkMask = _rembMask;
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+int PrinceEngine::checkLeftDownDir() {
+ if (_checkX && _checkY != (kMaxPicHeight / 2 - 1)) {
+ int tempMask = _checkMask;
+ if (tempMask != 128) {
+ tempMask <<= 1;
+ if ((*(_checkBitmap + kPBW) & tempMask)) {
+ if (!(*(_checkBitmapTemp + kPBW) & tempMask)) {
+ _checkBitmap += kPBW;
+ _checkBitmapTemp += kPBW;
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap + kPBW - 1) & 1)) {
+ if (!(*(_checkBitmapTemp + kPBW - 1) & 1)) {
+ _checkBitmap += (kPBW - 1);
+ _checkBitmapTemp += (kPBW - 1);
+ _checkMask = 1;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX--;
+ _checkY++;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkLeftDir() {
+ if (_checkX) {
+ int tempMask = _checkMask;
+ if (tempMask != 128) {
+ tempMask <<= 1;
+ if ((*(_checkBitmap) & tempMask)) {
+ if (!(*(_checkBitmapTemp) & tempMask)) {
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap - 1) & 1)) {
+ if (!(*(_checkBitmapTemp - 1) & 1)) {
+ _checkBitmap--;
+ _checkBitmapTemp--;
+ _checkMask = 1;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX--;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkDownDir() {
+ if (_checkY != (kMaxPicHeight / 2 - 1)) {
+ if ((*(_checkBitmap + kPBW) & _checkMask)) {
+ if (!(*(_checkBitmapTemp + kPBW) & _checkMask)) {
+ _checkBitmap += kPBW;
+ _checkBitmapTemp += kPBW;
+ _checkY++;
+ return cpe();
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkUpDir() {
+ if (_checkY) {
+ if ((*(_checkBitmap - kPBW) & _checkMask)) {
+ if (!(*(_checkBitmapTemp - kPBW) & _checkMask)) {
+ _checkBitmap -= kPBW;
+ _checkBitmapTemp -= kPBW;
+ _checkY--;
+ return cpe();
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkRightDir() {
+ if (_checkX != (kMaxPicWidth / 2 - 1)) {
+ int tempMask = _checkMask;
+ if (tempMask != 1) {
+ tempMask >>= 1;
+ if ((*(_checkBitmap) & tempMask)) {
+ if (!(*(_checkBitmapTemp) & tempMask)) {
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap + 1) & 128)) {
+ if (!(*(_checkBitmapTemp + 1) & 128)) {
+ _checkBitmap++;
+ _checkBitmapTemp++;
+ _checkMask = 128;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX++;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkLeftUpDir() {
+ if (_checkX && _checkY) {
+ int tempMask = _checkMask;
+ if (tempMask != 128) {
+ tempMask <<= 1;
+ if ((*(_checkBitmap - kPBW) & tempMask)) {
+ if (!(*(_checkBitmapTemp - kPBW) & tempMask)) {
+ _checkBitmap -= kPBW;
+ _checkBitmapTemp -= kPBW;
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap - (kPBW + 1)) & 1)) {
+ if (!(*(_checkBitmapTemp - (kPBW + 1)) & 1)) {
+ _checkBitmap -= (kPBW + 1);
+ _checkBitmapTemp -= (kPBW + 1);
+ _checkMask = 1;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX--;
+ _checkY--;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkRightDownDir() {
+ if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY != (kMaxPicHeight / 2 - 1)) {
+ int tempMask = _checkMask;
+ if (tempMask != 1) {
+ tempMask >>= 1;
+ if ((*(_checkBitmap + kPBW) & tempMask)) {
+ if (!(*(_checkBitmapTemp + kPBW) & tempMask)) {
+ _checkBitmap += kPBW;
+ _checkBitmapTemp += kPBW;
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap + kPBW + 1) & 128)) {
+ if (!(*(_checkBitmapTemp + kPBW + 1) & 128)) {
+ _checkBitmap += kPBW + 1;
+ _checkBitmapTemp += kPBW + 1;
+ _checkMask = 128;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX++;
+ _checkY++;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+int PrinceEngine::checkRightUpDir() {
+ if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY) {
+ int tempMask = _checkMask;
+ if (tempMask != 1) {
+ tempMask >>= 1;
+ if ((*(_checkBitmap - kPBW) & tempMask)) {
+ if (!(*(_checkBitmapTemp - kPBW) & tempMask)) {
+ _checkBitmap -= kPBW;
+ _checkBitmapTemp -= kPBW;
+ _checkMask = tempMask;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ if ((*(_checkBitmap - kPBW + 1) & 128)) {
+ if (!(*(_checkBitmapTemp - kPBW + 1) & 128)) {
+ _checkBitmap -= (kPBW - 1);
+ _checkBitmapTemp -= (kPBW - 1);
+ _checkMask = 128;
+ } else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ _checkX++;
+ _checkY--;
+ return cpe();
+ } else {
+ return -1;
+ }
+}
+
+bool PrinceEngine::tracePath(int x1, int y1, int x2, int y2) {
+ for (int i = 0; i < kPathBitmapLen; i++) {
+ _roomPathBitmapTemp[i] = 0;
+ }
+ if (x1 != x2 || y1 != y2) {
+ if (getPixelAddr(_roomPathBitmap, x1, y1)) {
+ if (getPixelAddr(_roomPathBitmap, x2, y2)) {
+ _coords = _coordsBuf;
+ specialPlot(x1, y1);
+
+ int x = x1;
+ int y = y1;
+ byte *bcad;
+ int btx, bty;
+
+ while (1) {
+ btx = x;
+ bty = y;
+ bcad = _coords;
+
+ _traceLineLen = 0;
+ _traceLineFirstPointFlag = true;
+ int drawLineFlag = drawLine(x, y, x2, y2, &this->plotTraceLine, this);
+
+ if (!drawLineFlag) {
+ return true;
+ } else if (drawLineFlag == -1 && _traceLineLen >= 2) {
+ byte *tempCorrds = bcad;
+ while (tempCorrds != _coords) {
+ x = READ_LE_UINT16(tempCorrds);
+ y = READ_LE_UINT16(tempCorrds + 2);
+ tempCorrds += 4;
+ specialPlot2(x, y);
+ }
+ } else {
+ _coords = bcad;
+ x = btx;
+ y = bty;
+ }
+
+ Direction dir = makeDirection(x, y, x2, y2);
+
+ _rembBitmapTemp = &_roomPathBitmapTemp[x / 8 + y * 80];
+ _rembBitmap = &_roomPathBitmap[x / 8 + y * 80];
+ _rembMask = 128 >> (x & 7);
+ _rembX = x;
+ _rembY = y;
+
+ _checkBitmapTemp = _rembBitmapTemp;
+ _checkBitmap = _rembBitmap;
+ _checkMask = _rembMask;
+ _checkX = _rembX;
+ _checkY = _rembY;
+
+ int result;
+ switch (dir) {
+ case kDirLD:
+ result = leftDownDir();
+ break;
+ case kDirL:
+ result = leftDir();
+ break;
+ case kDirLU:
+ result = leftUpDir();
+ break;
+ case kDirRD:
+ result = rightDownDir();
+ break;
+ case kDirR:
+ result = rightDir();
+ break;
+ case kDirRU:
+ result = rightUpDir();
+ break;
+ case kDirUL:
+ result = upLeftDir();
+ break;
+ case kDirU:
+ result = upDir();
+ break;
+ case kDirUR:
+ result = upRightDir();
+ break;
+ case kDirDL:
+ result = downLeftDir();
+ break;
+ case kDirD:
+ result = downDir();
+ break;
+ case kDirDR:
+ result = downRightDir();
+ break;
+ default:
+ result = -1;
+ error("tracePath: wrong direction %d", dir);
+ break;
+ }
+
+ if (result) {
+ byte *tempCoords = _coords;
+ tempCoords -= 4;
+ if (tempCoords > _coordsBuf) {
+ int tempX = READ_LE_UINT16(tempCoords);
+ int tempY = READ_LE_UINT16(tempCoords + 2);
+ if (_checkX == tempX && _checkY == tempY) {
+ _coords = tempCoords;
+ }
+ x = READ_LE_UINT16(tempCoords);
+ y = READ_LE_UINT16(tempCoords + 2);
+ } else {
+ return false;
+ }
+ } else {
+ x = _checkX;
+ y = _checkY;
+ }
+ }
+ return true;
+ } else {
+ error("tracePath: wrong destination point");
+ }
+ } else {
+ error("tracePath: wrong start point");
+ }
+ } else {
+ error("tracePath: same point");
+ }
+}
+
+void PrinceEngine::specialPlotInside2(int x, int y) {
+ WRITE_LE_UINT16(_coords2, x);
+ _coords2 += 2;
+ WRITE_LE_UINT16(_coords2, y);
+ _coords2 += 2;
+}
+
+int PrinceEngine::plotTracePoint(int x, int y, void *data) {
+ PrinceEngine *tracePoint = (PrinceEngine *)data;
+ if (!tracePoint->_tracePointFirstPointFlag) {
+ if (tracePoint->getPixelAddr(tracePoint->_roomPathBitmap, x, y)) {
+ tracePoint->specialPlotInside2(x, y);
+ return 0;
+ } else {
+ return -1;
+ }
+ } else {
+ tracePoint->_tracePointFirstPointFlag = false;
+ return 0;
+ }
+}
+
+void PrinceEngine::approxPath() {
+ byte *oldCoords;
+ _coords2 = _coordsBuf2;
+ byte *tempCoordsBuf = _coordsBuf; // first point on path
+ byte *tempCoords = _coords;
+ int x1, y1, x2, y2;
+ if (tempCoordsBuf != tempCoords) {
+ tempCoords -= 4; // last point on path
+ while (tempCoordsBuf != tempCoords) {
+ x1 = READ_LE_UINT16(tempCoords);
+ y1 = READ_LE_UINT16(tempCoords + 2);
+ x2 = READ_LE_UINT16(tempCoordsBuf);
+ y2 = READ_LE_UINT16(tempCoordsBuf + 2);
+ tempCoordsBuf += 4;
+ //TracePoint
+ oldCoords = _coords2;
+ if (_coords2 == _coordsBuf2) {
+ WRITE_LE_UINT16(_coords2, x1);
+ WRITE_LE_UINT16(_coords2 + 2, y1);
+ _coords2 += 4;
+ } else {
+ int testX = READ_LE_UINT16(_coords2 - 4);
+ int testY = READ_LE_UINT16(_coords2 - 2);
+ if (testX != x1 || testY != y1) {
+ WRITE_LE_UINT16(_coords2, x1);
+ WRITE_LE_UINT16(_coords2 + 2, y1);
+ _coords2 += 4;
+ }
+ }
+ _tracePointFirstPointFlag = true;
+ bool drawLineFlag = drawLine(x1, y1, x2, y2, &this->plotTracePoint, this);
+ if (!drawLineFlag) {
+ tempCoords = tempCoordsBuf - 4;
+ tempCoordsBuf = _coordsBuf;
+ } else {
+ _coords2 = oldCoords;
+ }
+ }
+ }
+}
+
+void PrinceEngine::freeDirectionTable() {
+ if (_directionTable != nullptr) {
+ free(_directionTable);
+ _directionTable = nullptr;
+ }
+}
+
+int PrinceEngine::scanDirectionsFindNext(byte *tempCoordsBuf, int xDiff, int yDiff) {
+
+ int tempX, tempY, direction, dX, dY, againPointX1, againPointY1;
+
+ tempX = Hero::kHeroDirLeft;
+ if (xDiff < 0) {
+ tempX = Hero::kHeroDirRight;
+ }
+
+ tempY = Hero::kHeroDirUp;
+ if (yDiff < 0) {
+ tempY = Hero::kHeroDirDown;
+ }
+
+ while (1) {
+ againPointX1 = READ_LE_UINT16(tempCoordsBuf);
+ againPointY1 = READ_LE_UINT16(tempCoordsBuf + 2);
+ tempCoordsBuf += 4;
+
+ if (tempCoordsBuf == _coords) {
+ direction = tempX;
+ break;
+ }
+
+ dX = againPointX1 - READ_LE_UINT16(tempCoordsBuf);
+ dY = againPointY1 - READ_LE_UINT16(tempCoordsBuf + 2);
+
+ if (dX != xDiff) {
+ direction = tempY;
+ break;
+ }
+
+ if (dY != yDiff) {
+ direction = tempX;
+ break;
+ }
+ }
+ return direction;
+}
+
+void PrinceEngine::scanDirections() {
+ freeDirectionTable();
+ byte *tempCoordsBuf = _coordsBuf;
+ if (tempCoordsBuf != _coords) {
+ int size = (_coords - tempCoordsBuf) / 4 + 1; // number of coord points plus one for end marker
+ _directionTable = (byte *)malloc(size);
+ byte *tempDirTab = _directionTable;
+ int direction = -1;
+ int lastDirection = -1;
+ int x1, y1, x2, y2, xDiff, yDiff;
+
+ while (1) {
+ x1 = READ_LE_UINT16(tempCoordsBuf);
+ y1 = READ_LE_UINT16(tempCoordsBuf + 2);
+ tempCoordsBuf += 4;
+ if (tempCoordsBuf == _coords) {
+ break;
+ }
+ x2 = READ_LE_UINT16(tempCoordsBuf);
+ y2 = READ_LE_UINT16(tempCoordsBuf + 2);
+
+ xDiff = x1 - x2;
+ yDiff = y1 - y2;
+
+ if (xDiff) {
+ if (yDiff) {
+ if (lastDirection != -1) {
+ direction = lastDirection;
+ if (direction == Hero::kHeroDirLeft) {
+ if (xDiff < 0) {
+ direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
+ }
+ } else if (direction == Hero::kHeroDirRight) {
+ if (xDiff >= 0) {
+ direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
+ }
+ } else if (direction == Hero::kHeroDirUp) {
+ if (yDiff < 0) {
+ direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
+ }
+ } else {
+ if (yDiff >= 0) {
+ direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
+ }
+ }
+ } else {
+ direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
+ }
+ } else {
+ direction = Hero::kHeroDirLeft;
+ if (xDiff < 0) {
+ direction = Hero::kHeroDirRight;
+ }
+ }
+ } else {
+ if (yDiff) {
+ direction = Hero::kHeroDirUp;
+ if (yDiff < 0) {
+ direction = Hero::kHeroDirDown;
+ }
+ } else {
+ direction = lastDirection;
+ }
+ }
+ lastDirection = direction;
+ *tempDirTab = direction;
+ tempDirTab++;
+ }
+ *tempDirTab = *(tempDirTab - 1);
+ tempDirTab++;
+ *tempDirTab = 0;
+ }
+}
+
+void PrinceEngine::moveShandria() {
+ int shanLen1 = _shanLen;
+ if (_flags->getFlagValue(Flags::SHANDOG)) {
+ _secondHero->freeHeroAnim();
+ _secondHero->freeOldMove();
+ byte *shanCoords = _mainHero->_currCoords + shanLen1 * 4 - 4;
+ int shanX = READ_LE_UINT16(shanCoords - 4);
+ int shanY = READ_LE_UINT16(shanCoords - 2);
+ int xDiff = shanX - _secondHero->_middleX;
+ if (xDiff < 0) {
+ xDiff *= -1;
+ }
+ int yDiff = shanY - _secondHero->_middleY;
+ if (yDiff < 0) {
+ yDiff *= -1;
+ }
+ shanCoords -= 4;
+ if (shanCoords != _mainHero->_currCoords) {
+ yDiff *= 1.5;
+ int shanDis = xDiff * xDiff + yDiff * yDiff;
+ if (shanDis >= kMinDistance) {
+ while (1) {
+ shanCoords -= 4;
+ if (shanCoords == _mainHero->_currCoords) {
+ break;
+ }
+ int x = READ_LE_UINT16(shanCoords);
+ int y = READ_LE_UINT16(shanCoords + 2);
+ int pointDiffX = x - shanX;
+ if (pointDiffX < 0) {
+ pointDiffX *= -1;
+ }
+ int pointDiffY = y - shanY;
+ if (pointDiffY < 0) {
+ pointDiffY *= -1;
+ }
+ pointDiffY *= 1.5;
+ int distance = pointDiffX * pointDiffX + pointDiffY * pointDiffY;
+ if (distance >= kMinDistance) {
+ break;
+ }
+ }
+ int pathSizeDiff = (shanCoords - _mainHero->_currCoords) / 4;
+ int destDir = *(_mainHero->_currDirTab + pathSizeDiff);
+ _secondHero->_destDirection = destDir;
+ int destX = READ_LE_UINT16(shanCoords);
+ int destY = READ_LE_UINT16(shanCoords + 2);
+ _secondHero->_coords = makePath(kSecondHero, _secondHero->_middleX, _secondHero->_middleY, destX, destY);
+ if (_secondHero->_coords != nullptr) {
+ _secondHero->_currCoords = _secondHero->_coords;
+ int delay = shanLen1 - _shanLen;
+ if (delay < 6) {
+ delay = 6;
+ }
+ _secondHero->_moveDelay = delay / 2;
+ _secondHero->_state = Hero::kHeroStateDelayMove;
+ _secondHero->_dirTab = _directionTable;
+ _secondHero->_currDirTab = _directionTable;
+ _directionTable = nullptr;
+ }
+ }
+ }
+ }
+}
+
+byte *PrinceEngine::makePath(int heroId, int currX, int currY, int destX, int destY) {
+ int realDestX = destX;
+ int realDestY = destY;
+ _flags->setFlagValue(Flags::MOVEDESTX, destX);
+ _flags->setFlagValue(Flags::MOVEDESTY, destY);
+
+ int x1 = currX / 2;
+ int y1 = currY / 2;
+ int x2 = destX / 2;
+ int y2 = destY / 2;
+
+ if ((x1 != x2) || (y1 != y2)) {
+ findPoint(x1, y1);
+ if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) {
+ return nullptr;
+ }
+ if ((x1 != _fpX) || (y1 != _fpY)) {
+ x1 = _fpX;
+ y1 = _fpY;
+ }
+ findPoint(x2, y2);
+ if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) {
+ return nullptr;
+ }
+ if ((x2 != _fpX) || (y2 != _fpY)) {
+ x2 = _fpX;
+ y2 = _fpY;
+ if (!_flags->getFlagValue(Flags::EXACTMOVE)) {
+ realDestX = x2 * 2;
+ realDestY = y2 * 2;
+ _flags->setFlagValue(Flags::MOVEDESTX, realDestX);
+ _flags->setFlagValue(Flags::MOVEDESTY, realDestY);
+ } else {
+ return nullptr;
+ }
+ }
+
+ if ((x1 == x2) && (y1 == y2)) {
+ if (!heroId) {
+ _mainHero->freeOldMove();
+ _mainHero->_state = Hero::kHeroStateTurn;
+ } else if (heroId == 1) {
+ _secondHero->freeOldMove();
+ _secondHero->_state = Hero::kHeroStateTurn;
+ }
+ return nullptr;
+ }
+
+ int pathLen1 = 0;
+ int pathLen2 = 0;
+ int stX = x1;
+ int stY = y1;
+ int sizeCoords2 = 0;
+
+ if (tracePath(x1, y1, x2, y2)) {
+ allocCoords2();
+ approxPath();
+ sizeCoords2 = _coords2 - _coordsBuf2;
+ for (int i = 0; i < sizeCoords2; i++) {
+ _coordsBuf[i] = _coordsBuf2[i];
+ }
+ _coords = _coordsBuf + sizeCoords2;
+ approxPath();
+ _coordsBuf3 = _coordsBuf2;
+ _coordsBuf2 = nullptr;
+ _coords3 = _coords2;
+ _coords2 = nullptr;
+ pathLen1 = _coords3 - _coordsBuf3;
+ }
+ if (tracePath(x2, y2, x1, y1)) {
+ allocCoords2();
+ approxPath();
+ sizeCoords2 = _coords2 - _coordsBuf2;
+ for (int i = 0; i < sizeCoords2; i++) {
+ _coordsBuf[i] = _coordsBuf2[i];
+ }
+ _coords = _coordsBuf + sizeCoords2;
+ approxPath();
+ pathLen2 = _coords2 - _coordsBuf2;
+ }
+
+ byte *chosenCoordsBuf = _coordsBuf2;
+ byte *choosenCoords = _coords2;
+ int choosenLength = pathLen1;
+ if (pathLen1 < pathLen2) {
+ chosenCoordsBuf = _coordsBuf3;
+ choosenCoords = _coords3;
+ choosenLength = pathLen2;
+ }
+
+ if (choosenLength) {
+ if (chosenCoordsBuf != nullptr) {
+ int tempXBegin = READ_LE_UINT16(chosenCoordsBuf);
+ int tempYBegin = READ_LE_UINT16(chosenCoordsBuf + 2);
+ if (stX != tempXBegin || stY != tempYBegin) {
+ SWAP(chosenCoordsBuf, choosenCoords);
+ chosenCoordsBuf -= 4;
+ int cord;
+ byte *tempCoordsBuf = _coordsBuf;
+ while (1) {
+ cord = READ_LE_UINT32(chosenCoordsBuf);
+ WRITE_LE_UINT32(tempCoordsBuf, cord);
+ tempCoordsBuf += 4;
+ if (chosenCoordsBuf == choosenCoords) {
+ break;
+ }
+ chosenCoordsBuf -= 4;
+ }
+ _coords = tempCoordsBuf;
+ } else {
+ int sizeChoosen = choosenCoords - chosenCoordsBuf;
+ for (int i = 0; i < sizeChoosen; i++) {
+ _coordsBuf[i] = chosenCoordsBuf[i];
+ }
+ _coords = _coordsBuf + sizeChoosen;
+ }
+ WRITE_LE_UINT32(_coords, 0xFFFFFFFF);
+ freeCoords2();
+ freeCoords3();
+ scanDirections();
+
+ byte *tempCoordsBuf = _coordsBuf;
+ byte *tempCoords = _coords;
+ byte *newCoords;
+ byte *newCoordsBegin;
+ int newValueX = 0;
+ int newValueY = 0;
+ if (tempCoordsBuf != tempCoords) {
+ int normCoordsSize = _coords - _coordsBuf + 4;
+ newCoords = (byte *)malloc(normCoordsSize);
+ newCoordsBegin = newCoords;
+ while (tempCoordsBuf != tempCoords) {
+ newValueX = READ_LE_UINT16(tempCoordsBuf);
+ WRITE_LE_UINT16(newCoords, newValueX * 2);
+ newCoords += 2;
+ newValueY = READ_LE_UINT16(tempCoordsBuf + 2);
+ WRITE_LE_UINT16(newCoords, newValueY * 2);
+ newCoords += 2;
+ tempCoordsBuf += 4;
+ }
+ WRITE_LE_UINT16(newCoords - 4, realDestX);
+ WRITE_LE_UINT16(newCoords - 2, realDestY);
+ WRITE_LE_UINT32(newCoords, 0xFFFFFFFF);
+ newCoords += 4;
+ _shanLen = (newCoords - newCoordsBegin);
+ _shanLen /= 4;
+ return newCoordsBegin;
+ }
+ }
+ }
+ _coords = _coordsBuf;
+ freeCoords2();
+ freeCoords3();
+ return nullptr;
+ } else {
+ if (!heroId) {
+ _mainHero->freeOldMove();
+ _mainHero->_state = Hero::kHeroStateTurn;
+ } else if (heroId == 1) {
+ _secondHero->freeOldMove();
+ _secondHero->_state = Hero::kHeroStateTurn;
+ }
+ return nullptr;
+ }
+}
+
+void PrinceEngine::allocCoords2() {
+ if (_coordsBuf2 == nullptr) {
+ _coordsBuf2 = (byte *)malloc(kTracePts * 4);
+ _coords2 = _coordsBuf2;
+ }
+}
+
+void PrinceEngine::freeCoords2() {
+ if (_coordsBuf2 != nullptr) {
+ free(_coordsBuf2);
+ _coordsBuf2 = nullptr;
+ _coords2 = nullptr;
+ }
+}
+
+void PrinceEngine::freeCoords3() {
+ if (_coordsBuf3 != nullptr) {
+ free(_coordsBuf3);
+ _coordsBuf3 = nullptr;
+ _coords3 = nullptr;
+ }
+}
+
+void PrinceEngine::openInventoryCheck() {
+ if (!_optionsFlag) {
+ if (_mouseFlag == 1 || _mouseFlag == 2) {
+ if (_mainHero->_visible) {
+ if (!_flags->getFlagValue(Flags::INVALLOWED)) {
+ // 29 - Basement, 50 - Map
+ if (_locationNr != 29 && _locationNr != 50) {
+ Common::Point mousePos = _system->getEventManager()->getMousePos();
+ if (mousePos.y < 4 && !_showInventoryFlag) {
+ _invCounter++;
+ } else {
+ _invCounter = 0;
+ }
+ if (_invCounter >= _invMaxCount) {
+ inventoryFlagChange(true);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void PrinceEngine::mainLoop() {
+ changeCursor(0);
+ _currentTime = _system->getMillis();
+
+ while (!shouldQuit()) {
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ keyHandler(event);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ leftMouseButton();
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ rightMouseButton();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (shouldQuit()) {
+ return;
+ }
+
+ // for "throw a rock" mini-game
+ mouseWeirdo();
+
+ _interpreter->stepBg();
+ _interpreter->stepFg();
+
+ drawScreen();
+
+ _graph->update(_graph->_frontScreen);
+
+ openInventoryCheck();
+
+ pause();
+ }
+}
+
+} // End of namespace Prince