aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorEugene Sandulenko2014-10-10 13:49:05 +0200
committerEugene Sandulenko2014-10-10 13:49:05 +0200
commitbb8f66c2437a0f3a704ddc84e08dab7551b08253 (patch)
treea8167e4c8df92cb80004a6314fe31352330a217f /engines
parentc128365068660af764b4fdfc9e5256a7caed3839 (diff)
parentc769c3b8855820122e6a7a6712cb7139445d8a0b (diff)
downloadscummvm-rg350-bb8f66c2437a0f3a704ddc84e08dab7551b08253.tar.gz
scummvm-rg350-bb8f66c2437a0f3a704ddc84e08dab7551b08253.tar.bz2
scummvm-rg350-bb8f66c2437a0f3a704ddc84e08dab7551b08253.zip
Merge pull request #516 from lukaslw/prince-lukaslw
PRINCE: Adding The Prince and the Coward
Diffstat (limited to 'engines')
-rw-r--r--engines/prince/animation.cpp178
-rw-r--r--engines/prince/animation.h73
-rw-r--r--engines/prince/archive.cpp144
-rw-r--r--engines/prince/archive.h61
-rw-r--r--engines/prince/common.h45
-rw-r--r--engines/prince/configure.engine3
-rw-r--r--engines/prince/cursor.cpp54
-rw-r--r--engines/prince/cursor.h46
-rw-r--r--engines/prince/curve_values.h45
-rw-r--r--engines/prince/debugger.cpp174
-rw-r--r--engines/prince/debugger.h58
-rw-r--r--engines/prince/decompress.cpp171
-rw-r--r--engines/prince/decompress.h44
-rw-r--r--engines/prince/detection.cpp73
-rw-r--r--engines/prince/detection.h100
-rw-r--r--engines/prince/flags.cpp420
-rw-r--r--engines/prince/flags.h421
-rw-r--r--engines/prince/font.cpp94
-rw-r--r--engines/prince/font.h64
-rw-r--r--engines/prince/graphics.cpp491
-rw-r--r--engines/prince/graphics.h76
-rw-r--r--engines/prince/hero.cpp1004
-rw-r--r--engines/prince/hero.h184
-rw-r--r--engines/prince/hero_set.h244
-rw-r--r--engines/prince/mhwanh.cpp71
-rw-r--r--engines/prince/mhwanh.h55
-rw-r--r--engines/prince/mob.cpp108
-rw-r--r--engines/prince/mob.h70
-rw-r--r--engines/prince/module.mk30
-rw-r--r--engines/prince/musNum.h87
-rw-r--r--engines/prince/object.cpp113
-rw-r--r--engines/prince/object.h70
-rw-r--r--engines/prince/option_text.h85
-rw-r--r--engines/prince/prince.cpp4737
-rw-r--r--engines/prince/prince.h668
-rw-r--r--engines/prince/pscr.cpp75
-rw-r--r--engines/prince/pscr.h48
-rw-r--r--engines/prince/resource.h100
-rw-r--r--engines/prince/saveload.cpp514
-rw-r--r--engines/prince/script.cpp2071
-rw-r--r--engines/prince/script.h397
-rw-r--r--engines/prince/sound.cpp211
-rw-r--r--engines/prince/sound.h67
-rw-r--r--engines/prince/variatxt.cpp55
-rw-r--r--engines/prince/variatxt.h40
45 files changed, 13939 insertions, 0 deletions
diff --git a/engines/prince/animation.cpp b/engines/prince/animation.cpp
new file mode 100644
index 0000000000..b4bde27f69
--- /dev/null
+++ b/engines/prince/animation.cpp
@@ -0,0 +1,178 @@
+/* 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 "prince/animation.h"
+#include "prince/decompress.h"
+
+#include "common/debug.h"
+#include "common/endian.h"
+
+namespace Prince {
+
+Animation::Animation() : _loopCount(0), _phaseCount(0), _frameCount(0), _baseX(0), _baseY(0)
+{
+}
+
+Animation::~Animation() {
+ clear();
+}
+
+void Animation::clear() {
+ _phaseList.clear();
+ for (int i = 0; i < _frameCount; i++) {
+ _frameList[i]._surface->free();
+ delete _frameList[i]._surface;
+ _frameList[i]._surface = nullptr;
+ if (_frameList[i]._compressedData != nullptr) {
+ free(_frameList[i]._compressedData);
+ _frameList[i]._compressedData = nullptr;
+ }
+ }
+}
+
+bool Animation::loadStream(Common::SeekableReadStream &stream) {
+ stream.skip(2); // skip not used x and y coord diff
+ _loopCount = stream.readUint16LE();
+ _phaseCount = stream.readUint16LE();
+ stream.skip(2); // skip _frameCount here
+ _baseX = stream.readUint16LE();
+ _baseY = stream.readUint16LE();
+ uint32 phaseTableOffset = stream.readUint32LE();
+ uint32 tableOfFrameOffsets = stream.pos();
+
+ stream.seek(phaseTableOffset);
+ Phase tempPhase;
+ _frameCount = 0;
+ for (int phase = 0; phase < _phaseCount; phase++) {
+ tempPhase._phaseOffsetX = stream.readSint16LE();
+ tempPhase._phaseOffsetY = stream.readSint16LE();
+ tempPhase._phaseToFrameIndex = stream.readUint16LE();
+ if (tempPhase._phaseToFrameIndex > _frameCount) {
+ _frameCount = tempPhase._phaseToFrameIndex;
+ }
+ _phaseList.push_back(tempPhase);
+ stream.skip(2);
+ }
+ if (_phaseCount) {
+ _frameCount++;
+ }
+
+ Frame tempFrame;
+ for (int frame = 0; frame < _frameCount; frame++) {
+ stream.seek(tableOfFrameOffsets + frame * 4);
+ uint32 frameInfoOffset = stream.readUint32LE();
+ stream.seek(frameInfoOffset);
+ uint16 frameWidth = stream.readUint16LE();
+ uint16 frameHeight = stream.readUint16LE();
+ uint32 frameDataPos = stream.pos();
+ uint32 frameDataOffset = stream.readUint32BE();
+
+ tempFrame._surface = new Graphics::Surface();
+ tempFrame._surface->create(frameWidth, frameHeight, Graphics::PixelFormat::createFormatCLUT8());
+ if (frameDataOffset == MKTAG('m', 'a', 's', 'm')) {
+ tempFrame._isCompressed = true;
+ tempFrame._dataSize = stream.readUint32LE();
+ tempFrame._compressedData = (byte *)malloc(tempFrame._dataSize);
+ stream.read(tempFrame._compressedData, tempFrame._dataSize);
+ } else {
+ tempFrame._isCompressed = false;
+ tempFrame._dataSize = 0;
+ tempFrame._compressedData = nullptr;
+ stream.seek(frameDataPos);
+ for (uint16 i = 0; i < frameHeight; i++) {
+ stream.read(tempFrame._surface->getBasePtr(0, i), frameWidth);
+ }
+ }
+ _frameList.push_back(tempFrame);
+ }
+
+ return true;
+}
+
+int16 Animation::getLoopCount() const {
+ return _loopCount;
+}
+
+int32 Animation::getPhaseCount() const {
+ return _phaseCount;
+}
+
+int32 Animation::getFrameCount() const {
+ return _frameCount;
+}
+
+int16 Animation::getBaseX() const {
+ return _baseX;
+}
+
+int16 Animation::getBaseY() const {
+ return _baseY;
+}
+
+int16 Animation::getPhaseOffsetX(int phaseIndex) const {
+ if (phaseIndex < _phaseCount) {
+ return _phaseList[phaseIndex]._phaseOffsetX;
+ } else {
+ error("getPhaseOffsetX() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount);
+ }
+}
+
+int16 Animation::getPhaseOffsetY(int phaseIndex) const {
+ if (phaseIndex < _phaseCount) {
+ return _phaseList[phaseIndex]._phaseOffsetY;
+ } else {
+ error("getPhaseOffsetY() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount);
+ }
+}
+
+int16 Animation::getPhaseFrameIndex(int phaseIndex) const {
+ if (phaseIndex < _phaseCount) {
+ return _phaseList[phaseIndex]._phaseToFrameIndex;
+ } else {
+ error("getPhaseFrameIndex() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount);
+ }
+}
+
+Graphics::Surface *Animation::getFrame(int frameIndex) {
+ if (frameIndex < _frameCount) {
+ if (_frameList[frameIndex]._isCompressed) {
+ Decompressor dec;
+ byte *ddata = (byte *)malloc(_frameList[frameIndex]._dataSize);
+ dec.decompress(_frameList[frameIndex]._compressedData, ddata, _frameList[frameIndex]._dataSize);
+ int frameHeight = _frameList[frameIndex]._surface->h;
+ int frameWidth = _frameList[frameIndex]._surface->w;
+ for (uint16 i = 0; i < frameHeight; i++) {
+ memcpy(_frameList[frameIndex]._surface->getBasePtr(0, i), ddata + frameWidth * i, frameWidth);
+ }
+ free(ddata);
+ free(_frameList[frameIndex]._compressedData);
+ _frameList[frameIndex]._compressedData = nullptr;
+ _frameList[frameIndex]._dataSize = 0;
+ _frameList[frameIndex]._isCompressed = false;
+ }
+ return _frameList[frameIndex]._surface;
+ } else {
+ error("getFrame() frameIndex: %d, frameCount: %d", frameIndex, _frameCount);
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/animation.h b/engines/prince/animation.h
new file mode 100644
index 0000000000..733acb399c
--- /dev/null
+++ b/engines/prince/animation.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_ANIMATION_H
+#define PRINCE_ANIMATION_H
+
+#include "common/array.h"
+#include "common/stream.h"
+
+#include "graphics/surface.h"
+
+namespace Prince {
+
+class Animation {
+public:
+ Animation();
+ ~Animation();
+ bool loadStream(Common::SeekableReadStream &stream);
+
+ int16 getLoopCount() const;
+ int32 getPhaseCount() const;
+ int32 getFrameCount() const;
+ int16 getBaseX() const;
+ int16 getBaseY() const;
+ int16 getPhaseOffsetX(int phaseIndex) const;
+ int16 getPhaseOffsetY(int phaseIndex) const;
+ int16 getPhaseFrameIndex(int phaseIndex) const;
+ Graphics::Surface *getFrame(int frameIndex);
+ void clear();
+
+private:
+ struct Phase {
+ int16 _phaseOffsetX;
+ int16 _phaseOffsetY;
+ uint16 _phaseToFrameIndex;
+ };
+ struct Frame {
+ bool _isCompressed;
+ uint32 _dataSize;
+ byte *_compressedData;
+ Graphics::Surface *_surface;
+ };
+ Common::Array<Frame> _frameList;
+ Common::Array<Phase> _phaseList;
+ int16 _loopCount;
+ int16 _phaseCount;
+ int32 _frameCount;
+ int16 _baseX;
+ int16 _baseY;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/archive.cpp b/engines/prince/archive.cpp
new file mode 100644
index 0000000000..ae6a2b7546
--- /dev/null
+++ b/engines/prince/archive.cpp
@@ -0,0 +1,144 @@
+/* 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 "prince/archive.h"
+#include "prince/decompress.h"
+
+#include "common/stream.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+
+namespace Prince {
+
+PtcArchive::PtcArchive() : _stream(nullptr) {
+}
+
+PtcArchive::~PtcArchive() {
+ close();
+}
+
+static void decrypt(byte *buffer, uint32 size) {
+ uint32 key = 0xDEADF00D;
+ while (size--) {
+ *buffer++ += key & 0xFF;
+ key ^= 0x2E84299A;
+ key += MKTAG('B', 'L', 'A', 'H');
+ key = ((key & 1) << 31) | (key >> 1);
+ }
+}
+
+bool PtcArchive::open(const Common::String &filename) {
+ _stream = SearchMan.createReadStreamForMember(filename);
+ if (!_stream)
+ return false;
+
+ _stream->readUint32LE(); // magic
+ uint32 fileTableOffset = _stream->readUint32LE() ^ 0x4D4F4B2D; // MOK-
+ uint32 fileTableSize = _stream->readUint32LE() ^ 0x534F4654; // SOFT
+
+ //debug("fileTableOffset : %08X", fileTableOffset);
+ //debug("fileTableSize: %08X", fileTableSize);
+
+ _stream->seek(fileTableOffset);
+
+ byte *fileTable = (byte *)malloc(fileTableSize);
+ byte *fileTableEnd = fileTable + fileTableSize;
+ _stream->read(fileTable, fileTableSize);
+ decrypt(fileTable, fileTableSize);
+
+ for (byte *fileItem = fileTable; fileItem < fileTableEnd; fileItem += 32) {
+ FileEntry item;
+ Common::String name = (const char*)fileItem;
+ item._offset = READ_LE_UINT32(fileItem + 24);
+ item._size = READ_LE_UINT32(fileItem + 28);
+ //debug("%12s %8X %d", name.c_str(), item._offset, item._size);
+ _items[name] = item;
+ }
+
+ free(fileTable);
+
+ return true;
+}
+
+void PtcArchive::close() {
+ delete _stream;
+ _stream = nullptr;
+ _items.clear();
+}
+
+bool PtcArchive::hasFile(const Common::String &name) const {
+ // TODO: check if path matching should be added
+ return _items.contains(name);
+}
+
+int PtcArchive::listMembers(Common::ArchiveMemberList &list) const {
+ int matches = 0;
+
+ for (FileMap::const_iterator it = _items.begin(); it != _items.end(); ++it) {
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_key, this)));
+ matches++;
+ }
+
+ return matches;
+}
+
+const Common::ArchiveMemberPtr PtcArchive::getMember(const Common::String &name) const {
+ if (!_items.contains(name)) {
+ Common::ArchiveMemberPtr();
+ }
+ return Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *PtcArchive::createReadStreamForMember(const Common::String &name) const {
+ if (!_items.contains(name)) {
+ return 0;
+ }
+
+ const FileEntry &entryHeader = _items[name];
+
+ if (entryHeader._size < 4)
+ return 0;
+
+ uint32 size = entryHeader._size;
+
+ _stream->seek(entryHeader._offset);
+
+ // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory
+ byte *buffer = (byte *)malloc(size);
+ _stream->read(buffer, size);
+
+ if (READ_BE_UINT32(buffer) == MKTAG('M', 'A', 'S', 'M')) {
+ Decompressor dec;
+ uint32 decompLen = READ_BE_UINT32(buffer + 14);
+ byte *decompData = (byte *)malloc(decompLen);
+ dec.decompress(buffer + 18, decompData, decompLen);
+ free(buffer);
+ size = decompLen;
+ buffer = decompData;
+ }
+
+ //debug("PtcArchive::createReadStreamForMember name %s", name.c_str());
+
+ return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/archive.h b/engines/prince/archive.h
new file mode 100644
index 0000000000..e211036ed6
--- /dev/null
+++ b/engines/prince/archive.h
@@ -0,0 +1,61 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_ARCHIVE_H
+#define PRINCE_ARCHIVE_H
+
+#include "common/archive.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Prince {
+
+class PtcArchive : public Common::Archive {
+public:
+ PtcArchive();
+ ~PtcArchive();
+
+ bool open(const Common::String &filename);
+ void close();
+ bool isOpen() const { return _stream != 0; }
+
+ // Common::Archive API implementation
+ bool hasFile(const Common::String &name) const;
+ int listMembers(Common::ArchiveMemberList &list) const;
+ const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+private:
+ struct FileEntry {
+ uint32 _offset;
+ uint32 _size;
+ };
+
+ Common::SeekableReadStream *_stream;
+
+ typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+ FileMap _items;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/common.h b/engines/prince/common.h
new file mode 100644
index 0000000000..c846f9a751
--- /dev/null
+++ b/engines/prince/common.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_COMMON_H
+#define PRINCE_COMMON_H
+
+namespace Prince {
+
+enum Direction {
+ kDirLD,
+ kDirL,
+ kDirLU,
+ kDirRD,
+ kDirR,
+ kDirRU,
+ kDirUL,
+ kDirU,
+ kDirUR,
+ kDirDL,
+ kDirD,
+ kDirDR
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/configure.engine b/engines/prince/configure.engine
new file mode 100644
index 0000000000..50740d9f41
--- /dev/null
+++ b/engines/prince/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine prince "The Prince and The Coward" no
diff --git a/engines/prince/cursor.cpp b/engines/prince/cursor.cpp
new file mode 100644
index 0000000000..ddcabbd28f
--- /dev/null
+++ b/engines/prince/cursor.cpp
@@ -0,0 +1,54 @@
+/* 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 "prince/cursor.h"
+
+#include "common/debug.h"
+
+namespace Prince {
+
+Cursor::Cursor() : _surface(nullptr) {
+}
+
+Cursor::~Cursor() {
+ if (_surface != nullptr) {
+ _surface->free();
+ delete _surface;
+ _surface = nullptr;
+ }
+}
+
+bool Cursor::loadStream(Common::SeekableReadStream &stream) {
+ stream.skip(4);
+ uint16 width = stream.readUint16LE();
+ uint16 height = stream.readUint16LE();
+
+ _surface = new Graphics::Surface();
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int h = 0; h < height; h++) {
+ stream.read(_surface->getBasePtr(0, h), width);
+ }
+ return true;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/cursor.h b/engines/prince/cursor.h
new file mode 100644
index 0000000000..9387f344dc
--- /dev/null
+++ b/engines/prince/cursor.h
@@ -0,0 +1,46 @@
+/* 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.
+ *
+ */
+
+#ifndef CURSOR_PRINCE_H
+#define CURSOR_PRINCE_H
+
+#include "graphics/surface.h"
+
+#include "common/stream.h"
+
+namespace Prince {
+
+class Cursor {
+public:
+ Cursor();
+ ~Cursor();
+
+ bool loadStream(Common::SeekableReadStream &stream);
+ const Graphics::Surface *getSurface() const { return _surface; }
+
+private:
+ Graphics::Surface *_surface;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/curve_values.h b/engines/prince/curve_values.h
new file mode 100644
index 0000000000..d72f11fd36
--- /dev/null
+++ b/engines/prince/curve_values.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ */
+
+namespace Prince {
+
+const int curveValues[17][4] = {
+ { 32768, 0, 0, 0 },
+ { 25200, 7200, 480, -112 },
+ { 18816, 12544, 1792, -384 },
+ { 13520, 16224, 3744, -720 },
+ { 9216, 18432, 6144, -1024 },
+ { 5808, 19360, 8800, -1200 },
+ { 3200, 19200, 11520, -1152 },
+ { 1296, 18144, 14112, -784 },
+ { 0, 16384, 16384, 0 },
+ { -784, 14112, 18144, 1296 },
+ { -1152, 11520, 19200, 3200 },
+ { -1200, 8800, 19360, 5808 },
+ { -1024, 6144, 18432, 9216 },
+ { -720, 3744, 16224, 13520 },
+ { -384, 1792, 12544, 18816 },
+ { -112, 480, 7200, 25200 },
+ { 0, 0, 0, 32768 }
+};
+
+} // End of namespace Prince
diff --git a/engines/prince/debugger.cpp b/engines/prince/debugger.cpp
new file mode 100644
index 0000000000..481ea695f5
--- /dev/null
+++ b/engines/prince/debugger.cpp
@@ -0,0 +1,174 @@
+/* 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 "prince/debugger.h"
+#include "prince/prince.h"
+#include "prince/flags.h"
+#include "prince/script.h"
+
+namespace Prince {
+
+Debugger::Debugger(PrinceEngine *vm, InterpreterFlags *flags) : GUI::Debugger(), _vm(vm), _locationNr(0), _flags(flags) {
+ DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
+ DCmd_Register("level", WRAP_METHOD(Debugger, Cmd_DebugLevel));
+ DCmd_Register("setflag", WRAP_METHOD(Debugger, Cmd_SetFlag));
+ DCmd_Register("getflag", WRAP_METHOD(Debugger, Cmd_GetFlag));
+ DCmd_Register("clearflag", WRAP_METHOD(Debugger, Cmd_ClearFlag));
+ DCmd_Register("viewflc", WRAP_METHOD(Debugger, Cmd_ViewFlc));
+ DCmd_Register("initroom", WRAP_METHOD(Debugger, Cmd_InitRoom));
+ DCmd_Register("changecursor", WRAP_METHOD(Debugger, Cmd_ChangeCursor));
+ DCmd_Register("additem", WRAP_METHOD(Debugger, Cmd_AddItem));
+}
+
+static int strToInt(const char *s) {
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) != 'H')
+ // Standard decimal string
+ return atoi(s);
+
+ // Hexadecimal string
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
+ return (int)tmp;
+}
+
+bool Debugger::Cmd_DebugLevel(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Debugging is currently set at level %d\n", gDebugLevel);
+ } else { // set level
+ gDebugLevel = atoi(argv[1]);
+ if (0 <= gDebugLevel && gDebugLevel < 11) {
+ DebugPrintf("Debug level set to level %d\n", gDebugLevel);
+ } else if (gDebugLevel < 0) {
+ DebugPrintf("Debugging is now disabled\n");
+ } else
+ DebugPrintf("Not a valid debug level (0 - 10)\n");
+ }
+
+ return true;
+}
+
+/*
+ * This command sets a flag
+ */
+bool Debugger::Cmd_SetFlag(int argc, const char **argv) {
+ // Check for a flag to set
+ if (argc != 3) {
+ DebugPrintf("Usage: %s <flag number> <value>\n", argv[0]);
+ return true;
+ }
+
+ int flagNum = strToInt(argv[1]);
+ uint16 value = strToInt(argv[2]);
+ _flags->setFlagValue((Flags::Id)flagNum, value);
+ return true;
+}
+
+/*
+ * This command gets the value of a flag
+ */
+bool Debugger::Cmd_GetFlag(int argc, const char **argv) {
+ // Check for an flag to display
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <flag number>\n", argv[0]);
+ return true;
+ }
+
+ int flagNum = strToInt(argv[1]);
+ DebugPrintf("Value: %d\n", _flags->getFlagValue((Flags::Id)flagNum));
+ return true;
+}
+
+/*
+ * This command clears a flag
+ */
+bool Debugger::Cmd_ClearFlag(int argc, const char **argv) {
+ // Check for a flag to clear
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <flag number>\n", argv[0]);
+ return true;
+ }
+
+ int flagNum = strToInt(argv[1]);
+ _flags->setFlagValue((Flags::Id)flagNum, 0);
+ return true;
+}
+
+/*
+ * This command starts new flc anim
+ */
+bool Debugger::Cmd_ViewFlc(int argc, const char **argv) {
+ // Check for a flag to clear
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <anim number>\n", argv[0]);
+ return true;
+ }
+
+ int flagNum = strToInt(argv[1]);
+ _vm->loadAnim(flagNum, false);
+ return true;
+}
+
+bool Debugger::Cmd_InitRoom(int argc, const char **argv) {
+ // Check for a flag to clear
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <anim number>\n", argv[0]);
+ return true;
+ }
+
+ _locationNr = strToInt(argv[1]);
+ return true;
+}
+
+bool Debugger::Cmd_ChangeCursor(int argc, const char **argv) {
+ // Check for a flag to clear
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <curId>\n", argv[0]);
+ return true;
+ }
+
+ _cursorNr = strToInt(argv[1]);
+
+ return true;
+}
+
+bool Debugger::Cmd_AddItem(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage: %s <itemId>\n", argv[0]);
+ return true;
+ }
+ if (!strcmp(argv[1], "map")) {
+ _vm->addInv(0, 29, true);
+ _vm->_flags->setFlagValue(Flags::MapaUsable, 1);
+ } else {
+ int itemId = strToInt(argv[1]);
+ _vm->addInv(0, itemId, true);
+ }
+
+ return true;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/debugger.h b/engines/prince/debugger.h
new file mode 100644
index 0000000000..a4467e63d5
--- /dev/null
+++ b/engines/prince/debugger.h
@@ -0,0 +1,58 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_DEBUGGER_H
+#define PRINCE_DEBUGGER_H
+
+#include "common/scummsys.h"
+#include "gui/debugger.h"
+
+namespace Prince {
+
+class PrinceEngine;
+class InterpreterFlags;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(PrinceEngine *vm, InterpreterFlags *flags);
+ virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+ uint8 _locationNr;
+ uint8 _cursorNr;
+
+private:
+ bool Cmd_DebugLevel(int argc, const char **argv);
+ bool Cmd_SetFlag(int argc, const char **argv);
+ bool Cmd_GetFlag(int argc, const char **argv);
+ bool Cmd_ClearFlag(int argc, const char **argv);
+ bool Cmd_ViewFlc(int argc, const char **argv);
+ bool Cmd_InitRoom(int argc, const char **argv);
+ bool Cmd_ChangeCursor(int argc, const char **argv);
+ bool Cmd_AddItem(int argc, const char **argv);
+
+ PrinceEngine *_vm;
+ InterpreterFlags *_flags;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/decompress.cpp b/engines/prince/decompress.cpp
new file mode 100644
index 0000000000..7fba179541
--- /dev/null
+++ b/engines/prince/decompress.cpp
@@ -0,0 +1,171 @@
+/* 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.
+ *
+ */
+
+// John_Doe's implementation
+
+#include "prince/decompress.h"
+
+namespace Prince {
+
+static const uint16 table1[] = {
+ 0x8000, 0x0002,
+ 0x4000, 0x0004,
+ 0x2000, 0x0008,
+ 0x1000, 0x0010,
+ 0x0800, 0x0020,
+ 0x0400, 0x0040,
+ 0x0200, 0x0080,
+ 0x0100, 0x0100,
+ 0x0080, 0x0200,
+ 0x0040, 0x0400
+};
+
+static const uint32 table2[] = {
+ 0x0000F000,
+ 0x0020FC00,
+ 0x00A0FF00,
+ 0x02A0FF80,
+ 0x06A0FFC0,
+ 0x0EA0FFE0,
+ 0x1EA0FFF0,
+ 0x3EA0FFF8
+};
+
+static const uint16 table3[] = {
+ 0x8000, 0x0000,
+ 0x4000, 0x0002,
+ 0x2000, 0x0006,
+ 0x1000, 0x000E,
+ 0x0800, 0x001E,
+ 0x0400, 0x003E,
+ 0x0200, 0x007E,
+ 0x0100, 0x00FE,
+ 0x0080, 0x01FE,
+ 0x0040, 0x03FE,
+ 0x0020, 0x07FE,
+ 0x0010, 0x0FFE,
+ 0x0008, 0x1FFE,
+ 0x0004, 0x3FFE,
+ 0x0002, 0x7FFE,
+ 0x0001, 0xFFFE
+};
+
+void Decompressor::decompress(byte *source, byte *dest, uint32 destSize) {
+ byte *destEnd = dest + destSize;
+ int more;
+ _src = source;
+ _dst = dest;
+ _bitBuffer = 0x80;
+ while (_dst < destEnd) {
+ uint32 ebp;
+ uint16 offset, length;
+ if (getBit()) {
+ if (getBit()) {
+ if (getBit()) {
+ if (getBit()) {
+ if (getBit()) {
+ if (getBit()) {
+ uint32 tableIndex = 0;
+ while (getBit())
+ tableIndex++;
+ length = table3[tableIndex * 2 + 0];
+ do {
+ more = !(length & 0x8000);
+ length = (length << 1) | getBit();
+ } while (more);
+ length += table3[tableIndex * 2 + 1];
+ length++;
+ memcpy(_dst, _src, length);
+ _src += length;
+ _dst += length;
+ }
+ *_dst++ = *_src++;
+ }
+ *_dst++ = *_src++;
+ }
+ *_dst++ = *_src++;
+ }
+ *_dst++ = *_src++;
+ }
+ *_dst++ = *_src++;
+ }
+ if (!getBit()) {
+ if (getBit()) {
+ uint32 tableIndex = getBit();
+ tableIndex = (tableIndex << 1) | getBit();
+ tableIndex = (tableIndex << 1) | getBit();
+ ebp = table2[tableIndex];
+ length = 1;
+ } else {
+ ebp = 0x0000FF00;
+ length = 0;
+ }
+ } else {
+ uint32 tableIndex = 0;
+ while (getBit())
+ tableIndex++;
+ length = table1[tableIndex * 2 + 0];
+ do {
+ more = !(length & 0x8000);
+ length = (length << 1) | getBit();
+ } while (more);
+ length += table1[tableIndex * 2 + 1];
+ tableIndex = getBit();
+ tableIndex = (tableIndex << 1) | getBit();
+ tableIndex = (tableIndex << 1) | getBit();
+ ebp = table2[tableIndex];
+ }
+ offset = ebp & 0xFFFF;
+ do {
+ if (_bitBuffer == 0x80) {
+ if (offset >= 0xFF00) {
+ offset = (offset << 8) | *_src++;
+ }
+ }
+ more = offset & 0x8000;
+ offset = (offset << 1) | getBit();
+ } while (more);
+ offset += (ebp >> 16);
+ length += 2;
+ while (length--) {
+ if (_dst >= destEnd) {
+ return;
+ }
+ *_dst = *(_dst - offset);
+ _dst++;
+ }
+ }
+}
+
+int Decompressor::getBit() {
+ int bit = (_bitBuffer & 0x80) >> 7;
+ _bitBuffer <<= 1;
+ if (_bitBuffer == 0) {
+ _bitBuffer = *_src++;
+ bit = (_bitBuffer & 0x80) >> 7;
+ _bitBuffer <<= 1;
+ _bitBuffer |= 1;
+ }
+ return bit;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/decompress.h b/engines/prince/decompress.h
new file mode 100644
index 0000000000..ef495db65e
--- /dev/null
+++ b/engines/prince/decompress.h
@@ -0,0 +1,44 @@
+/* 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.
+ *
+ */
+
+// John_Doe's implementation
+
+#ifndef PRINCE_DECOMPRESS_H
+#define PRINCE_DECOMPRESS_H
+
+#include "engines/util.h"
+
+namespace Prince {
+
+class Decompressor {
+public:
+ void decompress(byte *source, byte *dest, uint32 destSize);
+protected:
+ byte *_src, *_dst;
+ byte _bitBuffer;
+ int _bitsLeft;
+ int getBit();
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp
new file mode 100644
index 0000000000..3f83009de8
--- /dev/null
+++ b/engines/prince/detection.cpp
@@ -0,0 +1,73 @@
+/* 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 "prince/detection.h"
+
+namespace Prince {
+
+int PrinceEngine::getGameType() const {
+ return _gameDescription->gameType;
+}
+
+const char *PrinceEngine::getGameId() const {
+ return _gameDescription->desc.gameid;
+}
+
+uint32 PrinceEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language PrinceEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ using namespace Prince;
+ const PrinceGameDescription *gd = (const PrinceGameDescription *)desc;
+ if (gd) {
+ *engine = new PrinceEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSupportsListSaves);
+}
+
+bool Prince::PrinceEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+} // End of namespace Prince
+
+#if PLUGIN_ENABLED_DYNAMIC(PRINCE)
+REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine);
+#endif
diff --git a/engines/prince/detection.h b/engines/prince/detection.h
new file mode 100644
index 0000000000..5cc0d32be4
--- /dev/null
+++ b/engines/prince/detection.h
@@ -0,0 +1,100 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_DETECTION_H
+#define PRINCE_DETECTION_H
+
+#include "prince/prince.h"
+#include "engines/advancedDetector.h"
+
+namespace Prince {
+
+struct PrinceGameDescription {
+ ADGameDescription desc;
+ int gameType;
+};
+
+static const PlainGameDescriptor princeGames[] = {
+ {"prince", "Prince Game"},
+ {0, 0}
+};
+
+static const PrinceGameDescription gameDescriptions[] = {
+ {
+ {
+ "prince",
+ "Galador",
+ AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_TESTING,
+ GUIO1(GUIO_NONE)
+ },
+ 0
+ },
+ {
+ {
+ "prince",
+ "Ksiaze i Tchorz",
+ AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298),
+ Common::PL_POL,
+ Common::kPlatformWindows,
+ ADGF_TESTING,
+ GUIO1(GUIO_NONE)
+ },
+ 1
+ },
+ { AD_TABLE_END_MARKER, 0 }
+};
+
+const static char *directoryGlobs[] = {
+ "all",
+ 0
+};
+
+class PrinceMetaEngine : public AdvancedMetaEngine {
+public:
+ PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) {
+ _singleid = "prince";
+ _maxScanDepth = 2;
+ _directoryGlobs = directoryGlobs;
+ }
+
+ virtual const char *getName() const {
+ return "Prince Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "The Prince and the Coward (C) 1996-97 Metropolis";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual SaveStateList listSaves(const char *target) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ virtual void removeSaveState(const char *target, int slot) const;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/flags.cpp b/engines/prince/flags.cpp
new file mode 100644
index 0000000000..f1a05bd4df
--- /dev/null
+++ b/engines/prince/flags.cpp
@@ -0,0 +1,420 @@
+/* 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 "prince/flags.h"
+#include "prince/script.h"
+
+namespace Prince {
+
+const char *Flags::getFlagName(uint16 flagId) {
+ FlagDebug *flagd = nullptr;
+ flagd = (FlagDebug *)bsearch(&flagId, _flagNames, kFlagDebugAmount, sizeof(FlagDebug), Flags::compareFlagDebug);
+ if (flagd != nullptr) {
+ return flagd->flagName;
+ } else {
+ return "unknown_flag";
+ }
+}
+
+int Flags::compareFlagDebug(const void *a, const void *b) {
+ const uint32 *flagId = (const uint32 *)a;
+ const FlagDebug *entry = (const FlagDebug *)b;
+ if (*flagId < (uint32)entry->id) {
+ return -1;
+ } else if (*flagId > (uint32)entry->id) {
+ return 1;
+ }
+ return 0;
+}
+
+const Flags::FlagDebug Flags::_flagNames[Flags::kFlagDebugAmount] = {
+ { Flags::FLAGA1, "FLAGA1" },
+ { Flags::FLAGA2, "FLAGA2" },
+ { Flags::FLAGA3, "FLAGA3" },
+ { Flags::DESTX, "DESTX" },
+ { Flags::DESTY, "DESTY" },
+ { Flags::DESTD, "DESTD" },
+ { Flags::DwarfDone, "DwarfDone" },
+ { Flags::GRABARZCOUNTER, "GRABARZCOUNTER" },
+ { Flags::KIERUNEK, "KIERUNEK" },
+ { Flags::BACKFLAG1, "BACKFLAG1" },
+ { Flags::BACKFLAG2, "BACKFLAG2" },
+ { Flags::BACKFLAG3, "BACKFLAG3" },
+ { Flags::BACKFLAG4, "BACKFLAG4" },
+ { Flags::MACROFLAG1, "MACROFLAG1" },
+ { Flags::MACROFLAG2, "MACROFLAG2" },
+ { Flags::MACROFLAG3, "MACROFLAG3" },
+ { Flags::HEROLDDONE, "HEROLDDONE" },
+ { Flags::BRIDGESET, "BRIDGESET" },
+ { Flags::U_BT_1, "U_BT_1" },
+ { Flags::U_BT_2, "U_BT_2" },
+ { Flags::U_BT_3, "U_BT_3" },
+ { Flags::U_BT_4, "U_BT_4" },
+ { Flags::U_BT_5, "U_BT_5" },
+ { Flags::U_BT_6, "U_BT_6" },
+ { Flags::U_BT_7, "U_BT_7" },
+ { Flags::U_BT_8, "U_BT_8" },
+ { Flags::U_BT_9, "U_BT_9" },
+ { Flags::U_BT_COUNTER, "U_BT_COUNTER" },
+ { Flags::ARIVALDALIVE, "ARIVALDALIVE" },
+ { Flags::TALKCHAR1, "TALKCHAR1" },
+ { Flags::TalkType1, "TalkType1" },
+ { Flags::TALKROUT1, "TALKROUT1" },
+ { Flags::TALKROUT2, "TALKROUT2" },
+ { Flags::TALKROUT3, "TALKROUT3" },
+ { Flags::TALKROUT4, "TALKROUT4" },
+ { Flags::TALKANIM1, "TALKANIM1" },
+ { Flags::TALKANIM2, "TALKANIM2" },
+ { Flags::TALKCOLOR1, "TALKCOLOR1" },
+ { Flags::TALKCOLOR2, "TALKCOLOR2" },
+ { Flags::KapciuchTaken, "KapciuchTaken" },
+ { Flags::CurrentBeggarA, "CurrentBeggarA" },
+ { Flags::TempKapc, "TempKapc" },
+ { Flags::HomTaken, "HomTaken" },
+ { Flags::WizardTalk, "WizardTalk" },
+ { Flags::SunlordTalk, "SunlordTalk" },
+ { Flags::HermitTalk, "HermitTalk" },
+ { Flags::RunyMode, "RunyMode" },
+ { Flags::FatMerchantTalk, "FatMerchantTalk" },
+ { Flags::HotDogTalk, "HotDogTalk" },
+ { Flags::ThiefTalk, "ThiefTalk" },
+ { Flags::BeggarTalk, "BeggarTalk" },
+ { Flags::MonkTalk, "MonkTalk" },
+ { Flags::BardTalk, "BardTalk" },
+ { Flags::BarmanTalk, "BarmanTalk" },
+ { Flags::LeftPlayerTalk, "LeftPlayerTalk" },
+ { Flags::OczySowy, "OczySowy" },
+ { Flags::CzachySpeed1, "CzachySpeed1" },
+ { Flags::CzachySpeed2, "CzachySpeed2" },
+ { Flags::CzachySpeed3, "CzachySpeed3" },
+ { Flags::CzachySlowDown1, "CzachySlowDown1" },
+ { Flags::CzachySlowDown2, "CzachySlowDown2" },
+ { Flags::CzachySlowDown3, "CzachySlowDown3" },
+ { Flags::FjordDane, "FjordDane" },
+ { Flags::GKopany1, "GKopany1" },
+ { Flags::GKopany2, "GKopany2" },
+ { Flags::GKopany3, "GKopany3" },
+ { Flags::GKopany4, "GKopany4" },
+ { Flags::KnowGodWord, "KnowGodWord" },
+ { Flags::TALKROUT21, "TALKROUT21" },
+ { Flags::TALKROUT22, "TALKROUT22" },
+ { Flags::TALKROUT23, "TALKROUT23" },
+ { Flags::TALKROUT24, "TALKROUT24" },
+ { Flags::TalkType2, "TalkType2" },
+ { Flags::GrabarzTalk, "GrabarzTalk" },
+ { Flags::LastTalker, "LastTalker" },
+ { Flags::MapaPustelniaEnabled, "MapaPustelniaEnabled" },
+ { Flags::MapaTempleEnabled, "MapaTempleEnabled" },
+ { Flags::MapaFjordEnabled, "MapaFjordEnabled" },
+ { Flags::MapaSilmanionaEnabled, "MapaSilmanionaEnabled" },
+ { Flags::MapaKurhanEnabled, "MapaKurhanEnabled" },
+ { Flags::MapaDragonEnabled, "MapaDragonEnabled" },
+ { Flags::MapaMillEnabled, "MapaMillEnabled" },
+ { Flags::DwarfRunning, "DwarfRunning" },
+ { Flags::DwarfTalk, "DwarfTalk" },
+ { Flags::CurseLift, "CurseLift" },
+ { Flags::KosciSwapped, "KosciSwapped" },
+ { Flags::BookStolen, "BookStolen" },
+ { Flags::MapaUsable, "MapaUsable" },
+ { Flags::FjordBoss, "FjordBoss" },
+ { Flags::FjordHotDog, "FjordHotDog" },
+ { Flags::FjordLewy, "FjordLewy" },
+ { Flags::FjordPrawy, "FjordPrawy" },
+ { Flags::TalkArivald, "TalkArivald" },
+ { Flags::ShootDone, "ShootDone" },
+ { Flags::ShootRunning, "ShootRunning" },
+ { Flags::ShootKnow, "ShootKnow" },
+ { Flags::MirrorKnow, "MirrorKnow" },
+ { Flags::Gar1stTime, "Gar1stTime" },
+ { Flags::KosciTaken, "KosciTaken" },
+ { Flags::ArivGotSpell, "ArivGotSpell" },
+ { Flags::BookGiven, "BookGiven" },
+ { Flags::Wywieszka, "Wywieszka" },
+ { Flags::TalkSheila, "TalkSheila" },
+ { Flags::TalkSheila2, "TalkSheila2" },
+ { Flags::BackHuman, "BackHuman" },
+ { Flags::SkarbiecOpen, "SkarbiecOpen" },
+ { Flags::LustroTaken, "LustroTaken" },
+ { Flags::GargoyleHom, "GargoyleHom" },
+ { Flags::GargoyleBroken, "GargoyleBroken" },
+ { Flags::FjordDzien, "FjordDzien" },
+ { Flags::GargoyleHom2, "GargoyleHom2" },
+ { Flags::RunMonstersRunning, "RunMonstersRunning" },
+ { Flags::FoundPaperInCoffin, "FoundPaperInCoffin" },
+ { Flags::KnowSunlord, "KnowSunlord" },
+ { Flags::KnowSunlordTalk, "KnowSunlordTalk" },
+ { Flags::ArivaldCzyta, "ArivaldCzyta" },
+ { Flags::TelepX, "TelepX" },
+ { Flags::TelepY, "TelepY" },
+ { Flags::TelepDir, "TelepDir" },
+ { Flags::TelepRoom, "TelepRoom" },
+ { Flags::ListStolen, "ListStolen" },
+ { Flags::WifeInDoor, "WifeInDoor" },
+ { Flags::TalkWifeFlag, "TalkWifeFlag" },
+ { Flags::LetterGiven, "LetterGiven" },
+ { Flags::LutniaTaken, "LutniaTaken" },
+ { Flags::BardHomeOpen, "BardHomeOpen" },
+ { Flags::FjordNoMonsters, "FjordNoMonsters" },
+ { Flags::ShandriaWallTalking, "ShandriaWallTalking" },
+ { Flags::ShandriaWallCounter, "ShandriaWallCounter" },
+ { Flags::ShandriaWallDone, "ShandriaWallDone" },
+ { Flags::FutureDone, "FutureDone" },
+ { Flags::TalkButch, "TalkButch" },
+ { Flags::GotSzalik, "GotSzalik" },
+ { Flags::GotCzosnek, "GotCzosnek" },
+ { Flags::BearDone, "BearDone" },
+ { Flags::NekrVisited, "NekrVisited" },
+ { Flags::SunRiddle, "SunRiddle" },
+ { Flags::PtaszekAway, "PtaszekAway" },
+ { Flags::KotGadanie, "KotGadanie" },
+ { Flags::SzlafmycaTaken, "SzlafmycaTaken" },
+ { Flags::BabkaTalk, "BabkaTalk" },
+ { Flags::SellerTalk, "SellerTalk" },
+ { Flags::CzosnekDone, "CzosnekDone" },
+ { Flags::PriestCounter, "PriestCounter" },
+ { Flags::PriestGest1, "PriestGest1" },
+ { Flags::PriestGest2, "PriestGest2" },
+ { Flags::PriestGest3, "PriestGest3" },
+ { Flags::PriestGest4, "PriestGest4" },
+ { Flags::PriestAnim, "PriestAnim" },
+ { Flags::HolyWaterTaken, "HolyWaterTaken" },
+ { Flags::AxeTaken, "AxeTaken" },
+ { Flags::BadylTaken1, "BadylTaken1" },
+ { Flags::BadylTaken2, "BadylTaken2" },
+ { Flags::BadylSharpened, "BadylSharpened" },
+ { Flags::PorwanieSmoka, "PorwanieSmoka" },
+ { Flags::ShopReOpen, "ShopReOpen" },
+ { Flags::LuskaShown, "LuskaShown" },
+ { Flags::CudKnow, "CudKnow" },
+ { Flags::VampireDead, "VampireDead" },
+ { Flags::MapaVisible1, "MapaVisible1" },
+ { Flags::MapaVisible2, "MapaVisible2" },
+ { Flags::MapaVisible3, "MapaVisible3" },
+ { Flags::MapaVisible4, "MapaVisible4" },
+ { Flags::MapaVisible5, "MapaVisible5" },
+ { Flags::MapaVisible6, "MapaVisible6" },
+ { Flags::MapaVisible7, "MapaVisible7" },
+ { Flags::MapaVisible8, "MapaVisible8" },
+ { Flags::MapaVisible9, "MapaVisible9" },
+ { Flags::MapaX, "MapaX" },
+ { Flags::MapaY, "MapaY" },
+ { Flags::MapaD, "MapaD" },
+ { Flags::OldMapaX, "OldMapaX" },
+ { Flags::OldMapaY, "OldMapaY" },
+ { Flags::OldMapaD, "OldMapaD" },
+ { Flags::MovingBack, "MovingBack" },
+ { Flags::MapaCount, "MapaCount" },
+ { Flags::Pustelnia1st, "Pustelnia1st" },
+ { Flags::CzarnePole1st, "CzarnePole1st" },
+ { Flags::TalkArivNum, "TalkArivNum" },
+ { Flags::Pfui, "Pfui" },
+ { Flags::MapaSunlordEnabled, "MapaSunlordEnabled" },
+ { Flags::WebDone, "WebDone" },
+ { Flags::DragonDone, "DragonDone" },
+ { Flags::KanPlay, "KanPlay" },
+ { Flags::OldKanPlay, "OldKanPlay" },
+ { Flags::LapkiWait, "LapkiWait" },
+ { Flags::WebNoCheck, "WebNoCheck" },
+ { Flags::Perfumeria, "Perfumeria" },
+ { Flags::SmokNoCheck, "SmokNoCheck" },
+ { Flags::IluzjaBroken, "IluzjaBroken" },
+ { Flags::IluzjaWorking, "IluzjaWorking" },
+ { Flags::IluzjaCounter, "IluzjaCounter" },
+ { Flags::KurhanOpen1, "KurhanOpen1" },
+ { Flags::KastetTaken, "KastetTaken" },
+ { Flags::KastetDown, "KastetDown" },
+ { Flags::KurhanDone, "KurhanDone" },
+ { Flags::SkelCounter, "SkelCounter" },
+ { Flags::SkelDial1, "SkelDial1" },
+ { Flags::SkelDial2, "SkelDial2" },
+ { Flags::SkelDial3, "SkelDial3" },
+ { Flags::SkelDial4, "SkelDial4" },
+ { Flags::SameTalker, "SameTalker" },
+ { Flags::RunMonstersText, "RunMonstersText" },
+ { Flags::PiwnicaChecked, "PiwnicaChecked" },
+ { Flags::DragonTalked, "DragonTalked" },
+ { Flags::ToldAboutBook, "ToldAboutBook" },
+ { Flags::SilmanionaDone, "SilmanionaDone" },
+ { Flags::ToldBookCount, "ToldBookCount" },
+ { Flags::SmrodNoCheck, "SmrodNoCheck" },
+ { Flags::RopeTaken, "RopeTaken" },
+ { Flags::RopeTime, "RopeTime" },
+ { Flags::LaskaFree, "LaskaFree" },
+ { Flags::ShanSmokTalked, "ShanSmokTalked" },
+ { Flags::SwordTaken, "SwordTaken" },
+ { Flags::Mill1st, "Mill1st" },
+ { Flags::SawRat, "SawRat" },
+ { Flags::KnowRat, "KnowRat" },
+ { Flags::DziuraTimer, "DziuraTimer" },
+ { Flags::LaskaInside, "LaskaInside" },
+ { Flags::HoleBig, "HoleBig" },
+ { Flags::EnableWiedzmin, "EnableWiedzmin" },
+ { Flags::EnableTrucizna, "EnableTrucizna" },
+ { Flags::KnowPoison, "KnowPoison" },
+ { Flags::KufelTaken, "KufelTaken" },
+ { Flags::BojkaEnabled, "BojkaEnabled" },
+ { Flags::BitwaNot1st, "BitwaNot1st" },
+ { Flags::BojkaTimer, "BojkaTimer" },
+ { Flags::BojkaGirl, "BojkaGirl" },
+ { Flags::Look1st, "Look1st" },
+ { Flags::RatTaken, "RatTaken" },
+ { Flags::LaskaTalkedGr, "LaskaTalkedGr" },
+ { Flags::RatusGivus, "RatusGivus" },
+ { Flags::MamObole, "MamObole" },
+ { Flags::Speed1st, "Speed1st" },
+ { Flags::SpeedTimer, "SpeedTimer" },
+ { Flags::ProveIt, "ProveIt" },
+ { Flags::Proven, "Proven" },
+ { Flags::ShowWoalka, "ShowWoalka" },
+ { Flags::PoisonTaken, "PoisonTaken" },
+ { Flags::HellOpened, "HellOpened" },
+ { Flags::HellNoCheck, "HellNoCheck" },
+ { Flags::TalAn1, "TalAn1" },
+ { Flags::TalAn2, "TalAn2" },
+ { Flags::TalAn3, "TalAn3" },
+ { Flags::TalkDevilGuard, "TalkDevilGuard" },
+ { Flags::Sword1st, "Sword1st" },
+ { Flags::IluzjaNoCheck, "IluzjaNoCheck" },
+ { Flags::RozdzielniaNumber, "RozdzielniaNumber" },
+ { Flags::JailChecked, "JailChecked" },
+ { Flags::JailTalked, "JailTalked" },
+ { Flags::TrickFailed, "TrickFailed" },
+ { Flags::WegielVisible, "WegielVisible" },
+ { Flags::WegielTimer1, "WegielTimer1" },
+ { Flags::RandomSample, "RandomSample" },
+ { Flags::RandomSampleTimer, "RandomSampleTimer" },
+ { Flags::SampleTimer, "SampleTimer" },
+ { Flags::ZonaSample, "ZonaSample" },
+ { Flags::HoleTryAgain, "HoleTryAgain" },
+ { Flags::TeleportTimer, "TeleportTimer" },
+ { Flags::RozLezy, "RozLezy" },
+ { Flags::UdkoTimer, "UdkoTimer" },
+ { Flags::ZaworZatkany, "ZaworZatkany" },
+ { Flags::ZaworOpened, "ZaworOpened" },
+ { Flags::DoorExploded, "DoorExploded" },
+ { Flags::SkoraTaken, "SkoraTaken" },
+ { Flags::CiezkieByl, "CiezkieByl" },
+ { Flags::MamWegiel, "MamWegiel" },
+ { Flags::SwiecaAway, "SwiecaAway" },
+ { Flags::ITSAVE, "ITSAVE" },
+ { Flags::RozpadlSie, "RozpadlSie" },
+ { Flags::WegielFullTimer, "WegielFullTimer" },
+ { Flags::WegielDown, "WegielDown" },
+ { Flags::WegielDownTimer, "WegielDownTimer" },
+ { Flags::PaliSie, "PaliSie" },
+ { Flags::DiabGuardTalked, "DiabGuardTalked" },
+ { Flags::GuardsNoCheck, "GuardsNoCheck" },
+ { Flags::TalkedPowloka, "TalkedPowloka" },
+ { Flags::JailOpen, "JailOpen" },
+ { Flags::PrzytulTimer, "PrzytulTimer" },
+ { Flags::JailDone, "JailDone" },
+ { Flags::MamMonety, "MamMonety" },
+ { Flags::LotTimer, "LotTimer" },
+ { Flags::LotObj, "LotObj" },
+ { Flags::PtakTimer, "PtakTimer" },
+ { Flags::BookTimer, "BookTimer" },
+ { Flags::BookGiba, "BookGiba" },
+ { Flags::PtakLata, "PtakLata" },
+ { Flags::Podej, "Podej" },
+ { Flags::GotHint, "GotHint" },
+ { Flags::LawaLeci, "LawaLeci" },
+ { Flags::PowerKlik, "PowerKlik" },
+ { Flags::LucekBad, "LucekBad" },
+ { Flags::LucekBad1st, "LucekBad1st" },
+ { Flags::IntroDial1, "IntroDial1" },
+ { Flags::IntroDial2, "IntroDial2" },
+ { Flags::ItsOutro, "ItsOutro" },
+ { Flags::KamienComment, "KamienComment" },
+ { Flags::KamienSkip, "KamienSkip" },
+ { Flags::TesterFlag, "TesterFlag" },
+ { Flags::RememberLine, "RememberLine" },
+ { Flags::OpisLapek, "OpisLapek" },
+ { Flags::TalWait, "TalWait" },
+ { Flags::OpisKamienia, "OpisKamienia" },
+ { Flags::JumpBox, "JumpBox" },
+ { Flags::JumpBox1, "JumpBox1" },
+ { Flags::JumpBox2, "JumpBox2" },
+ { Flags::JumpBox3, "JumpBox3" },
+ { Flags::SpecPiesek, "SpecPiesek" },
+ { Flags::SpecPiesekCount, "SpecPiesekCount" },
+ { Flags::SpecPiesekGadanie, "SpecPiesekGadanie" },
+ { Flags::ZnikaFlag, "ZnikaFlag" },
+ { Flags::ZnikaTimer, "ZnikaTimer" },
+ { Flags::SowaTimer, "SowaTimer" },
+ { Flags::MamrotanieOff, "MamrotanieOff" },
+ { Flags::CURRMOB, "CURRMOB" },
+ { Flags::KOLOR, "KOLOR" },
+ { Flags::MBFLAG, "MBFLAG" },
+ { Flags::MXFLAG, "MXFLAG" },
+ { Flags::MYFLAG, "MYFLAG" },
+ { Flags::SCROLLTYPE, "SCROLLTYPE" },
+ { Flags::SCROLLVALUE, "SCROLLVALUE" },
+ { Flags::SCROLLVALUE2, "SCROLLVALUE2" },
+ { Flags::TALKEXITCODE, "TALKEXITCODE" },
+ { Flags::SPECROUTFLAG1, "SPECROUTFLAG1" },
+ { Flags::SPECROUTFLAG2, "SPECROUTFLAG2" },
+ { Flags::SPECROUTFLAG3, "SPECROUTFLAG3" },
+ { Flags::TALKFLAGCODE, "TALKFLAGCODE" },
+ { Flags::CURRROOM, "CURRROOM" },
+ { Flags::Talker1Init, "Talker1Init" },
+ { Flags::Talker2Init, "Talker2Init" },
+ { Flags::RESTOREROOM, "RESTOREROOM" },
+ { Flags::INVALLOWED, "INVALLOWED" },
+ { Flags::BOXSEL, "BOXSEL" },
+ { Flags::CURSEBLINK, "CURSEBLINK" },
+ { Flags::EXACTMOVE, "EXACTMOVE" },
+ { Flags::MOVEDESTX, "MOVEDESTX" },
+ { Flags::MOVEDESTY, "MOVEDESTY" },
+ { Flags::NOANTIALIAS, "NOANTIALIAS" },
+ { Flags::ESCAPED, "ESCAPED" },
+ { Flags::ALLOW1OPTION, "ALLOW1OPTION" },
+ { Flags::VOICE_H_LINE, "VOICE_H_LINE" },
+ { Flags::VOICE_A_LINE, "VOICE_A_LINE" },
+ { Flags::VOICE_B_LINE, "VOICE_B_LINE" },
+ { Flags::VOICE_C_LINE, "VOICE_C_LINE" },
+ { Flags::NOHEROATALL, "NOHEROATALL" },
+ { Flags::MOUSEENABLED, "MOUSEENABLED" },
+ { Flags::DIALINES, "DIALINES" },
+ { Flags::SHANWALK, "SHANWALK" },
+ { Flags::SHANDOG, "SHANDOG" },
+ { Flags::GETACTIONBACK, "GETACTIONBACK" },
+ { Flags::GETACTIONDATA, "GETACTIONDATA" },
+ { Flags::GETACTION, "GETACTION" },
+ { Flags::HEROFAST, "HEROFAST" },
+ { Flags::SELITEM, "SELITEM" },
+ { Flags::LMOUSE, "LMOUSE" },
+ { Flags::MINMX, "MINMX" },
+ { Flags::MAXMX, "MAXMX" },
+ { Flags::MINMY, "MINMY" },
+ { Flags::MAXMY, "MAXMY" },
+ { Flags::TORX1, "TORX1" },
+ { Flags::TORY1, "TORY1" },
+ { Flags::TORX2, "TORX2" },
+ { Flags::TORY2, "TORY2" },
+ { Flags::POWER, "POWER" },
+ { Flags::POWERENABLED, "POWERENABLED" },
+ { Flags::FLCRESTORE, "FLCRESTORE" },
+ { Flags::NOCLSTEXT, "NOCLSTEXT" },
+ { Flags::ESCAPED2, "ESCAPED2" },
+};
+
+} // End of namespace Prince
diff --git a/engines/prince/flags.h b/engines/prince/flags.h
new file mode 100644
index 0000000000..8337f82a95
--- /dev/null
+++ b/engines/prince/flags.h
@@ -0,0 +1,421 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_FLAGS_H
+#define PRINCE_FLAGS_H
+
+#include "common/scummsys.h"
+
+namespace Prince {
+
+class Flags {
+public:
+ static int compareFlagDebug(const void *a, const void *b);
+ static const char *getFlagName(uint16 flagId);
+
+ enum Id {
+ FLAGA1 = 0x8000,
+ FLAGA2 = 0x8002,
+ FLAGA3 = 0x8004,
+ DESTX = 0x8006,
+ DESTY = 0x8008,
+ DESTD = 0x800A,
+ DwarfDone = 0x800C,
+ GRABARZCOUNTER = 0x800E,
+ KIERUNEK = 0x8010,
+ BACKFLAG1 = 0x8012,
+ BACKFLAG2 = 0x8014,
+ BACKFLAG3 = 0x8016,
+ BACKFLAG4 = 0x8018,
+ MACROFLAG1 = 0x801A,
+ MACROFLAG2 = 0x801C,
+ MACROFLAG3 = 0x801E,
+ HEROLDDONE = 0x8020,
+ BRIDGESET = 0x8022,
+ U_BT_1 = 0x8024,
+ U_BT_2 = 0x8026,
+ U_BT_3 = 0x8028,
+ U_BT_4 = 0x802A,
+ U_BT_5 = 0x802C,
+ U_BT_6 = 0x802E,
+ U_BT_7 = 0x8030,
+ U_BT_8 = 0x8032,
+ U_BT_9 = 0x8034,
+ U_BT_COUNTER = 0x8036,
+ ARIVALDALIVE = 0x8038,
+ TALKCHAR1 = 0x803A,
+ TalkType1 = 0x803C,
+ TALKROUT1 = 0x803E,
+ TALKROUT2 = 0x8042,
+ TALKROUT3 = 0x8046,
+ TALKROUT4 = 0x804A,
+ TALKANIM1 = 0x804E,
+ TALKANIM2 = 0x8050,
+ TALKCOLOR1 = 0x8052,
+ TALKCOLOR2 = 0x8054,
+ KapciuchTaken = 0x8056,
+ CurrentBeggarA = 0x8058,
+ TempKapc = 0x805A,
+ HomTaken = 0x805C,
+ WizardTalk = 0x805E,
+ SunlordTalk = 0x8060,
+ HermitTalk = 0x8062,
+ RunyMode = 0x8064,
+ FatMerchantTalk = 0x8066,
+ HotDogTalk = 0x8068,
+ ThiefTalk = 0x806A,
+ BeggarTalk = 0x806C,
+ // DwarfTalk = 0x806E, // Redefinition
+ MonkTalk = 0x8070,
+ BardTalk = 0x8072,
+ BarmanTalk = 0x8074,
+ LeftPlayerTalk = 0x8076,
+ OczySowy = 0x8078,
+ CzachySpeed1 = 0x807A,
+ CzachySpeed2 = 0x807C,
+ CzachySpeed3 = 0x807E,
+ CzachySlowDown1 = 0x8080,
+ CzachySlowDown2 = 0x8082,
+ CzachySlowDown3 = 0x8084,
+ FjordDane = 0x8086,
+ GKopany1 = 0x8088,
+ GKopany2 = 0x808A,
+ GKopany3 = 0x808C,
+ GKopany4 = 0x808E,
+ KnowGodWord = 0x8090,
+ TALKROUT21 = 0x8092,
+ TALKROUT22 = 0x8096,
+ TALKROUT23 = 0x809A,
+ TALKROUT24 = 0x809E,
+ TalkType2 = 0x80A2,
+ GrabarzTalk = 0x80A4,
+ LastTalker = 0x80A6,
+ MapaPustelniaEnabled = 0x80A8,
+ MapaTempleEnabled = 0x80AA,
+ MapaFjordEnabled = 0x80AC,
+ MapaSilmanionaEnabled = 0x80AE,
+ MapaKurhanEnabled = 0x80B0,
+ MapaDragonEnabled = 0x80B2,
+ MapaMillEnabled = 0x80B4,
+ DwarfRunning = 0x80B6,
+ DwarfTalk = 0x80B8,
+ CurseLift = 0x80BA,
+ KosciSwapped = 0x80BC,
+ BookStolen = 0x80BE,
+ MapaUsable = 0x80C0,
+ FjordBoss = 0x80C2,
+ FjordHotDog = 0x80C4,
+ FjordLewy = 0x80C6,
+ FjordPrawy = 0x80C8,
+ TalkArivald = 0x80CA,
+ ShootDone = 0x80CC,
+ ShootRunning = 0x80CE,
+ ShootKnow = 0x80D0,
+ MirrorKnow = 0x80D2,
+ Gar1stTime = 0x80D4,
+ KosciTaken = 0x80D6,
+ ArivGotSpell = 0x80D8,
+ BookGiven = 0x80DA,
+ Wywieszka = 0x80DC,
+ TalkSheila = 0x80DE,
+ TalkSheila2 = 0x80E0,
+ BackHuman = 0x80E2,
+ SkarbiecOpen = 0x80E4,
+ LustroTaken = 0x80E6,
+ GargoyleHom = 0x80E8,
+ GargoyleBroken = 0x80EA,
+ FjordDzien = 0x80EC,
+ GargoyleHom2 = 0x80EE,
+ RunMonstersRunning = 0x80F0,
+ FoundPaperInCoffin = 0x80F2,
+ KnowSunlord = 0x80F4,
+ KnowSunlordTalk = 0x80F6,
+ ArivaldCzyta = 0x80F8,
+ TelepX = 0x80FA,
+ TelepY = 0x80FC,
+ TelepDir = 0x80FE,
+ TelepRoom = 0x8100,
+ ListStolen = 0x8102,
+ WifeInDoor = 0x8104,
+ TalkWifeFlag = 0x8106,
+ LetterGiven = 0x8108,
+ LutniaTaken = 0x810A,
+ BardHomeOpen = 0x810C,
+ FjordNoMonsters = 0x810E,
+ ShandriaWallTalking = 0x8110,
+ ShandriaWallCounter = 0x8112,
+ ShandriaWallDone = 0x8114,
+ FutureDone = 0x8116,
+ TalkButch = 0x8118,
+ GotSzalik = 0x811A,
+ GotCzosnek = 0x811C,
+ BearDone = 0x811E,
+ NekrVisited = 0x8120,
+ SunRiddle = 0x8122,
+ PtaszekAway = 0x8124,
+ KotGadanie = 0x8126,
+ SzlafmycaTaken = 0x8128,
+ BabkaTalk = 0x812A,
+ SellerTalk = 0x812C,
+ CzosnekDone = 0x812E,
+ PriestCounter = 0x8130,
+ PriestGest1 = 0x8132,
+ PriestGest2 = 0x8134,
+ PriestGest3 = 0x8136,
+ PriestGest4 = 0x8138,
+ PriestAnim = 0x813A,
+ HolyWaterTaken = 0x813C,
+ AxeTaken = 0x813E,
+ BadylTaken1 = 0x8140,
+ BadylTaken2 = 0x8142,
+ BadylSharpened = 0x8144,
+ PorwanieSmoka = 0x8146,
+ ShopReOpen = 0x8148,
+ LuskaShown = 0x814A,
+ CudKnow = 0x814C,
+ VampireDead = 0x814E,
+ MapaVisible1 = 0x8150,
+ MapaVisible2 = 0x8152,
+ MapaVisible3 = 0x8154,
+ MapaVisible4 = 0x8156,
+ MapaVisible5 = 0x8158,
+ MapaVisible6 = 0x815A,
+ MapaVisible7 = 0x815C,
+ MapaVisible8 = 0x815E,
+ MapaVisible9 = 0x8160,
+ MapaX = 0x8162,
+ MapaY = 0x8164,
+ MapaD = 0x8166,
+ OldMapaX = 0x8168,
+ OldMapaY = 0x816A,
+ OldMapaD = 0x816C,
+ MovingBack = 0x816E,
+ MapaCount = 0x8170,
+ Pustelnia1st = 0x8172,
+ CzarnePole1st = 0x8174,
+ TalkArivNum = 0x8176,
+ Pfui = 0x8178,
+ MapaSunlordEnabled = 0x817A,
+ WebDone = 0x817C,
+ DragonDone = 0x817E,
+ KanPlay = 0x8180,
+ OldKanPlay = 0x8182,
+ LapkiWait = 0x8184,
+ WebNoCheck = 0x8186,
+ Perfumeria = 0x8188,
+ SmokNoCheck = 0x818A,
+ IluzjaBroken = 0x818C,
+ IluzjaWorking = 0x818E,
+ IluzjaCounter = 0x8190,
+ KurhanOpen1 = 0x8192,
+ KastetTaken = 0x8194,
+ KastetDown = 0x8196,
+ KurhanDone = 0x8198,
+ SkelCounter = 0x819A,
+ SkelDial1 = 0x819C,
+ SkelDial2 = 0x819E,
+ SkelDial3 = 0x81A0,
+ SkelDial4 = 0x81A2,
+ SameTalker = 0x81A4,
+ RunMonstersText = 0x81A6,
+ PiwnicaChecked = 0x81A8,
+ DragonTalked = 0x81AA,
+ ToldAboutBook = 0x81AC,
+ SilmanionaDone = 0x81AE,
+ ToldBookCount = 0x81B0,
+ SmrodNoCheck = 0x81B2,
+ RopeTaken = 0x81B4,
+ RopeTime = 0x81B6,
+ LaskaFree = 0x81B8,
+ ShanSmokTalked = 0x81BA,
+ SwordTaken = 0x81BC,
+ Mill1st = 0x81BE,
+ SawRat = 0x81C0,
+ KnowRat = 0x81C2,
+ DziuraTimer = 0x81C4,
+ LaskaInside = 0x81C6,
+ HoleBig = 0x81C8,
+ EnableWiedzmin = 0x81CA,
+ EnableTrucizna = 0x81CC,
+ KnowPoison = 0x81CE,
+ KufelTaken = 0x81D0,
+ BojkaEnabled = 0x81D2,
+ BitwaNot1st = 0x81D4,
+ BojkaTimer = 0x81D6,
+ BojkaGirl = 0x81D8,
+ Look1st = 0x81DA,
+ RatTaken = 0x81DC,
+ LaskaTalkedGr = 0x81DE,
+ RatusGivus = 0x81E0,
+ MamObole = 0x81E2,
+ Speed1st = 0x81E4,
+ SpeedTimer = 0x81E6,
+ ProveIt = 0x81E8,
+ Proven = 0x81EA,
+ ShowWoalka = 0x81EC,
+ PoisonTaken = 0x81EE,
+ HellOpened = 0x81F0,
+ HellNoCheck = 0x81F2,
+ TalAn1 = 0x81F4,
+ TalAn2 = 0x81F6,
+ TalAn3 = 0x81F8,
+ TalkDevilGuard = 0x81fA,
+ Sword1st = 0x81FC,
+ IluzjaNoCheck = 0x81FE,
+ RozdzielniaNumber = 0x8200,
+ JailChecked = 0x8202,
+ JailTalked = 0x8204,
+ TrickFailed = 0x8206,
+ WegielVisible = 0x8208,
+ WegielTimer1 = 0x820A,
+ RandomSample = 0x820C,
+ RandomSampleTimer = 0x820E,
+ SampleTimer = 0x8210,
+ ZonaSample = 0x8212,
+ HoleTryAgain = 0x8214,
+ TeleportTimer = 0x8216,
+ RozLezy = 0x8218,
+ UdkoTimer = 0x821A,
+ ZaworZatkany = 0x821C,
+ ZaworOpened = 0x821E,
+ DoorExploded = 0x8220,
+ SkoraTaken = 0x8222,
+ CiezkieByl = 0x8224,
+ MamWegiel = 0x8226,
+ SwiecaAway = 0x8228,
+ ITSAVE = 0x822A,
+ RozpadlSie = 0x822C,
+ WegielFullTimer = 0x822E,
+ WegielDown = 0x8230,
+ WegielDownTimer = 0x8232,
+ PaliSie = 0x8234,
+ DiabGuardTalked = 0x8236,
+ GuardsNoCheck = 0x8238,
+ TalkedPowloka = 0x823A,
+ JailOpen = 0x823C,
+ PrzytulTimer = 0x823E,
+ JailDone = 0x8240,
+ MamMonety = 0x8242,
+ LotTimer = 0x8244,
+ LotObj = 0x8246,
+ PtakTimer = 0x8248,
+ BookTimer = 0x824A,
+ BookGiba = 0x824C,
+ PtakLata = 0x824E,
+ Podej = 0x8250,
+ GotHint = 0x8252,
+ LawaLeci = 0x8254,
+ PowerKlik = 0x8258,
+ LucekBad = 0x825A,
+ LucekBad1st = 0x825C,
+ IntroDial1 = 0x825E,
+ IntroDial2 = 0x8260,
+ ItsOutro = 0x8262,
+ KamienComment = 0x8264,
+ KamienSkip = 0x8266,
+ TesterFlag = 0x8268,
+ RememberLine = 0x826A,
+ OpisLapek = 0x826C,
+ //OpisKamienia = 0x826E, // Redefinition
+ TalWait = 0x8270,
+ OpisKamienia = 0x8272,
+ JumpBox = 0x8274,
+ JumpBox1 = 0x8276,
+ JumpBox2 = 0x8278,
+ JumpBox3 = 0x827A,
+ SpecPiesek = 0x827C,
+ SpecPiesekCount = 0x827E,
+ SpecPiesekGadanie = 0x8282,
+ ZnikaFlag = 0x8284,
+ ZnikaTimer = 0x8286,
+ SowaTimer = 0x8288,
+ MamrotanieOff = 0x828A,
+ // System flags controlled by script
+ CURRMOB = 0x8400,
+ KOLOR = 0x8402,
+ MBFLAG = 0x8404,
+ MXFLAG = 0x8406,
+ MYFLAG = 0x8408,
+ SCROLLTYPE = 0x840A,
+ SCROLLVALUE = 0x840C,
+ SCROLLVALUE2 = 0x840E,
+ TALKEXITCODE = 0x8410,
+ SPECROUTFLAG1 = 0x8412,
+ SPECROUTFLAG2 = 0x8414,
+ SPECROUTFLAG3 = 0x8416,
+ TALKFLAGCODE = 0x8418,
+ CURRROOM = 0x841A,
+ Talker1Init = 0x841C,
+ Talker2Init = 0x841E,
+ RESTOREROOM = 0x8420,
+ INVALLOWED = 0x8422,
+ BOXSEL = 0x8424,
+ CURSEBLINK = 0x8426,
+ EXACTMOVE = 0x8428,
+ MOVEDESTX = 0x842A,
+ MOVEDESTY = 0x842C,
+ NOANTIALIAS = 0x842E,
+ ESCAPED = 0x8430,
+ ALLOW1OPTION = 0x8432,
+ VOICE_H_LINE = 0x8434,
+ VOICE_A_LINE = 0x8436,
+ VOICE_B_LINE = 0x8438,
+ VOICE_C_LINE = 0x843A,
+ NOHEROATALL = 0x843C,
+ MOUSEENABLED = 0x843E,
+ DIALINES = 0x8440,
+ //SELITEM = 0x8442, // Redefinition
+ SHANWALK = 0x8444,
+ SHANDOG = 0x8446,
+ GETACTIONBACK = 0x8448,
+ GETACTIONDATA = 0x844C,
+ GETACTION = 0x8450,
+ HEROFAST = 0x8452,
+ SELITEM = 0x8454,
+ LMOUSE = 0x8456,
+ MINMX = 0x8458,
+ MAXMX = 0x845A,
+ MINMY = 0x845C,
+ MAXMY = 0x845E,
+ TORX1 = 0x8460,
+ TORY1 = 0x8462,
+ TORX2 = 0x8464,
+ TORY2 = 0x8466,
+ POWER = 0x8468,
+ POWERENABLED = 0x846A,
+ FLCRESTORE = 0x846C,
+ NOCLSTEXT = 0x846E,
+ ESCAPED2 = 0x8470
+ };
+
+ struct FlagDebug {
+ Id id;
+ char flagName[30];
+ };
+
+ static const int kFlagDebugAmount = 368;
+ static const FlagDebug _flagNames[kFlagDebugAmount];
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/font.cpp b/engines/prince/font.cpp
new file mode 100644
index 0000000000..e81a93d1a1
--- /dev/null
+++ b/engines/prince/font.cpp
@@ -0,0 +1,94 @@
+/* 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/archive.h"
+#include "common/debug.h"
+#include "common/stream.h"
+
+#include "prince/font.h"
+#include "prince/prince.h"
+
+namespace Prince {
+
+Font::Font() : _fontData(nullptr) {
+}
+
+Font::~Font() {
+ if (_fontData != nullptr) {
+ free(_fontData);
+ _fontData = nullptr;
+ }
+}
+
+bool Font::loadStream(Common::SeekableReadStream &stream) {
+ stream.seek(0);
+ uint32 dataSize = stream.size();
+ _fontData = (byte *)malloc(dataSize);
+ stream.read(_fontData, stream.size());
+ return true;
+}
+
+int Font::getFontHeight() const {
+ return _fontData[5];
+}
+
+int Font::getMaxCharWidth() const {
+ return 0;
+}
+
+Font::ChrData Font::getChrData(byte chr) const {
+ chr -= 32;
+ uint16 chrOffset = 4 * chr + 6;
+
+ ChrData chrData;
+ chrData._width = _fontData[chrOffset + 2];
+ chrData._height = _fontData[chrOffset + 3];
+ chrData._pixels = _fontData + READ_LE_UINT16(_fontData + chrOffset);
+
+ return chrData;
+}
+
+int Font::getCharWidth(uint32 chr) const {
+ return getChrData(chr)._width;
+}
+
+void Font::drawChar(Graphics::Surface *dst, uint32 chr, int posX, int posY, uint32 color) const {
+ const ChrData chrData = getChrData(chr);
+ Common::Rect screenRect(0, 0, PrinceEngine::kNormalWidth, PrinceEngine::kNormalHeight);
+
+ for (int y = 0; y < chrData._height; y++) {
+ for (int x = 0; x < chrData._width; x++) {
+ byte d = chrData._pixels[x + (chrData._width * y)];
+ if (d == 0) d = 255;
+ else if (d == 1) d = 0;
+ else if (d == 2) d = color;
+ else if (d == 3) d = 0;
+ if (d != 255) {
+ if (screenRect.contains(posX + x, posY + y)) {
+ *(byte *)dst->getBasePtr(posX + x, posY + y) = d;
+ }
+ }
+ }
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/font.h b/engines/prince/font.h
new file mode 100644
index 0000000000..b03849a4aa
--- /dev/null
+++ b/engines/prince/font.h
@@ -0,0 +1,64 @@
+/* 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.
+ */
+
+#ifndef PRINCE_FONT_H
+#define PRINCE_FONT_H
+
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
+#include "common/str.h"
+#include "common/rect.h"
+
+namespace Prince {
+
+class Font : public Graphics::Font {
+public:
+ Font();
+ virtual ~Font();
+
+ bool loadStream(Common::SeekableReadStream &stream);
+
+ virtual int getFontHeight() const override;
+
+ virtual int getMaxCharWidth() const override;
+
+ virtual int getCharWidth(uint32 chr) const override;
+
+ virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+
+ virtual int getKerningOffset(uint32 left, uint32 right) const override { return -2; }
+
+private:
+ struct ChrData {
+ byte *_pixels;
+ byte _width;
+ byte _height;
+ };
+
+ ChrData getChrData(byte chr) const;
+
+ byte *_fontData;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/graphics.cpp b/engines/prince/graphics.cpp
new file mode 100644
index 0000000000..cd4b62f7a5
--- /dev/null
+++ b/engines/prince/graphics.cpp
@@ -0,0 +1,491 @@
+/* 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 "prince/graphics.h"
+#include "prince/prince.h"
+#include "prince/mhwanh.h"
+
+#include "graphics/palette.h"
+
+#include "common/memstream.h"
+
+namespace Prince {
+
+GraphicsMan::GraphicsMan(PrinceEngine *vm) : _vm(vm), _changed(false) {
+ initGraphics(640, 480, true);
+
+ _frontScreen = new Graphics::Surface();
+ _frontScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
+
+ _screenForInventory = new Graphics::Surface();
+ _screenForInventory->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
+
+ _mapScreen = new Graphics::Surface();
+ _mapScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
+
+ _shadowTable70 = (byte *)malloc(256);
+ _shadowTable50 = (byte *)malloc(256);
+}
+
+GraphicsMan::~GraphicsMan() {
+ _frontScreen->free();
+ delete _frontScreen;
+
+ _screenForInventory->free();
+ delete _screenForInventory;
+
+ _mapScreen->free();
+ delete _mapScreen;
+
+ free(_shadowTable70);
+ free(_shadowTable50);
+}
+
+void GraphicsMan::update(Graphics::Surface *screen) {
+ if (_changed) {
+ _vm->_system->copyRectToScreen((byte *)screen->getBasePtr(0, 0), 640, 0, 0, 640, 480);
+
+ _vm->_system->updateScreen();
+ _changed = false;
+ }
+}
+
+void GraphicsMan::setPalette(const byte *palette) {
+ _vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
+}
+
+void GraphicsMan::change() {
+ _changed = true;
+}
+
+void GraphicsMan::draw(Graphics::Surface *screen, const Graphics::Surface *s) {
+ uint16 w = MIN(screen->w, s->w);
+ byte *src = (byte *)s->getBasePtr(0, 0);
+ byte *dst = (byte *)screen->getBasePtr(0, 0);
+ for (uint y = 0; y < s->h; y++) {
+ if (y < screen->h) {
+ memcpy(dst, src, w);
+ }
+ src += s->pitch;
+ dst += screen->pitch;
+ }
+ change();
+}
+
+// Black (value = 0) as a primary transparent color, fix for FLC animations
+void GraphicsMan::drawTransparentSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, int secondTransColor) {
+ byte *src1 = (byte *)s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(posX, posY);
+ for (int y = 0; y < s->h; y++) {
+ if (y + posY < screen->h && y + posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < s->w; x++, src2++, dst2++) {
+ if (*src2 && *src2 != secondTransColor) {
+ if (x + posX < screen->w && x + posX >= 0) {
+ *dst2 = *src2;
+ }
+ }
+ }
+ }
+ src1 += s->pitch;
+ dst1 += screen->pitch;
+ }
+ change();
+}
+
+/**
+ * Similar to drawTransparentSurface but with use of shadowTable for color recalculation
+ * and kShadowColor (191) as a transparent color.
+ */
+void GraphicsMan::drawAsShadowSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, byte *shadowTable) {
+ byte *src1 = (byte *)s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(posX, posY);
+ for (int y = 0; y < s->h; y++) {
+ if (y + posY < screen->h && y + posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < s->w; x++, src2++, dst2++) {
+ if (*src2 == kShadowColor) {
+ if (x + posX < screen->w && x + posX >= 0) {
+ *dst2 = *(shadowTable + *dst2);
+ }
+ }
+ }
+ }
+ src1 += s->pitch;
+ dst1 += screen->pitch;
+ }
+}
+
+/**
+ * Used in glowing animation for inventory items. Creates special blendTable array of colors,
+ * use black (0) as a transparent color.
+ */
+void GraphicsMan::drawTransparentWithBlendSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s) {
+ byte *src1 = (byte *)s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(posX, posY);
+ byte *blendTable = (byte *)malloc(256);
+ for (int i = 0; i < 256; i++) {
+ blendTable[i] = 255;
+ }
+ for (int y = 0; y < s->h; y++) {
+ if (y + posY < screen->h && y + posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < s->w; x++, src2++, dst2++) {
+ if (*src2) {
+ if (x + posX < screen->w && x + posX >= 0) {
+ *dst2 = getBlendTableColor(*src2, *dst2, blendTable);
+ }
+ }
+ }
+ }
+ src1 += s->pitch;
+ dst1 += screen->pitch;
+ }
+ free(blendTable);
+ change();
+}
+
+/**
+ * Similar to drawTransparentSurface but with with use of DrawNode as argument for Z axis sorting
+ * and white (255) as transparent color.
+ */
+void GraphicsMan::drawTransparentDrawNode(Graphics::Surface *screen, DrawNode *drawNode) {
+ byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY);
+ for (int y = 0; y < drawNode->s->h; y++) {
+ if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) {
+ if (*src2 != 255) {
+ if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) {
+ *dst2 = *src2;
+ }
+ }
+ }
+ }
+ src1 += drawNode->s->pitch;
+ dst1 += screen->pitch;
+ }
+}
+
+/**
+ * Similar to drawTransparentDrawNode but with additional anti-aliasing code for sprite drawing.
+ * Edge smoothing is based on 256 x 256 table of colors transition.
+ * Algorithm is checking if currently drawing pixel is located next to the edge of sprite and if it makes jagged line.
+ * If it does then this pixel is set with color from transition table calculated of original background pixel color
+ * and sprite's edge pixel color.
+ */
+void GraphicsMan::drawTransparentWithTransDrawNode(Graphics::Surface *screen, DrawNode *drawNode) {
+ // pos of first pixel for each row of source sprite
+ byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0);
+ // pos of drawing first pixel for each row on screen surface
+ byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY);
+ // trasition table for calculating new color value
+ byte *transTableData = (byte *)drawNode->data;
+ for (int y = 0; y < drawNode->s->h; y++) {
+ if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) {
+ // current pixel in row of source sprite
+ byte *src2 = src1;
+ // current pixel in row of screen surface
+ byte *dst2 = dst1;
+ for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) {
+ if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) {
+ if (*src2 != 255) {
+ // if source sprite pixel is not mask color than draw it normally
+ *dst2 = *src2;
+ } else {
+ // check for making jagged line
+ if (x) {
+ // not first pixel in row
+ if (*(src2 - 1) == 255) {
+ // if it has mask color to the left - check right
+ if (x != drawNode->s->w - 1) {
+ // not last pixel in row
+ if (*(src2 + 1) == 255) {
+ // pixel to the right with mask color - no anti-alias
+ continue;
+ }
+ // it's not mask color to the right - we continue checking
+ } else {
+ // last pixel in row, no right check - no anti-alias
+ continue;
+ }
+ }
+ // it's not mask color to the left - we continue checking
+ } else if (x != drawNode->s->w - 1) {
+ // first pixel in row but not last - just right pixel checking
+ if (*(src2 + 1) == 255) {
+ // pixel to the right with mask color - no anti-alias
+ continue;
+ }
+ // it's not mask color to the right - we continue checking
+ } else {
+ // it's first and last pixel in row at the same time (width = 1) - no anti-alias
+ continue;
+ }
+ byte value = 0;
+ if (y != drawNode->s->h - 1) {
+ // not last row
+ // check pixel below of current src2 pixel
+ value = *(src2 + drawNode->s->pitch);
+ if (value == 255) {
+ // pixel below with mask color - check above
+ if (y) {
+ // not first row
+ value = *(src2 - drawNode->s->pitch);
+ if (value == 255) {
+ // pixel above with mask color - no anti-alias
+ continue;
+ }
+ // it's not mask color above - we draw as transition color
+ } else {
+ // first row - no anti-alias
+ continue;
+ }
+ }
+ // it's not mask color below - we draw as transition color
+ } else if (y) {
+ // last row - just check above
+ value = *(src2 - drawNode->s->pitch);
+ if (value == 255) {
+ // pixel above with mask color - no anti-alias
+ continue;
+ }
+ // it's not mask color above - we draw as transition color
+ } else {
+ // first and last row at the same time (height = 1) - no anti-alias
+ continue;
+ }
+ // new color value based on orginal screen surface color and sprite's edge pixel color
+ *dst2 = transTableData[*dst2 * 256 + value];
+ }
+ }
+ }
+ }
+ // adding pitch to jump to next row of pixels
+ src1 += drawNode->s->pitch;
+ dst1 += screen->pitch;
+ }
+}
+
+void GraphicsMan::drawMaskDrawNode(Graphics::Surface *screen, DrawNode *drawNode) {
+ byte *maskData = (byte *)drawNode->data;
+ byte *src1 = (byte *)drawNode->originalRoomSurface->getBasePtr(drawNode->posX, drawNode->posY);
+ byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY);
+ int maskWidth = drawNode->width >> 3;
+ int maskPostion = 0;
+ int maskCounter = 128;
+ for (int y = 0; y < drawNode->height; y++) {
+ if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ int tempMaskPostion = maskPostion;
+ for (int x = 0; x < drawNode->width; x++, src2++, dst2++) {
+ if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) {
+ if ((maskData[tempMaskPostion] & maskCounter) != 0) {
+ *dst2 = *src2;
+ }
+ }
+ maskCounter >>= 1;
+ if (maskCounter == 0) {
+ maskCounter = 128;
+ tempMaskPostion++;
+ }
+ }
+ }
+ src1 += drawNode->originalRoomSurface->pitch;
+ dst1 += screen->pitch;
+ maskPostion += maskWidth;
+ maskCounter = 128;
+ }
+}
+
+void GraphicsMan::drawAsShadowDrawNode(Graphics::Surface *screen, DrawNode *drawNode) {
+ byte *shadowData = (byte *)drawNode->data;
+ byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY);
+ for (int y = 0; y < drawNode->s->h; y++) {
+ if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) {
+ if (*src2 == kShadowColor) {
+ if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) {
+ *dst2 = *(shadowData + *dst2);
+ }
+ }
+ }
+ }
+ src1 += drawNode->s->pitch;
+ dst1 += screen->pitch;
+ }
+}
+
+void GraphicsMan::drawBackSpriteDrawNode(Graphics::Surface *screen, DrawNode *drawNode) {
+ byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0);
+ byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY);
+ for (int y = 0; y < drawNode->s->h; y++) {
+ if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) {
+ byte *src2 = src1;
+ byte *dst2 = dst1;
+ for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) {
+ if (*src2 != 255) {
+ if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) {
+ if (*dst2 == 255) {
+ *dst2 = *src2;
+ }
+ }
+ }
+ }
+ }
+ src1 += drawNode->s->pitch;
+ dst1 += screen->pitch;
+ }
+}
+
+byte GraphicsMan::getBlendTableColor(byte pixelColor, byte backgroundPixelColor, byte *blendTable) {
+ int32 redFirstOrg, greenFirstOrg, blueFirstOrg;
+ int32 redFirstBack, greenFirstBack, blueFirstBack;
+ int32 redSecondOrg, greenSecondOrg, blueSecondOrg;
+ int32 redNew, greenNew, blueNew;
+
+ int32 sumOfColorValues;
+ int32 bigValue;
+ int32 currColor;
+
+ if (blendTable[pixelColor] != 255) {
+ currColor = blendTable[pixelColor];
+ } else {
+ const byte *originalPalette = _vm->_roomBmp->getPalette();
+
+ redFirstOrg = originalPalette[pixelColor * 3] * _vm->_mst_shadow / 256;
+ CLIP(redFirstOrg, 0, 255);
+ if (_vm->_mst_shadow <= 256) {
+ redFirstBack = originalPalette[backgroundPixelColor * 3] * (256 - _vm->_mst_shadow) / 256;
+ CLIP(redFirstBack, 0, 255);
+ redFirstOrg += redFirstBack;
+ CLIP(redFirstOrg, 0, 255);
+ }
+
+ greenFirstOrg = originalPalette[pixelColor * 3 + 1] * _vm->_mst_shadow / 256;
+ CLIP(greenFirstOrg, 0, 255);
+ if (_vm->_mst_shadow <= 256) {
+ greenFirstBack = originalPalette[backgroundPixelColor * 3 + 1] * (256 - _vm->_mst_shadow) / 256;
+ CLIP(greenFirstBack, 0, 255);
+ greenFirstOrg += greenFirstBack;
+ CLIP(greenFirstOrg, 0, 255);
+ }
+
+ blueFirstOrg = originalPalette[pixelColor * 3 + 2] * _vm->_mst_shadow / 256;
+ CLIP(blueFirstOrg, 0, 255);
+ if (_vm->_mst_shadow <= 256) {
+ blueFirstBack = originalPalette[backgroundPixelColor * 3 + 2] * (256 - _vm->_mst_shadow) / 256;
+ CLIP(blueFirstBack, 0, 255);
+ blueFirstOrg += blueFirstBack;
+ CLIP(blueFirstOrg, 0, 255);
+ }
+
+ currColor = 0;
+ bigValue = PrinceEngine::kIntMax; // infinity
+ for (int j = 0; j < 256; j++) {
+ redSecondOrg = originalPalette[3 * j];
+ redNew = redFirstOrg - redSecondOrg;
+ redNew = redNew * redNew;
+
+ greenSecondOrg = originalPalette[3 * j + 1];
+ greenNew = greenFirstOrg - greenSecondOrg;
+ greenNew = greenNew * greenNew;
+
+ blueSecondOrg = originalPalette[3 * j + 2];
+ blueNew = blueFirstOrg - blueSecondOrg;
+ blueNew = blueNew * blueNew;
+
+ sumOfColorValues = redNew + greenNew + blueNew;
+
+ if (sumOfColorValues < bigValue) {
+ bigValue = sumOfColorValues;
+ currColor = j;
+ }
+
+ if (sumOfColorValues == 0) {
+ break;
+ }
+ }
+ blendTable[pixelColor] = currColor;
+ }
+ return currColor;
+}
+
+void GraphicsMan::makeShadowTable(int brightness, byte *shadowPalette) {
+ int32 redFirstOrg, greenFirstOrg, blueFirstOrg;
+ int32 redSecondOrg, greenSecondOrg, blueSecondOrg;
+ int32 redNew, greenNew, blueNew;
+
+ int32 sumOfColorValues;
+ int32 bigValue;
+ int32 currColor;
+
+ int shadow = brightness * 256 / 100;
+
+ const byte *originalPalette = _vm->_roomBmp->getPalette();
+
+ for (int i = 0; i < 256; i++) {
+ redFirstOrg = originalPalette[3 * i] * shadow / 256;
+ greenFirstOrg = originalPalette[3 * i + 1] * shadow / 256;
+ blueFirstOrg = originalPalette[3 * i + 2] * shadow / 256;
+
+ currColor = 0;
+ bigValue = 999999999; // infinity
+
+ for (int j = 0; j < 256; j++) {
+ redSecondOrg = originalPalette[3 * j];
+ redNew = redFirstOrg - redSecondOrg;
+ redNew = redNew * redNew;
+
+ greenSecondOrg = originalPalette[3 * j + 1];
+ greenNew = greenFirstOrg - greenSecondOrg;
+ greenNew = greenNew * greenNew;
+
+ blueSecondOrg = originalPalette[3 * j + 2];
+ blueNew = blueFirstOrg - blueSecondOrg;
+ blueNew = blueNew * blueNew;
+
+ sumOfColorValues = redNew + greenNew + blueNew;
+
+ if (sumOfColorValues < bigValue) {
+ bigValue = sumOfColorValues;
+ currColor = j;
+ }
+
+ if (sumOfColorValues == 0) {
+ break;
+ }
+ }
+ shadowPalette[i] = currColor;
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/graphics.h b/engines/prince/graphics.h
new file mode 100644
index 0000000000..b6f002b7da
--- /dev/null
+++ b/engines/prince/graphics.h
@@ -0,0 +1,76 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_GRAPHICS_H
+#define PRINCE_GRAPHICS_H
+
+#include "graphics/surface.h"
+
+namespace Prince {
+
+class PrinceEngine;
+class MhwanhDecoder;
+struct DrawNode;
+
+class GraphicsMan {
+public:
+ GraphicsMan(PrinceEngine *vm);
+ ~GraphicsMan();
+
+ void update(Graphics::Surface *screen);
+
+ void change();
+
+ void setPalette(const byte *palette);
+ void makeShadowTable(int brightness, byte *shadowTable);
+
+ void draw(Graphics::Surface *screen, const Graphics::Surface *s);
+ void drawTransparentSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, int secondTransColor = 0);
+ void drawAsShadowSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, byte *shadowTable);
+ void drawTransparentWithBlendSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s);
+
+ static void drawTransparentDrawNode(Graphics::Surface *screen, DrawNode *drawNode);
+ static void drawTransparentWithTransDrawNode(Graphics::Surface *screen, DrawNode *drawNode);
+ static void drawAsShadowDrawNode(Graphics::Surface *screen, DrawNode *drawNode);
+ static void drawMaskDrawNode(Graphics::Surface *screen, DrawNode *drawNode);
+ static void drawBackSpriteDrawNode(Graphics::Surface *screen, DrawNode *drawNode);
+
+ byte getBlendTableColor(byte pixelColor, byte backgroundPixelColor, byte *blendTable);
+
+ Graphics::Surface *_frontScreen;
+ Graphics::Surface *_screenForInventory;
+ Graphics::Surface *_mapScreen;
+ const Graphics::Surface *_roomBackground;
+
+ byte *_shadowTable70;
+ byte *_shadowTable50;
+
+ static const byte kShadowColor = 191;
+
+private:
+ PrinceEngine *_vm;
+ bool _changed;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/hero.cpp b/engines/prince/hero.cpp
new file mode 100644
index 0000000000..8c13c6d1f9
--- /dev/null
+++ b/engines/prince/hero.cpp
@@ -0,0 +1,1004 @@
+/* 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/debug.h"
+#include "common/random.h"
+
+#include "prince/hero.h"
+#include "prince/hero_set.h"
+#include "prince/animation.h"
+#include "prince/resource.h"
+#include "prince/prince.h"
+#include "prince/graphics.h"
+#include "prince/flags.h"
+#include "prince/script.h"
+
+namespace Prince {
+
+Hero::Hero(PrinceEngine *vm, GraphicsMan *graph) : _vm(vm), _graph(graph),
+ _number(0), _visible(false), _state(kHeroStateStay), _middleX(0), _middleY(0),
+ _boreNum(1), _currHeight(0), _moveDelay(0), _shadMinus(0), _moveSetType(0), _zoomedHeroSurface(nullptr),
+ _lastDirection(kHeroDirDown), _destDirection(kHeroDirDown), _talkTime(0), _boredomTime(0), _phase(0),
+ _specAnim(nullptr), _drawX(0), _drawY(0), _drawZ(0),
+ _frameXSize(0), _frameYSize(0), _scaledFrameXSize(0), _scaledFrameYSize(0), _color(0),
+ _coords(nullptr), _dirTab(nullptr), _currCoords(nullptr), _currDirTab(nullptr), _step(0),
+ _maxBoredom(200), _leftRightMainDir(0), _upDownMainDir(0), _animSetNr(0)
+{
+}
+
+Hero::~Hero() {
+ freeHeroAnim();
+ freeOldMove();
+ freeZoomedSurface();
+}
+
+bool Hero::loadAnimSet(uint32 animSetNr) {
+ _animSetNr = animSetNr;
+
+ if (animSetNr > sizeof(heroSetTable)) {
+ return false;
+ }
+
+ _shadMinus = heroSetBack[animSetNr];
+
+ for (uint32 i = 0; i < _moveSet.size(); i++) {
+ delete _moveSet[i];
+ }
+
+ const HeroSetAnimNames &animSet = *heroSetTable[animSetNr];
+
+ _moveSet.resize(kMoveSetSize);
+ for (uint32 i = 0; i < kMoveSetSize; i++) {
+ debug("Anim set item %d %s", i, animSet[i]);
+ Animation *anim = nullptr;
+ if (animSet[i] != nullptr) {
+ anim = new Animation();
+ Resource::loadResource(anim, animSet[i], false);
+ }
+ _moveSet[i] = anim;
+ }
+
+ return true;
+}
+
+Graphics::Surface *Hero::getSurface() {
+ Animation *heroAnim = nullptr;
+ if (_specAnim != nullptr) {
+ heroAnim = _specAnim;
+ } else {
+ heroAnim = _moveSet[_moveSetType];
+ }
+
+ if (heroAnim != nullptr) {
+ int16 phaseFrameIndex = heroAnim->getPhaseFrameIndex(_phase);
+ Graphics::Surface *heroFrame = heroAnim->getFrame(phaseFrameIndex);
+ return heroFrame;
+ }
+ return nullptr;
+}
+
+uint16 Hero::getData(AttrId dataId) {
+ switch (dataId) {
+ case kHeroLastDir:
+ return _lastDirection;
+ case kHeroAnimSet:
+ return _animSetNr;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+int Hero::getScaledValue(int size) {
+ int newSize = 0;
+ int16 initScaleValue = _vm->_scaleValue;
+ if (_vm->_scaleValue != 10000) {
+ for (int i = 0; i < size; i++) {
+ initScaleValue -= 100;
+ if (initScaleValue >= 0) {
+ newSize++;
+ } else {
+ initScaleValue += _vm->_scaleValue;
+ }
+ }
+ return newSize;
+ } else {
+ return size;
+ }
+}
+
+Graphics::Surface *Hero::zoomSprite(Graphics::Surface *heroFrame) {
+ Graphics::Surface *zoomedFrame = new Graphics::Surface();
+ zoomedFrame->create(_scaledFrameXSize, _scaledFrameYSize, Graphics::PixelFormat::createFormatCLUT8());
+
+ int sprZoomX;
+ int sprZoomY = _vm->_scaleValue;
+ uint xSource = 0;
+ uint ySource = 0;
+ uint xDest = 0;
+ uint yDest = 0;
+
+ for (int i = 0; i < _scaledFrameYSize; i++) {
+ // linear_loop:
+ while (1) {
+ sprZoomY -= 100;
+ if (sprZoomY >= 0 || _vm->_scaleValue == 10000) {
+ // all_r_y
+ sprZoomX = _vm->_scaleValue;
+ break; // to loop_lin
+ } else {
+ sprZoomY += _vm->_scaleValue;
+ xSource = 0;
+ ySource++;
+ }
+ }
+ // loop_lin:
+ for (int j = 0; j < _scaledFrameXSize; j++) {
+ sprZoomX -= 100;
+ if (sprZoomX >= 0) {
+ // its_all_r
+ memcpy(zoomedFrame->getBasePtr(xDest, yDest), heroFrame->getBasePtr(xSource, ySource), 1);
+ xDest++;
+ } else {
+ sprZoomX += _vm->_scaleValue;
+ j--;
+ }
+ xSource++;
+ }
+ xDest = 0;
+ yDest++;
+ xSource = 0;
+ ySource++;
+ }
+ return zoomedFrame;
+}
+
+void Hero::countDrawPosition() {
+ Animation *heroAnim = nullptr;
+ if (_specAnim != nullptr) {
+ heroAnim = _specAnim;
+ } else {
+ heroAnim = _moveSet[_moveSetType];
+ }
+ if (heroAnim != nullptr) {
+ int phaseFrameIndex = heroAnim->getPhaseFrameIndex(_phase);
+ Graphics::Surface *heroSurface = heroAnim->getFrame(phaseFrameIndex);
+
+ _frameXSize = heroSurface->w;
+ _frameYSize = heroSurface->h;
+ _scaledFrameXSize = getScaledValue(_frameXSize);
+ _scaledFrameYSize = getScaledValue(_frameYSize);
+
+ if (_vm->_scaleValue != 10000) {
+ //notfullSize
+ _drawX = _middleX - _scaledFrameXSize / 2;
+ _drawY = _middleY + 1 - _scaledFrameYSize;
+ _vm->checkMasks(_drawX, _drawY - 1, _scaledFrameXSize, _scaledFrameYSize, _middleY);
+ } else {
+ //fullSize
+ _drawX = _middleX - _frameXSize / 2;
+ _drawY = _middleY + 1 - _frameYSize;
+ _vm->checkMasks(_drawX, _drawY - 1, _frameXSize, _frameYSize, _middleY);
+ }
+ _drawZ = _middleY;
+ }
+}
+
+void Hero::showHeroShadow(Graphics::Surface *screen, DrawNode *drawNode) {
+ PrinceEngine *vm = (PrinceEngine *)drawNode->data;
+ int16 heroSurfaceWidth = drawNode->s->w;
+ int16 heroSurfaceHeight = drawNode->s->h;
+
+ Graphics::Surface *makeShadow = new Graphics::Surface();
+ makeShadow->create(heroSurfaceWidth, heroSurfaceHeight, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int y = 0; y < heroSurfaceHeight; y++) {
+ byte *src = (byte *)drawNode->s->getBasePtr(0, y);
+ byte *dst = (byte *)makeShadow->getBasePtr(0, y);
+ for (int x = 0; x < heroSurfaceWidth; x++, dst++, src++) {
+ if (*src != 0xFF) {
+ *dst = GraphicsMan::kShadowColor;
+ } else {
+ *dst = *src;
+ }
+ }
+ }
+
+ if (drawNode->posY > 1 && drawNode->posY < PrinceEngine::kMaxPicHeight) {
+ int shadDirection;
+ if (vm->_lightY > drawNode->posY) {
+ shadDirection = 1;
+ } else {
+ shadDirection = 0;
+ }
+
+ vm->_shadLineLen = 0;
+ Graphics::drawLine(vm->_lightX, vm->_lightY, drawNode->posX, drawNode->posY, 0, &vm->plotShadowLinePoint, vm);
+
+ byte *sprShadow = vm->_graph->_shadowTable70;
+
+ int shadDrawX = drawNode->posX - vm->_picWindowX;
+ int shadDrawY = drawNode->posY - vm->_picWindowY;
+
+ int shadPosX = shadDrawX;
+ int shadPosY = shadDrawY;
+ int shadBitAddr = drawNode->posY * PrinceEngine::kMaxPicWidth / 8 + drawNode->posX / 8;
+ int shadBitMask = 128 >> (drawNode->posX % 8);
+
+ int shadZoomY2 = vm->_shadScaleValue;
+ int shadZoomY = drawNode->scaleValue;
+
+ int diffX = 0;
+ int diffY = 0;
+
+ int shadowHeroX = 0;
+ int shadowHeroY = heroSurfaceHeight - 1;
+
+ int shadLastY = 0;
+
+ byte *shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY); // first pixel from last row of shadow hero
+ byte *background = (byte *)screen->getBasePtr(shadDrawX, shadDrawY); // pixel of background where shadow sprite starts
+
+ // banked2
+ byte *shadowLineStart = vm->_shadowLine + 8;
+
+ int shadWallDown = 0;
+ int shadWallBitAddr = 0;
+ int shadWallBitMask = 0;
+ byte *shadWallDestAddr = 0;
+ int shadWallPosY = 0;
+ int shadWallSkipX = 0;
+ int shadWallModulo = 0;
+
+ // linear_loop
+ for (int i = 0; i < heroSurfaceHeight; i++) {
+ int shadSkipX = 0;
+ int ctLoop = 0;
+ int sprModulo = 0;
+
+ int j;
+ //retry_line:
+ for (j = heroSurfaceHeight - i; j > 0; j--) {
+ shadZoomY -= 100;
+ if (shadZoomY < 0 && drawNode->scaleValue != 10000) {
+ shadZoomY += drawNode->scaleValue;
+ shadowHeroY--;
+ if (shadowHeroY < 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (!j) {
+ break;
+ }
+ if (shadowHeroY < 0) {
+ break;
+ }
+
+ bool skipLineFlag = false;
+ //line_y_ok
+ if (shadLastY != shadPosY && shadPosY >= 0 && shadPosY < 480 && shadPosX < 640) {
+ shadLastY = shadPosY;
+ if (shadPosX < 0) {
+ shadSkipX = -1 * shadPosX;
+ if (heroSurfaceWidth > shadSkipX) {
+ ctLoop = heroSurfaceWidth - shadSkipX;
+ shadowHeroX = shadSkipX;
+ } else {
+ //skip_line
+ skipLineFlag = true;
+ }
+ } else {
+ //x1_ok
+ if (shadPosX + heroSurfaceWidth > 640) {
+ ctLoop = 640 - shadPosX;
+ sprModulo = shadPosX + heroSurfaceWidth - 640;
+ } else {
+ //draw_line
+ ctLoop = heroSurfaceWidth;
+ }
+ }
+
+ if (!skipLineFlag) {
+ //draw_line1
+ //retry_line2
+ int k;
+ for (k = j; k > 0; k--) {
+ shadZoomY2 -= 100;
+ if (shadZoomY2 < 0 && vm->_shadScaleValue != 10000) {
+ shadZoomY2 += vm->_shadScaleValue;
+ shadowHeroY--;
+ if (shadowHeroY < 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (shadowHeroY < 0) {
+ break;
+ }
+ if (!k) {
+ break;
+ }
+ //line_y_ok_2:
+ //copy_trans
+ bool shadWDFlag = false;
+ int shadZoomX = drawNode->scaleValue;
+ int backgroundDiff = shadSkipX;
+ int shadBitMaskCopyTrans = shadBitMask;
+ int shadBitAddrCopyTrans = shadBitAddr;
+ shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY);
+ background = (byte *)screen->getBasePtr(shadDrawX + diffX + backgroundDiff, shadDrawY + diffY);
+
+ if (shadPosX < 0) {
+ if (heroSurfaceWidth > shadSkipX) {
+ shadBitAddrCopyTrans += shadSkipX / 8;
+ if ((shadSkipX % 8)) {
+ //loop_rotate:
+ for (int a = 0; a < (shadSkipX % 8); a++) {
+ if (shadBitMaskCopyTrans == 1) {
+ shadBitMaskCopyTrans = 128;
+ shadBitAddrCopyTrans++;
+ } else {
+ shadBitMaskCopyTrans >>= 1;
+ }
+ }
+ }
+ }
+ }
+
+ //ct_loop:
+ for (int l = 0; l < ctLoop; l++) {
+ shadZoomX -= 100;
+ if (shadZoomX < 0 && drawNode->scaleValue != 10000) {
+ shadZoomX += drawNode->scaleValue;
+ } else {
+ if (*shadowHero == GraphicsMan::kShadowColor) {
+ if ((shadBitMaskCopyTrans & vm->_shadowBitmap[shadBitAddrCopyTrans])) {
+ if (shadWallDown == 0) {
+ if ((shadBitMaskCopyTrans & vm->_shadowBitmap[shadBitAddrCopyTrans + PrinceEngine::kShadowBitmapSize])) {
+ shadWDFlag = true;
+ //shadow
+ *background = *(sprShadow + *background);
+ }
+ }
+ } else {
+ //shadow
+ *background = *(sprShadow + *background);
+ }
+ }
+ //ct_next
+ if (shadBitMaskCopyTrans == 1) {
+ shadBitMaskCopyTrans = 128;
+ shadBitAddrCopyTrans++;
+ } else {
+ shadBitMaskCopyTrans >>= 1;
+ }
+ //okok
+ backgroundDiff++;
+ background = (byte *)screen->getBasePtr(shadDrawX + diffX + backgroundDiff, shadDrawY + diffY);
+ }
+ shadowHeroX++;
+ shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY);
+ }
+ //byebyebye
+ if (!shadWallDown && shadWDFlag) {
+ shadWallDown = shadPosX;
+ shadWallBitAddr = shadBitAddr;
+ shadWallDestAddr = (byte *)screen->getBasePtr(shadDrawX + diffX, shadDrawY + diffY);
+ shadWallBitMask = shadBitMask;
+ shadWallPosY = shadPosY;
+ shadWallSkipX = shadSkipX;
+ shadWallModulo = sprModulo;
+ }
+ //byebye
+ if (shadDirection && shadWallDown) {
+ int shadBitMaskWallCopyTrans = shadWallBitMask;
+ int shadBitAddrWallCopyTrans = shadWallBitAddr;
+ background = shadWallDestAddr;
+ shadowHero = (byte *)makeShadow->getBasePtr(shadWallSkipX, shadowHeroY);
+
+ if (ctLoop > shadWallSkipX && ctLoop - shadWallSkipX > shadWallModulo) {
+ //WALL_copy_trans
+ shadWDFlag = false;
+ int shadZoomXWall = drawNode->scaleValue;
+ int backgroundDiffWall = 0;
+ int shadowHeroXWall = 0;
+ //ct_loop:
+ for (int m = 0; m < ctLoop; m++) {
+ shadZoomXWall -= 100;
+ if (shadZoomXWall < 0 && drawNode->scaleValue != 10000) {
+ shadZoomXWall += drawNode->scaleValue;
+ } else {
+ //point_ok:
+ if (*shadowHero == GraphicsMan::kShadowColor) {
+ if ((shadBitMaskWallCopyTrans & vm->_shadowBitmap[shadBitAddrWallCopyTrans + PrinceEngine::kShadowBitmapSize])) {
+ *background = *(sprShadow + *background);
+ }
+ }
+ //ct_next
+ if (shadBitMaskWallCopyTrans == 1) {
+ shadBitMaskWallCopyTrans = 128;
+ shadBitAddrWallCopyTrans++;
+ } else {
+ shadBitMaskWallCopyTrans >>= 1;
+ }
+ //okok
+ backgroundDiffWall++;
+ background = shadWallDestAddr + backgroundDiffWall;
+ }
+ shadowHeroXWall++;
+ shadowHero = (byte *)makeShadow->getBasePtr(shadWallSkipX + shadowHeroXWall, shadowHeroY);
+ }
+ }
+ //krap2
+ shadWallDestAddr -= PrinceEngine::kNormalWidth;
+ shadWallBitAddr -= PrinceEngine::kMaxPicWidth / 8;
+ shadWallPosY--;
+ }
+ }
+ }
+ //skip_line
+ //next_line
+ if (READ_LE_UINT16(shadowLineStart + 2) < READ_LE_UINT16(shadowLineStart - 2)) {
+ //minus_y
+ shadBitAddr -= PrinceEngine::kMaxPicWidth / 8;
+ shadPosY--;
+ diffY--;
+ } else if (READ_LE_UINT16(shadowLineStart + 2) > READ_LE_UINT16(shadowLineStart - 2)) {
+ shadBitAddr += PrinceEngine::kMaxPicWidth / 8;
+ shadPosY++;
+ diffY++;
+ }
+ //no_change_y
+ if (READ_LE_UINT16(shadowLineStart) < READ_LE_UINT16(shadowLineStart - 4)) {
+ //minus_x
+ shadPosX--;
+ //rol
+ if (shadBitMask == 128) {
+ shadBitMask = 1;
+ shadBitAddr--;
+ } else {
+ shadBitMask <<= 1;
+ }
+ diffX--;
+ } else if (READ_LE_UINT16(shadowLineStart) > READ_LE_UINT16(shadowLineStart - 4)) {
+ shadPosX++;
+ //ror
+ if (shadBitMask == 1) {
+ shadBitMask = 128;
+ shadBitAddr++;
+ } else {
+ shadBitMask >>= 1;
+ }
+ diffX++;
+ }
+ //no_change_x
+ shadowLineStart += 4;
+ shadowHeroY--;
+ if (shadowHeroY < 0) {
+ break;
+ }
+ shadowHeroX = 0;
+ background = (byte *)screen->getBasePtr(shadDrawX + diffX, shadDrawY + diffY);
+ shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY);
+ }
+ //koniec_bajki - end_of_a_story
+ }
+ makeShadow->free();
+ delete makeShadow;
+}
+
+void Hero::setScale(int8 zoomBitmapValue) {
+ if (!zoomBitmapValue) {
+ _vm->_scaleValue = 10000;
+ } else {
+ _vm->_scaleValue = 10000 / zoomBitmapValue;
+ }
+}
+
+void Hero::selectZoom() {
+ int8 zoomBitmapValue = *(_vm->_zoomBitmap + _middleY / 4 * _vm->kZoomBitmapWidth + _middleX / 4);
+ setScale(zoomBitmapValue);
+}
+
+int Hero::rotateHero(int oldDirection, int newDirection) {
+ switch (oldDirection) {
+ case kHeroDirLeft:
+ switch (newDirection) {
+ case kHeroDirRight:
+ return kMove_MLR;
+ case kHeroDirUp:
+ return kMove_MLU;
+ case kHeroDirDown:
+ return kMove_MLD;
+ }
+ break;
+ case kHeroDirRight:
+ switch (newDirection) {
+ case kHeroDirLeft:
+ return kMove_MRL;
+ case kHeroDirUp:
+ return kMove_MRU;
+ case kHeroDirDown:
+ return kMove_MRD;
+ }
+ break;
+ case kHeroDirUp:
+ switch (newDirection) {
+ case kHeroDirLeft:
+ return kMove_MUL;
+ case kHeroDirRight:
+ return kMove_MUR;
+ case kHeroDirDown:
+ return kMove_MUD;
+ }
+ break;
+ case kHeroDirDown:
+ switch (newDirection) {
+ case kHeroDirLeft:
+ return kMove_MDL;
+ case kHeroDirRight:
+ return kMove_MDR;
+ case kHeroDirUp:
+ return kMove_MDU;
+ }
+ break;
+ }
+ error("rotateHero - wrong directions - old %d, new %d", oldDirection, newDirection);
+}
+
+void Hero::heroStanding() {
+ _phase = 0;
+ switch (_lastDirection) {
+ case kHeroDirLeft:
+ _moveSetType = kMove_SL;
+ break;
+ case kHeroDirRight:
+ _moveSetType = kMove_SR;
+ break;
+ case kHeroDirUp:
+ _moveSetType = kMove_SU;
+ break;
+ case kHeroDirDown:
+ _moveSetType = kMove_SD;
+ break;
+ }
+}
+
+void Hero::showHero() {
+ if (_visible && !_vm->_flags->getFlagValue(Flags::NOHEROATALL)) {
+
+ if (_talkTime != 0) {
+ _talkTime--;
+ }
+
+ // Scale of hero
+ selectZoom();
+
+ if (_state != kHeroStateStay) {
+ _boredomTime = 0;
+ }
+
+ if (_state == kHeroStateSpec) {
+ if (_specAnim != nullptr) {
+ if (_phase < _specAnim->getPhaseCount() - 1) {
+ _phase++;
+ } else {
+ if (!_talkTime) {
+ _state = kHeroStateStay;
+ } else {
+ _state = kHeroStateTalk;
+ }
+ countDrawPosition();
+ return;
+ }
+ } else {
+ _state = kHeroStateStay;
+ }
+ } else {
+ freeHeroAnim();
+ }
+
+ if (_state == kHeroStateTalk) {
+ if (_talkTime) {
+ switch (_lastDirection) {
+ case kHeroDirLeft:
+ _moveSetType = kMove_TL;
+ break;
+ case kHeroDirRight:
+ _moveSetType = kMove_TR;
+ break;
+ case kHeroDirUp:
+ _moveSetType = kMove_TU;
+ break;
+ case kHeroDirDown:
+ _moveSetType = kMove_TD;
+ break;
+ }
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) {
+ _phase++;
+ } else {
+ _phase = _moveSet[_moveSetType]->getLoopCount();
+ }
+ } else {
+ _state = kHeroStateStay;
+ }
+ }
+
+ if (_state == kHeroStateBore) {
+ switch (_boreNum) {
+ case 0:
+ _moveSetType = kMove_BORED1;
+ break;
+ case 1:
+ _moveSetType = kMove_BORED2;
+ break;
+ }
+ if (_moveSet[_moveSetType] != nullptr) {
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) {
+ _phase++;
+ } else {
+ _phase = 0;
+ _lastDirection = kHeroDirDown;
+ _state = kHeroStateStay;
+ }
+ } else {
+ _state = kHeroStateStay;
+ }
+ }
+
+ if (_state == kHeroStateStay) {
+ if (!_vm->_optionsFlag) {
+ if (!_vm->_interpreter->getLastOPCode() || !_vm->_interpreter->getFgOpcodePC()) {
+ _boredomTime++;
+ if (_boredomTime == _maxBoredom) {
+ _boreNum =_vm->_randomSource.getRandomNumber(1); // rand one of two 'bored' animation
+ _phase = 0;
+ _state = kHeroStateBore;
+ if (_lastDirection == kHeroDirUp) {
+ _lastDirection = kHeroDirLeft;
+ } else {
+ _lastDirection = kHeroDirDown;
+ }
+ }
+ } else {
+ _boredomTime = 0;
+ }
+ } else {
+ _boredomTime = 0;
+ }
+ heroStanding();
+ }
+
+ if (_state == kHeroStateTurn) {
+ if (_destDirection && (_lastDirection != _destDirection)) {
+ _phase = 0;
+ int rotateDir = rotateHero(_lastDirection, _destDirection);
+ _lastDirection = _destDirection;
+ if (rotateDir) {
+ _moveSetType = rotateDir;
+ _state = kHeroStateTran;
+ } else {
+ _state = kHeroStateStay;
+ heroStanding();
+ }
+ } else {
+ _state = kHeroStateStay;
+ heroStanding();
+ }
+ }
+
+ if (_state == kHeroStateTran) {
+ if (_moveSet[_moveSetType] != nullptr) {
+ // only in bear form
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) {
+ _phase += 2;
+ } else {
+ _state = kHeroStateStay;
+ heroStanding();
+ }
+ } else {
+ _state = kHeroStateStay;
+ heroStanding();
+ }
+ }
+
+ if (_state == kHeroStateMvan) {
+ if (_moveSet[_moveSetType] != nullptr) {
+ // only in bear form
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) {
+ _phase += 2;
+ } else {
+ _state = kHeroStateMove;
+ }
+ } else {
+ _state = kHeroStateMove;
+ }
+ }
+
+ if (_state == kHeroStateDelayMove) {
+ _moveDelay--;
+ if (!_moveDelay) {
+ _state = kHeroStateMove;
+ }
+ }
+
+ int x, y, dir;
+
+ if (_state == kHeroStateMove || _state == kHeroStateRun) {
+ //go_for_it:
+ while (1) {
+ if (_currCoords != nullptr) {
+ if (READ_LE_UINT32(_currCoords) != 0xFFFFFFFF) {
+ x = READ_LE_UINT16(_currCoords);
+ y = READ_LE_UINT16(_currCoords + 2);
+ _currCoords += 4;
+ dir = *_currDirTab;
+ _currDirTab++;
+ if (_lastDirection != dir) {
+ _phase = 0;
+ int rotateDir = rotateHero(_lastDirection, dir);
+ _lastDirection = dir;
+ if (_moveSet[rotateDir] != nullptr) {
+ // only in bear form
+ _state = kHeroStateMvan;
+ _moveSetType = rotateDir;
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) {
+ _phase += 2;
+ break;
+ } else {
+ _state = kHeroStateMove;
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+ //no_need_direction_change
+ if (dir == kHeroDirLeft) {
+ if (_middleX - x >= _step) {
+ heroMoveGotIt(x, y, dir);
+ break;
+ }
+ } else if (dir == kHeroDirRight) {
+ if (x - _middleX >= _step) {
+ heroMoveGotIt(x, y, dir);
+ break;
+ }
+ } else if (dir == kHeroDirUp) {
+ if (_middleY - y >= _step) {
+ heroMoveGotIt(x, y, dir);
+ break;
+ }
+ } else if (dir == kHeroDirDown) {
+ if (y - _middleY >= _step) {
+ heroMoveGotIt(x, y, dir);
+ break;
+ }
+ }
+ } else {
+ //finito
+ _middleX = READ_LE_UINT16(_currCoords - 4);
+ _middleY = READ_LE_UINT16(_currCoords - 2);
+ selectZoom();
+
+ if (_coords != nullptr) {
+ free(_coords);
+ _coords = nullptr;
+ _currCoords = nullptr;
+ }
+
+ if (_dirTab != nullptr) {
+ free(_dirTab);
+ _dirTab = nullptr;
+ _currDirTab = nullptr;
+ }
+
+ _boredomTime = 0;
+ _phase = 0;
+ _state = kHeroStateTurn;
+
+ if (!_destDirection) {
+ _destDirection = _lastDirection;
+ }
+
+ heroStanding();
+
+ break;
+ }
+ } else {
+ heroStanding();
+ break;
+ }
+ }
+ }
+ countDrawPosition();
+ }
+}
+
+void Hero::drawHero() {
+ if (_visible && !_vm->_flags->getFlagValue(Flags::NOHEROATALL)) {
+ freeZoomedSurface();
+ Graphics::Surface *mainHeroSurface = getSurface();
+ if (mainHeroSurface) {
+ DrawNode newDrawNode;
+ newDrawNode.posX = _drawX;
+ newDrawNode.posY = _drawY;
+ newDrawNode.posZ = _drawZ;
+ newDrawNode.width = 0;
+ newDrawNode.height = 0;
+ newDrawNode.originalRoomSurface = nullptr;
+ newDrawNode.data = _vm->_transTable;
+ newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode;
+
+ if (_vm->_scaleValue != 10000) {
+ _zoomedHeroSurface = zoomSprite(mainHeroSurface);
+ newDrawNode.s = _zoomedHeroSurface;
+ } else {
+ newDrawNode.s = mainHeroSurface;
+ }
+ _vm->_drawNodeList.push_back(newDrawNode);
+
+ drawHeroShadow(mainHeroSurface);
+
+ }
+ }
+}
+
+void Hero::drawHeroShadow(Graphics::Surface *heroFrame) {
+ DrawNode newDrawNode;
+ newDrawNode.posX = _middleX - _scaledFrameXSize / 2;
+ newDrawNode.posY = _middleY - _shadMinus - 1;
+ newDrawNode.posZ = kHeroShadowZ;
+ newDrawNode.width = 0;
+ newDrawNode.height = 0;
+ newDrawNode.scaleValue = _vm->_scaleValue;
+ newDrawNode.originalRoomSurface = nullptr;
+ newDrawNode.data = _vm;
+ newDrawNode.drawFunction = &showHeroShadow;
+ newDrawNode.s = heroFrame;
+ _vm->_drawNodeList.push_back(newDrawNode);
+}
+
+void Hero::heroMoveGotIt(int x, int y, int dir) {
+ _middleX = x;
+ _middleY = y;
+ selectZoom();
+
+ switch (dir) {
+ case kHeroDirLeft:
+ _moveSetType = kMove_ML;
+ break;
+ case kHeroDirRight:
+ _moveSetType = kMove_MR;
+ break;
+ case kHeroDirUp:
+ _moveSetType = kMove_MU;
+ break;
+ case kHeroDirDown:
+ _moveSetType = kMove_MD;
+ break;
+ }
+
+ if (_vm->_flags->getFlagValue(Flags::HEROFAST) || _state == kHeroStateRun) {
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) {
+ _phase += 2;
+ } else {
+ _phase = 0;
+ }
+ } else {
+ if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) {
+ _phase++;
+ } else {
+ _phase = 0;
+ }
+ }
+
+ _step = kStepLeftRight;
+ if (_moveSetType == kMove_MU || _moveSetType == kMove_MD) {
+ _step = kStepUpDown;
+ }
+ if (_vm->_flags->getFlagValue(Flags::HEROFAST)) {
+ _step *= 2.5;
+ } else if (_state == kHeroStateRun) {
+ _step *= 2;
+ }
+}
+
+void Hero::scrollHero() {
+ int scrollType = _vm->_flags->getFlagValue(Flags::SCROLLTYPE);
+ int position = _middleX;
+ int scrollValue, scrollValue2;
+
+ switch (scrollType) {
+ case 0:
+ position = _middleX;
+ break;
+ case 1:
+ scrollValue = _vm->_flags->getFlagValue(Flags::SCROLLVALUE);
+ position = _vm->_normAnimList[scrollValue]._currX + _vm->_normAnimList[scrollValue]._currW / 2;
+ break;
+ case 2:
+ scrollValue = _vm->_flags->getFlagValue(Flags::SCROLLVALUE);
+ scrollValue2 = _vm->_flags->getFlagValue(Flags::SCROLLVALUE2);
+ position = scrollValue;
+ if (scrollValue < scrollValue2) {
+ _vm->_flags->setFlagValue(Flags::SCROLLVALUE, 0);
+ } else {
+ _vm->_flags->setFlagValue(Flags::SCROLLVALUE, scrollValue - scrollValue2);
+ }
+ break;
+ }
+
+ int locationWidth = _vm->_sceneWidth;
+ int difference = locationWidth - _vm->kNormalWidth / 2;
+
+ int destValue = 0;
+ if (position > _vm->kNormalWidth / 2) {
+ destValue = difference - _vm->kNormalWidth / 2;
+ }
+ if (position < difference) {
+ destValue = position - _vm->kNormalWidth / 2;
+ }
+
+ if(destValue < 0) {
+ destValue = 0;
+ }
+ _vm->_picWindowX = destValue;
+ _drawX -= destValue;
+}
+
+void Hero::freeOldMove() {
+ if (_coords != nullptr) {
+ free(_coords);
+ _coords = nullptr;
+ }
+ if (_dirTab != nullptr) {
+ free(_dirTab);
+ _dirTab = nullptr;
+ }
+ _step = 0;
+ _phase = 0;
+ _moveDelay = 0;
+ _state = Hero::kHeroStateStay;
+}
+
+void Hero::freeHeroAnim() {
+ if (_specAnim != nullptr) {
+ delete _specAnim;
+ _specAnim = nullptr;
+ }
+}
+
+void Hero::freeZoomedSurface() {
+ if (_zoomedHeroSurface != nullptr) {
+ _zoomedHeroSurface->free();
+ delete _zoomedHeroSurface;
+ _zoomedHeroSurface = nullptr;
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/hero.h b/engines/prince/hero.h
new file mode 100644
index 0000000000..d5f7d8cc7a
--- /dev/null
+++ b/engines/prince/hero.h
@@ -0,0 +1,184 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_HERO_H
+#define PRINCE_HERO_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/memstream.h"
+
+#include "graphics/surface.h"
+#include "graphics/primitives.h"
+
+namespace Prince {
+
+class Animation;
+class PrinceEngine;
+class GraphicsMan;
+struct InventoryItem;
+struct DrawNode;
+
+class Hero {
+public:
+ static const uint32 kMoveSetSize = 26;
+ static const int16 kStepLeftRight = 8;
+ static const int16 kStepUpDown = 4;
+ static const int16 kHeroShadowZ = 2;
+
+ enum State {
+ kHeroStateStay,
+ kHeroStateTurn,
+ kHeroStateMove,
+ kHeroStateBore,
+ kHeroStateSpec,
+ kHeroStateTalk,
+ kHeroStateMvan,
+ kHeroStateTran,
+ kHeroStateRun,
+ kHeroStateDelayMove
+ };
+
+ enum Direction {
+ kHeroDirLeft = 1,
+ kHeroDirRight = 2,
+ kHeroDirUp = 3,
+ kHeroDirDown = 4
+ };
+
+ enum MoveSet {
+ kMove_SL,
+ kMove_SR,
+ kMove_SU,
+ kMove_SD,
+ kMove_ML,
+ kMove_MR,
+ kMove_MU,
+ kMove_MD,
+ kMove_TL,
+ kMove_TR,
+ kMove_TU,
+ kMove_TD,
+ kMove_MLU,
+ kMove_MLD,
+ kMove_MLR,
+ kMove_MRU,
+ kMove_MRD,
+ kMove_MRL,
+ kMove_MUL,
+ kMove_MUR,
+ kMove_MUD,
+ kMove_MDL,
+ kMove_MDR,
+ kMove_MDU,
+ kMove_BORED1,
+ kMove_BORED2
+ };
+
+ // Used instead of offset in getData
+ enum AttrId {
+ kHeroLastDir = 26,
+ kHeroAnimSet = 120
+ };
+
+ uint16 getData(AttrId dataId);
+
+ Hero(PrinceEngine *vm, GraphicsMan *graph);
+ ~Hero();
+ bool loadAnimSet(uint32 heroAnimNumber);
+
+ Graphics::Surface *getSurface();
+
+ void setPos(int16 x, int16 y) { _middleX = x; _middleY = y; }
+ void setVisible(bool flag) { _visible = flag; }
+
+ void showHero();
+ void drawHero();
+ void freeZoomedSurface();
+ void heroStanding();
+ void heroMoveGotIt(int x, int y, int dir);
+ int rotateHero(int oldDirection, int newDirection);
+ void scrollHero();
+ void setScale(int8 zoomBitmapValue);
+ int getScaledValue(int size);
+ void selectZoom();
+ void countDrawPosition();
+ Graphics::Surface *zoomSprite(Graphics::Surface *heroFrame);
+ void line(int x1, int y1, int x2, int y2);
+ void plotPoint(int x, int y);
+ static void showHeroShadow(Graphics::Surface *screen, DrawNode *drawNode);
+ void drawHeroShadow(Graphics::Surface *heroFrame);
+ void freeOldMove();
+ void freeHeroAnim();
+
+ uint16 _number;
+ uint16 _visible;
+ int16 _state;
+ int16 _middleX; // middle of X
+ int16 _middleY; // lower part of hero
+ int16 _moveSetType;
+
+ int16 _frameXSize;
+ int16 _frameYSize;
+ int16 _scaledFrameXSize;
+ int16 _scaledFrameYSize;
+ int16 _drawX;
+ int16 _drawY;
+ int16 _drawZ;
+
+ byte *_coords; // array of coordinates
+ byte *_dirTab; // array of directions
+ byte *_currCoords; // current coordinations
+ byte *_currDirTab; // current direction
+ int16 _lastDirection; // previous move direction
+ int16 _destDirection;
+ int16 _leftRightMainDir; // left or right - dominant direction
+ int16 _upDownMainDir; // up or down - dominant direction
+ int32 _phase; // Phase animation phase
+ int16 _step; // Step x/y step size depends on direction
+ int16 _maxBoredom; // stand still timeout
+ int16 _boredomTime; // Boredom current boredom time in frames
+ uint16 _boreNum; // Bore anim frame
+ int16 _talkTime; // TalkTime time of talk anim
+ Animation *_specAnim; // additional anim
+ Graphics::Surface *_zoomedHeroSurface;
+
+ uint16 _currHeight; // height of current anim phase
+
+ Common::Array<byte> _inventory; // Inventory array of items
+ Common::Array<byte> _inventory2; // Inventory2 array of items
+ // Font subtitiles font
+ int _color; // subtitles color
+ uint32 _animSetNr; // number of animation set
+ Common::Array<Animation *> _moveSet; // MoveAnims MoveSet
+
+ uint32 _moveDelay;
+ uint32 _shadMinus;
+
+private:
+ PrinceEngine *_vm;
+ GraphicsMan *_graph;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/hero_set.h b/engines/prince/hero_set.h
new file mode 100644
index 0000000000..dfe7e50268
--- /dev/null
+++ b/engines/prince/hero_set.h
@@ -0,0 +1,244 @@
+/* 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"
+
+namespace Prince {
+
+const int heroSetBack[7] = { 0, 0, 10, 0, 6, 0, 0 };
+
+typedef const char *HeroSetAnimNames[26];
+
+static HeroSetAnimNames heroSet5 = {
+ "SL_DIAB.ANI",
+ "SR_DIAB.ANI",
+ "SU_DIAB.ANI",
+ "SD_DIAB.ANI",
+ nullptr,
+ nullptr,
+ "MU_DIAB.ANI",
+ "MD_DIAB.ANI",
+ "TL_DIAB.ANI",
+ "TR_DIAB.ANI",
+ "TU_DIAB.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+static HeroSetAnimNames heroSet1 = {
+ "SL_HERO1.ANI",
+ "SR_HERO1.ANI",
+ "SU_HERO1.ANI",
+ "SD_HERO1.ANI",
+ "ML_HERO1.ANI",
+ "MR_HERO1.ANI",
+ "MU_HERO1.ANI",
+ "MD_HERO1.ANI",
+ "TL_HERO1.ANI",
+ "TR_HERO1.ANI",
+ "TU_HERO1.ANI",
+ "TD_HERO1.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "KSI_KURZ.ANI",
+ "KS_WLOSY.ANI"
+};
+
+static HeroSetAnimNames heroSet2 = {
+ "SL_HERO2.ANI",
+ "SR_HERO2.ANI",
+ "SU_HERO2.ANI",
+ "SD_HERO2.ANI",
+ "ML_HERO2.ANI",
+ "MR_HERO2.ANI",
+ "MU_HERO2.ANI",
+ "MD_HERO2.ANI",
+ "TL_HERO2.ANI",
+ "TR_HERO2.ANI",
+ "TU_HERO2.ANI",
+ "TD_HERO2.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "KSI_KU_S.ANI",
+ "KS_WLO_S.ANI"
+};
+
+static HeroSetAnimNames heroSet3 = {
+ "SL_BEAR.ANI",
+ "SR_BEAR.ANI",
+ "SU_BEAR.ANI",
+ "SD_BEAR.ANI",
+ "NIED-LEW.ANI",
+ "NIED-PRW.ANI",
+ "NIED-TYL.ANI",
+ "NIED-PRZ.ANI",
+ "SL_BEAR.ANI",
+ "SR_BEAR.ANI",
+ "SU_BEAR.ANI",
+ "SD_BEAR.ANI",
+ "N_LW-TYL.ANI",
+ "N_LW-PRZ.ANI",
+ "N_LW-PR.ANI",
+ "N_PR-TYL.ANI",
+ "N_PR-PRZ.ANI",
+ "N_PR-LW.ANI",
+ "N_TYL-LW.ANI",
+ "N_TYL-PR.ANI",
+ "N_TL-PRZ.ANI",
+ "N_PRZ-LW.ANI",
+ "N_PRZ-PR.ANI",
+ "N_PRZ-TL.ANI",
+ nullptr,
+ nullptr,
+};
+
+static HeroSetAnimNames shanSet1 = {
+ "SL_SHAN.ANI",
+ "SR_SHAN.ANI",
+ "SU_SHAN.ANI",
+ "SD_SHAN.ANI",
+ "ML_SHAN.ANI",
+ "MR_SHAN.ANI",
+ "MU_SHAN.ANI",
+ "MD_SHAN.ANI",
+ "TL_SHAN.ANI",
+ "TR_SHAN.ANI",
+ "TU_SHAN.ANI",
+ "TD_SHAN.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "B1_SHAN.ANI",
+ "B2_SHAN.ANI",
+};
+
+static HeroSetAnimNames shanSet2 = {
+ "SL_SHAN2.ANI",
+ "SR_SHAN2.ANI",
+ "SU_SHAN.ANI",
+ "SD_SHAN2.ANI",
+ "ML_SHAN2.ANI",
+ "MR_SHAN2.ANI",
+ "MU_SHAN.ANI",
+ "MD_SHAN2.ANI",
+ "TL_SHAN2.ANI",
+ "TR_SHAN2.ANI",
+ "TU_SHAN.ANI",
+ "TD_SHAN2.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "B1_SHAN2.ANI",
+ "B2_SHAN2.ANI"
+};
+
+static HeroSetAnimNames arivSet1 = {
+ "SL_ARIV.ANI",
+ "SR_ARIV.ANI",
+ "SU_ARIV.ANI",
+ "SD_ARIV.ANI",
+ "ML_ARIV.ANI",
+ "MR_ARIV.ANI",
+ "MU_ARIV.ANI",
+ "MD_ARIV.ANI",
+ "TL_ARIV.ANI",
+ "TR_ARIV.ANI",
+ "TU_ARIV.ANI",
+ "TD_ARIV.ANI",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+const HeroSetAnimNames *heroSetTable[7] = {
+ &heroSet1,
+ &heroSet2,
+ &heroSet3,
+ &shanSet1,
+ &arivSet1,
+ &heroSet5,
+ &shanSet2,
+};
+
+} // End of namespace Prince
diff --git a/engines/prince/mhwanh.cpp b/engines/prince/mhwanh.cpp
new file mode 100644
index 0000000000..ef94ef71f9
--- /dev/null
+++ b/engines/prince/mhwanh.cpp
@@ -0,0 +1,71 @@
+/* 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/stream.h"
+
+#include "graphics/surface.h"
+
+#include "prince/mhwanh.h"
+
+namespace Prince {
+
+MhwanhDecoder::MhwanhDecoder() : _surface(nullptr), _palette(nullptr) {
+}
+
+MhwanhDecoder::~MhwanhDecoder() {
+ destroy();
+}
+
+void MhwanhDecoder::destroy() {
+ if (_surface != nullptr) {
+ _surface->free();
+ delete _surface;
+ _surface = nullptr;
+ }
+ if (_palette != nullptr) {
+ free(_palette);
+ _palette = nullptr;
+ }
+}
+
+bool MhwanhDecoder::loadStream(Common::SeekableReadStream &stream) {
+ destroy();
+ stream.seek(0);
+ stream.skip(0x20);
+ // Read the palette
+ _palette = (byte *)malloc(kPaletteColorCount * 3);
+ for (uint16 i = 0; i < kPaletteColorCount; i++) {
+ _palette[i * 3] = stream.readByte();
+ _palette[i * 3 + 1] = stream.readByte();
+ _palette[i * 3 + 2] = stream.readByte();
+ }
+
+ _surface = new Graphics::Surface();
+ _surface->create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
+ for (int h = 0; h < 480; h++) {
+ stream.read(_surface->getBasePtr(0, h), 640);
+ }
+
+ return true;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/mhwanh.h b/engines/prince/mhwanh.h
new file mode 100644
index 0000000000..f8165a7666
--- /dev/null
+++ b/engines/prince/mhwanh.h
@@ -0,0 +1,55 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_MHWANH_H
+#define PRINCE_MHWANH_H
+
+#include "image/image_decoder.h"
+#include "image/bmp.h"
+
+#include "graphics/surface.h"
+
+#include "resource.h"
+
+namespace Prince {
+
+class MhwanhDecoder : public Image::ImageDecoder {
+public:
+ MhwanhDecoder();
+ virtual ~MhwanhDecoder();
+
+ // ImageDecoder API
+ void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+ virtual Graphics::Surface *getSurface() const { return _surface; }
+ virtual const byte *getPalette() const { return _palette; }
+ uint16 getPaletteCount() const { return kPaletteColorCount; }
+ static const uint16 kPaletteColorCount = 256;
+
+private:
+ Graphics::Surface *_surface;
+ byte *_palette;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/mob.cpp b/engines/prince/mob.cpp
new file mode 100644
index 0000000000..de825ef8b2
--- /dev/null
+++ b/engines/prince/mob.cpp
@@ -0,0 +1,108 @@
+/* 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 "prince/mob.h"
+
+namespace Prince {
+
+bool Mob::loadFromStream(Common::SeekableReadStream &stream) {
+ int32 pos = stream.pos();
+
+ uint16 visible = stream.readUint16LE();
+
+ if (visible == 0xFFFF)
+ return false;
+
+ _visible = visible;
+ _type = stream.readUint16LE();
+ _rect.left = stream.readUint16LE();
+ _rect.top = stream.readUint16LE();
+ _rect.right = stream.readUint16LE();
+ _rect.bottom = stream.readUint16LE();
+
+ _mask = stream.readUint16LE();
+
+ _examPosition.x = stream.readUint16LE();
+ _examPosition.y = stream.readUint16LE();
+ _examDirection = (Direction)stream.readUint16LE();
+
+ _usePosition.x = stream.readByte();
+ _usePosition.y = stream.readByte();
+ _useDirection = (Direction)stream.readUint16LE();
+
+ uint32 nameOffset = stream.readUint32LE();
+ uint32 examTextOffset = stream.readUint32LE();
+
+ byte c;
+ stream.seek(nameOffset);
+ _name.clear();
+ while ((c = stream.readByte()))
+ _name += c;
+
+ stream.seek(examTextOffset);
+ _examText.clear();
+ c = stream.readByte();
+ if (c) {
+ _examText += c;
+ do {
+ c = stream.readByte();
+ _examText += c;
+ } while (c != 255);
+ }
+ stream.seek(pos + 32);
+
+ return true;
+}
+
+void Mob::setData(AttrId dataId, uint16 value) {
+ switch (dataId) {
+ case kMobExamDir:
+ _examDirection = (Direction)value;
+ break;
+ case kMobExamX:
+ _examPosition.x = value;
+ break;
+ case kMobExamY:
+ _examPosition.y = value;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+uint16 Mob::getData(AttrId dataId) {
+ switch (dataId) {
+ case kMobVisible:
+ return _visible;
+ case kMobExamDir:
+ return _examDirection;
+ case kMobExamX:
+ return _examPosition.x;
+ case kMobExamY:
+ return _examPosition.y;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/mob.h b/engines/prince/mob.h
new file mode 100644
index 0000000000..ecf0eb4c3a
--- /dev/null
+++ b/engines/prince/mob.h
@@ -0,0 +1,70 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_MOB_H
+#define PRINCE_MOB_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+#include "prince/common.h"
+
+namespace Prince {
+
+class Mob {
+public:
+
+ Mob() : _name(""), _examText("") {}
+
+ bool loadFromStream(Common::SeekableReadStream &stream);
+
+ // Used instead of offset in setData and getData
+ enum AttrId {
+ kMobVisible = 0,
+ kMobExamX = 14,
+ kMobExamY = 16,
+ kMobExamDir = 18,
+ };
+
+ void setData(AttrId dataId, uint16 value);
+ uint16 getData(AttrId dataId);
+
+ bool _visible;
+ uint16 _type;
+ uint16 _mask;
+ Common::Rect _rect;
+
+ Common::Point _examPosition;
+ Direction _examDirection;
+
+ Common::Point _usePosition;
+ Direction _useDirection;
+
+ Common::String _name;
+ Common::String _examText;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/module.mk b/engines/prince/module.mk
new file mode 100644
index 0000000000..2dfa538a6e
--- /dev/null
+++ b/engines/prince/module.mk
@@ -0,0 +1,30 @@
+MODULE := engines/prince
+
+MODULE_OBJS = \
+ animation.o \
+ debugger.o \
+ script.o \
+ graphics.o \
+ mhwanh.o \
+ detection.o \
+ font.o \
+ mob.o \
+ object.o \
+ sound.o \
+ flags.o \
+ variatxt.o \
+ prince.o \
+ archive.o \
+ decompress.o \
+ hero.o \
+ cursor.o \
+ pscr.o \
+ saveload.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/prince/musNum.h b/engines/prince/musNum.h
new file mode 100644
index 0000000000..c24cba6ad7
--- /dev/null
+++ b/engines/prince/musNum.h
@@ -0,0 +1,87 @@
+/* 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.
+ *
+ */
+
+namespace Prince {
+
+enum RoomMus {
+ ROOM01MUS = 3,
+ ROOM02MUS = 9,
+ ROOM03MUS = 9,
+ ROOM04MUS = 9,
+ ROOM05MUS = 13,
+ ROOM06MUS = 9,
+ ROOM07MUS = 9,
+ ROOM08MUS = 9,
+ ROOM09MUS = 14,
+ ROOM10MUS = 9,
+ ROOM11MUS = 9,
+ ROOM12MUS = 9,
+ ROOM13MUS = 9,
+ ROOM14MUS = 9,
+ ROOM15MUS = 5,
+ ROOM16MUS = 5,
+ ROOM17MUS = 5,
+ ROOM18MUS = 5,
+ ROOM19MUS = 5,
+ ROOM20MUS = 12,
+ ROOM21MUS = 9,
+ ROOM22MUS = 9,
+ ROOM23MUS = 1,
+ ROOM24MUS = 1,
+ ROOM25MUS = 2,
+ ROOM26MUS = 10,
+ ROOM27MUS = 7,
+ ROOM28MUS = 10,
+ ROOM29MUS = 10,
+ ROOM30MUS = 11,
+ ROOM31MUS = 14,
+ ROOM32MUS = 11,
+ ROOM33MUS = 7,
+ ROOM34MUS = 7,
+ ROOM35MUS = 7,
+ ROOM36MUS = 7,
+ ROOM37MUS = 7,
+ ROOM38MUS = 7,
+ ROOM39MUS = 7,
+ ROOM40MUS = 7,
+ ROOM41MUS = 7,
+ ROOM42MUS = 7,
+ ROOM43MUS = 15,
+ ROOM46MUS = 100,
+ ROOM47MUS = 100,
+ ROOM48MUS = 100,
+ ROOM49MUS = 100,
+ ROOM50MUS = 100,
+ ROOM51MUS = 12,
+ ROOM52MUS = 9,
+ ROOM53MUS = 5,
+ ROOM54MUS = 11,
+ ROOM55MUS = 11,
+ ROOM56MUS = 11,
+ ROOM57MUS = 7,
+ ROOM58MUS = 13,
+ ROOM59MUS = 16,
+ ROOM60MUS = 4,
+ ROOM61MUS = 0
+};
+
+} // End of namespace Prince
diff --git a/engines/prince/object.cpp b/engines/prince/object.cpp
new file mode 100644
index 0000000000..a9a96455b1
--- /dev/null
+++ b/engines/prince/object.cpp
@@ -0,0 +1,113 @@
+/* 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/archive.h"
+#include "common/debug-channels.h"
+#include "common/debug.h"
+#include "common/stream.h"
+
+#include "graphics/surface.h"
+
+#include "prince/object.h"
+
+namespace Prince {
+
+Object::Object() : _surface(nullptr), _x(0), _y(0), _z(0), _flags(0), _width(0),
+ _height(0), _zoomTime(0), _zoomSurface(nullptr)
+{
+}
+
+Object::~Object() {
+ if (_surface != nullptr) {
+ _surface->free();
+ delete _surface;
+ _surface = nullptr;
+ }
+ if (_zoomSurface != nullptr) {
+ _zoomSurface->free();
+ delete _zoomSurface;
+ _zoomSurface = nullptr;
+ }
+}
+
+void Object::loadSurface(Common::SeekableReadStream &stream) {
+ stream.skip(4);
+ _width = stream.readUint16LE();
+ _height = stream.readUint16LE();
+ _surface = new Graphics::Surface();
+ _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int h = 0; h < _surface->h; h++) {
+ stream.read(_surface->getBasePtr(0, h), _surface->w);
+ }
+}
+
+bool Object::loadFromStream(Common::SeekableReadStream &stream) {
+ int32 pos = stream.pos();
+ uint16 x = stream.readUint16LE();
+ if (x == 0xFFFF) {
+ return false;
+ }
+ _x = x;
+ _y = stream.readSint16LE(); // skull mini-game has some signed y coords
+
+ const Common::String obStreamName = Common::String::format("OB%02d", stream.readUint16LE());
+ Common::SeekableReadStream *obStream = SearchMan.createReadStreamForMember(obStreamName);
+ if (obStream) {
+ loadSurface(*obStream);
+ }
+ delete obStream;
+
+ _flags = stream.readUint16LE();
+ _z = stream.readUint16LE();
+
+ stream.seek(pos + 16);
+
+ return true;
+}
+
+void Object::setData(AttrId dataId, int32 value) {
+ switch (dataId) {
+ case kObjectX:
+ _x = value;
+ break;
+ case kObjectY:
+ _y = value;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+int32 Object::getData(AttrId dataId) {
+ switch (dataId) {
+ case kObjectX:
+ return _x;
+ case kObjectY:
+ return _y;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/object.h b/engines/prince/object.h
new file mode 100644
index 0000000000..68edd061a0
--- /dev/null
+++ b/engines/prince/object.h
@@ -0,0 +1,70 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_OBJECT_H
+#define PRINCE_OBJECT_H
+
+#include "image/image_decoder.h"
+#include "graphics/surface.h"
+
+namespace Prince {
+
+class Object {
+public:
+ Object();
+ ~Object();
+
+ int32 _x;
+ int32 _y;
+ int32 _z;
+ uint16 _width;
+ uint16 _height;
+ int32 _flags;
+ int32 _zoomTime;
+ Graphics::Surface *_zoomSurface;
+
+ // Used instead of offset in setData and getData
+ enum AttrId {
+ kObjectAddr = 0,
+ kObjectX = 4,
+ kObjectY = 6,
+ kObjectZ = 8,
+ kObjectFlags = 10,
+ kObjectZoomInSource = 12,
+ kObjectZoomInLen = 16,
+ kObjectZoomInAddr = 20,
+ kObjectZoomInTime = 24
+ };
+
+ bool loadFromStream(Common::SeekableReadStream &stream);
+ Graphics::Surface *getSurface() const { return _surface; }
+ int32 getData(AttrId dataId);
+ void setData(AttrId dataId, int32 value);
+
+private:
+ void loadSurface(Common::SeekableReadStream &stream);
+ Graphics::Surface *_surface;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/option_text.h b/engines/prince/option_text.h
new file mode 100644
index 0000000000..d4d214c98c
--- /dev/null
+++ b/engines/prince/option_text.h
@@ -0,0 +1,85 @@
+/* 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.
+ *
+ */
+
+namespace Prince {
+
+// PL - Mazovia coding
+const char invOptionsTextPL[5][18] = {
+ "Obejrzyj",
+ "U\xa7""yj",
+ "Otw\xa2""rz/Pchnij",
+ "Zamknij/Poci\x86""gnij",
+ "Daj"
+};
+
+const char optionsTextPL[7][18] = {
+ "Podejd\xa6",
+ "Obejrzyj",
+ "Zabierz",
+ "U\xa7""yj",
+ "Otw\xa2""rz/Pchnij",
+ "Zamknij/Poci\x86""gnij",
+ "Porozmawiaj"
+};
+
+// DE - Other font then for PL + ISO 8859-2 or Windows-1250
+// + special letter values changing
+// Normal value: 196, 214, 220, 223, 228, 246, 252
+// Prince change: 131, 132, 133, 127, 128, 129, 130
+char invOptionsTextDE[5][17] = {
+ "Anschauen",
+ "Benutzen",
+ "\x84""ffnen/Sto\x7f""en",
+ "Schlie\x7f""en/Ziehen",
+ "Geben"
+};
+
+const char optionsTextDE[7][17] = {
+ "Hingehen",
+ "Anschauen",
+ "Wegnehmen",
+ "Benutzen",
+ "\x84""ffnen/Sto\x7f""en",
+ "Schlie\x7f""en/Ziehen",
+ "Ansprechen"
+};
+
+// EN
+const char *invOptionsTextEN[] = {
+ "Examine",
+ "Use",
+ "Open/Push",
+ "Close/Pull",
+ "Give"
+};
+
+const char *optionsTextEN[] = {
+ "Walk to",
+ "Examine",
+ "Pick up",
+ "Use",
+ "Open/Push",
+ "Close/Pull",
+ "Talk to"
+};
+
+} // End of namespace Prince
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
diff --git a/engines/prince/prince.h b/engines/prince/prince.h
new file mode 100644
index 0000000000..3d10c671f0
--- /dev/null
+++ b/engines/prince/prince.h
@@ -0,0 +1,668 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_H
+#define PRINCE_H
+
+#include "common/random.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/textconsole.h"
+#include "common/rect.h"
+#include "common/events.h"
+#include "common/endian.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+
+#include "image/bmp.h"
+
+#include "gui/debugger.h"
+
+#include "engines/engine.h"
+#include "engines/util.h"
+
+#include "audio/mixer.h"
+
+#include "video/flic_decoder.h"
+
+#include "prince/mob.h"
+#include "prince/object.h"
+#include "prince/pscr.h"
+
+namespace Prince {
+
+struct PrinceGameDescription;
+struct SavegameHeader;
+
+class PrinceEngine;
+class GraphicsMan;
+class Script;
+class Interpreter;
+class InterpreterFlags;
+class Debugger;
+class MusicPlayer;
+class VariaTxt;
+class Cursor;
+class MhwanhDecoder;
+class Font;
+class Hero;
+class Animation;
+class Room;
+class Pscr;
+
+struct Text {
+ const char *_str;
+ uint16 _x, _y;
+ uint16 _time;
+ uint32 _color;
+
+ Text() : _str(nullptr), _x(0), _y(0), _time(0), _color(255){
+ }
+};
+
+struct AnimListItem {
+ uint16 _type; // type of animation - for background anims RND of frame
+ uint16 _fileNumber;
+ uint16 _startPhase; // first phase number
+ uint16 _endPhase;
+ uint16 _loopPhase;
+ int16 _x;
+ int16 _y;
+ uint16 _loopType;
+ uint16 _nextAnim; // number of animation to do for loop = 3
+ uint16 _flags; // byte 0 - draw masks, byte 1 - draw in front of mask, byte 2 - load but turn off drawing
+ bool loadFromStream(Common::SeekableReadStream &stream);
+};
+
+struct BAS {
+ int32 _type; // type of sequence
+ int32 _data; // additional data
+ int32 _anims; // number of animations
+ int32 _current; // actual number of animation
+ int32 _counter; // time counter for animation
+ int32 _currRelative; //actual relative number for animation
+ int32 _data2; // additional data for measurements
+};
+
+const int kStructSizeBAS = 28;
+
+struct BASA {
+ int16 _num; // animation number
+ int16 _start; // initial frame
+ int16 _end; // final frame
+ //int16 _pad; // fulfilment to 8 bytes
+};
+
+const int kStructSizeBASA = 8;
+
+// background and normal animation
+struct Anim {
+ BASA _basaData;
+ int32 _addr; //animation adress
+ int16 _usage;
+ int16 _state; // state of animation: 0 - turning on, 1 - turning off
+ int16 _flags;
+ int16 _frame; // number of phase to show
+ int16 _lastFrame; // last phase
+ int16 _loopFrame; // first frame of loop
+ int16 _showFrame; // actual visible frame of animation
+ int16 _loopType; // type of loop (0 - last frame; 1 - normal loop (begin from _loopFrame); 2 - no loop; 3 - load new animation)
+ int16 _nextAnim; // number of next animation to load after actual
+ int16 _x;
+ int16 _y;
+ int32 _currFrame;
+ int16 _currX;
+ int16 _currY;
+ int16 _currW;
+ int16 _currH;
+ int16 _packFlag;
+ int32 _currShadowFrame;
+ int16 _packShadowFlag;
+ int32 _shadowBack;
+ int16 _relX;
+ int16 _relY;
+ Animation *_animData;
+ Animation *_shadowData;
+
+ enum AnimOffsets {
+ kAnimState = 10,
+ kAnimFrame = 14,
+ kAnimLastFrame = 16,
+ kAnimX = 26
+ };
+
+ int16 getAnimData(Anim::AnimOffsets offset) {
+ switch (offset) {
+ case kAnimState:
+ return _state;
+ case kAnimFrame:
+ return _frame + 1; // fix for location 30 - man with a dog animation
+ case kAnimX:
+ return _x;
+ default:
+ error("getAnimData() - Wrong offset type: %d", (int) offset);
+ }
+ }
+
+ void setAnimData(Anim::AnimOffsets offset, int16 value) {
+ if (offset == kAnimX) {
+ _x = value;
+ } else {
+ error("setAnimData() - Wrong offset: %d, value: %d", (int) offset, value);
+ }
+ }
+};
+
+struct BackgroundAnim {
+ BAS _seq;
+ Common::Array<Anim> backAnims;
+};
+
+enum AnimType {
+ kBackgroundAnimation,
+ kNormalAnimation
+};
+
+// Nak (PL - Nakladka)
+struct Mask {
+ int16 _state; // visible / invisible
+ int16 _flags; // turning on / turning off of an mask
+ int16 _x1;
+ int16 _y1;
+ int16 _x2;
+ int16 _y2;
+ int16 _z;
+ int16 _number; // number of mask for background recreating
+ int16 _width;
+ int16 _height;
+ byte *_data;
+
+ int16 getX() const {
+ return READ_LE_UINT16(_data);
+ }
+
+ int16 getY() const {
+ return READ_LE_UINT16(_data + 2);
+ }
+
+ int16 getWidth() const {
+ return READ_LE_UINT16(_data + 4);
+ }
+
+ int16 getHeight() const {
+ return READ_LE_UINT16(_data + 6);
+ }
+
+ byte *getMask() const {
+ return (byte *)(_data + 8);
+ }
+};
+
+struct InvItem {
+ int _x;
+ int _y;
+ Graphics::Surface *_surface;
+ Graphics::Surface *getSurface() const { return _surface; }
+};
+
+struct DrawNode {
+ int posX;
+ int posY;
+ int posZ;
+ int32 width;
+ int32 height;
+ int32 scaleValue;
+ Graphics::Surface *s;
+ Graphics::Surface *originalRoomSurface;
+ void *data;
+ void (*drawFunction)(Graphics::Surface *, DrawNode *);
+};
+
+struct DebugChannel {
+
+enum Type {
+ kScript,
+ kEngine
+};
+
+};
+
+class PrinceEngine : public Engine {
+protected:
+ Common::Error run();
+
+public:
+ PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc);
+ virtual ~PrinceEngine();
+
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual bool canSaveGameStateCurrently();
+ virtual bool canLoadGameStateCurrently();
+ virtual Common::Error saveGameState(int slot, const Common::String &desc);
+ virtual Common::Error loadGameState(int slot);
+
+ static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header);
+ Common::String generateSaveName(int slot);
+ void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
+ void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream);
+ bool loadGame(int slotNumber);
+ void resetGame();
+
+ int32 _creditsDataSize;
+ byte *_creditsData;
+ void scrollCredits();
+
+ int getGameType() const;
+ const char *getGameId() const;
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+
+ const PrinceGameDescription *_gameDescription;
+ Video::FlicDecoder _flicPlayer;
+ const Graphics::Surface *_flcFrameSurface;
+ VariaTxt *_variaTxt;
+
+ uint32 _talkTxtSize;
+ byte *_talkTxt;
+
+ bool loadLocation(uint16 locationNr);
+ bool loadAnim(uint16 animNr, bool loop);
+ bool loadVoice(uint32 textSlot, uint32 sampleSlot, const Common::String &name);
+ bool loadSample(uint32 sampleSlot, const Common::String &name);
+ bool loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName);
+ bool loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2);
+ bool loadTrans(byte *transTable, const char *resourceName);
+ bool loadMobPriority(const char *resourceName);
+
+ bool loadMusic(int musNumber);
+ void stopMusic();
+
+ void playSample(uint16 sampleId, uint16 loopType);
+ void stopSample(uint16 sampleId);
+ void stopAllSamples();
+ void freeSample(uint16 sampleId);
+ void freeAllSamples();
+
+ void setVoice(uint16 slot, uint32 sampleSlot, uint16 flag);
+
+ virtual GUI::Debugger *getDebugger();
+
+ void changeCursor(uint16 curId);
+ void printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y);
+ int calcTextLines(const char *s);
+ int calcTextTime(int numberOfLines);
+ void correctStringDEU(char *s);
+
+ static const uint8 kMaxTexts = 32;
+ Text _textSlots[kMaxTexts];
+
+ Hero *_mainHero;
+ Hero *_secondHero;
+
+ enum HeroId {
+ kMainHero,
+ kSecondHero
+ };
+
+ int _mouseFlag;
+ uint32 _currentTime;
+ uint16 _locationNr;
+ uint16 _sceneWidth;
+ int32 _picWindowX;
+ int32 _picWindowY;
+
+ Image::BitmapDecoder *_roomBmp;
+ MhwanhDecoder *_suitcaseBmp;
+ Room *_room;
+ Script *_script;
+ InterpreterFlags *_flags;
+ Interpreter *_interpreter;
+ GraphicsMan *_graph;
+ uint8 _currentMidi;
+ byte *_zoomBitmap;
+ byte *_shadowBitmap;
+ byte *_transTable;
+
+ int16 _scaleValue; // scale for hero or special shadow animation
+ int16 _lightX; // for hero shadow
+ int16 _lightY;
+ int32 _shadScaleValue;
+ int32 _shadLineLen;
+ byte *_shadowLine;
+ void setShadowScale(int32 shadowScale);
+ static void plotShadowLinePoint(int x, int y, int color, void *data);
+
+ static const int16 kFPS = 15;
+ static const int32 kIntMax = 2147483647;
+
+ static const int16 kMaxPicWidth = 1280;
+ static const int16 kMaxPicHeight = 480;
+ static const int16 kZoomStep = 4;
+ static const int32 kZoomBitmapLen = kMaxPicHeight / kZoomStep * kMaxPicWidth / kZoomStep;
+ static const int32 kShadowBitmapSize = kMaxPicWidth * kMaxPicHeight / 8;
+ static const int16 kShadowLineArraySize = 2 * 1280 * 4;
+ static const int16 kZoomBitmapWidth = kMaxPicWidth / kZoomStep;
+ static const int16 kZoomBitmapHeight = kMaxPicHeight / kZoomStep;
+ static const int16 kNormalWidth = 640;
+ static const int16 kNormalHeight = 480;
+ static const int32 kTransTableSize = 256 * 256;
+
+ static const int kMaxNormAnims = 64;
+ static const int kMaxBackAnims = 64;
+ static const int kMaxObjects = 64;
+ static const int kMaxMobs = 64;
+
+ Common::Array<DrawNode> _drawNodeList;
+ Common::Array<AnimListItem> _animList;
+ Common::Array<BackgroundAnim> _backAnimList;
+ Common::Array<Anim> _normAnimList;
+ Common::Array<Mob> _mobList;
+ Common::Array<uint32> _mobPriorityList;
+ Common::Array<Mask> _maskList;
+ Common::Array<Object *> _objList;
+ uint16 *_objSlot;
+
+ void freeNormAnim(int slot);
+ void freeAllNormAnims();
+ void removeSingleBackAnim(int slot);
+
+ Common::RandomSource _randomSource;
+
+ void checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z);
+ void insertMasks(Graphics::Surface *originalRoomSurface);
+ void showMask(int maskNr, Graphics::Surface *originalRoomSurface);
+ void clsMasks();
+
+ void grabMap();
+
+ int _selectedMob; // number of selected Mob / inventory item
+ int _selectedItem; // number of item on mouse cursor
+ int _selectedMode;
+ int _currentPointerNumber;
+
+ static const int16 kMaxInv = 90; // max amount of inventory items in whole game
+ static const int16 kMaxItems = 30; // size of inventory
+
+ uint32 _invTxtSize;
+ byte *_invTxt;
+
+ Graphics::Surface *_optionsPic;
+ Graphics::Surface *_optionsPicInInventory;
+
+ bool _optionsFlag;
+ int _optionEnabled;
+ int _optionsMob;
+ int _optionsX;
+ int _optionsY;
+ int _optionsWidth;
+ int _optionsHeight;
+ int _invOptionsWidth;
+ int _invOptionsHeight;
+ int _optionsStep;
+ int _invOptionsStep;
+ int _optionsNumber;
+ int _invOptionsNumber;
+ int _optionsColor1; // color for non-selected options
+ int _optionsColor2; // color for selected option
+
+ bool _showInventoryFlag;
+ int _invExamY;
+ bool _inventoryBackgroundRemember;
+ int _invLineX;
+ int _invLineY;
+ int _invLine; // number of items in one line
+ int _invLines; // number of lines with inventory items
+ int _invLineW;
+ int _invLineH;
+ int _maxInvW;
+ int _maxInvH;
+ int _invLineSkipX;
+ int _invLineSkipY;
+ int _invX1;
+ int _invY1;
+ int _invWidth;
+ int _invHeight;
+ bool _invCurInside;
+ int _mst_shadow;
+ int _mst_shadow2; // blinking after adding new item
+ int _candleCounter; // special counter for candle inventory item
+ int _invMaxCount; // time to turn inventory on
+ int _invCounter; // turning on counter
+
+ void inventoryFlagChange(bool inventoryState);
+ bool loadAllInv();
+ void rememberScreenInv();
+ void prepareInventoryToView();
+ void drawInvItems();
+ void displayInventory();
+ void addInv(int heroId, int item, bool addItemQuiet);
+ void remInv(int heroId, int item);
+ void clearInv(int heroId);
+ void swapInv(int heroId);
+ void addInvObj();
+ void makeInvCursor(int itemNr);
+ void enableOptions(bool checkType);
+ void checkOptions();
+ void checkInvOptions();
+ void openInventoryCheck();
+
+ void leftMouseButton();
+ void rightMouseButton();
+ void inventoryLeftMouseButton();
+ void inventoryRightMouseButton();
+ void dialogLeftMouseButton(byte *string, int dialogSelected);
+
+ uint32 _dialogDatSize;
+ byte *_dialogDat;
+ byte *_dialogData; // on, off flags for lines of dialog text
+
+ byte *_dialogBoxAddr[32]; // adresses of dialog windows
+ byte *_dialogOptAddr[32]; // adresses of dialog options
+ int _dialogOptLines[4 * 32]; // numbers of initial dialog lines
+
+ byte *_dialogText;
+ int _dialogLines;
+ bool _dialogFlag;
+ int _dialogWidth;
+ int _dialogHeight;
+ int _dialogLineSpace;
+ int _dialogColor1; // color for non-selected options
+ int _dialogColor2; // color for selected option
+ Graphics::Surface *_dialogImage;
+
+ void createDialogBox(int dialogBoxNr);
+ void runDialog();
+ void talkHero(int slot);
+ void doTalkAnim(int animNumber, int slot, AnimType animType);
+
+ static const uint8 zoomInStep = 8;
+ void initZoomIn(int slot);
+ void initZoomOut(int slot);
+ void doZoomIn(int slot);
+ void doZoomOut(int slot);
+ void freeZoomObject(int slot);
+
+ static const uint8 kFadeStep = 4;
+ void blackPalette();
+ void setPalette(const byte *palette);
+
+ int getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY);
+
+ // 'Throw a rock' mini-game:
+ static const int16 kCurveLen = 17;
+ static const int kCelStep = 4;
+ int16 *_curveData;
+ int _curvPos;
+ void makeCurve();
+ void getCurve();
+ void mouseWeirdo();
+
+ static const uint16 kPowerBarPosX = 288;
+ static const uint16 kPowerBarPosY = 430;
+ static const uint8 kPowerBarWidth = 64;
+ static const uint8 kPowerBarHeight = 16;
+ static const uint8 kPowerBarBackgroundColor = 0;
+ static const uint16 kPowerBarGreenPosY = 434;
+ static const uint8 kPowerBarGreenColor1 = 202;
+ static const uint8 kPowerBarGreenColor2 = 235;
+ static const uint8 kPowerBarGreenHeight = 8;
+ void showPower();
+
+ // Pathfinding
+ static const int16 kPathGridStep = 2;
+ static const int32 kPathBitmapLen = (kMaxPicHeight / kPathGridStep * kMaxPicWidth / kPathGridStep) / 8;
+ static const int32 kTracePts = 8000;
+ static const int32 kPBW = kMaxPicWidth / 16; // PathBitmapWidth
+ static const int kMinDistance = 2500;
+
+ byte *_roomPathBitmap; // PL - Sala
+ byte *_roomPathBitmapTemp; // PL - SSala
+ byte *_coordsBufEnd;
+ byte *_coordsBuf; // optimal path
+ byte *_coords; // last path point adress from coordsBuf
+ byte *_coordsBuf2;
+ byte *_coords2;
+ byte *_coordsBuf3;
+ byte *_coords3;
+ int _traceLineLen;
+ bool _traceLineFirstPointFlag; // if plotTraceLine after first point
+ bool _tracePointFirstPointFlag; // if plotTracePoint after first point
+ byte *_directionTable;
+ int _shanLen;
+
+ byte *_checkBitmapTemp;
+ byte *_checkBitmap;
+ int _checkMask;
+ int _checkX;
+ int _checkY;
+
+ byte *_rembBitmapTemp;
+ byte *_rembBitmap;
+ int _rembMask;
+ int _rembX;
+ int _rembY;
+
+ int _fpX;
+ int _fpY;
+
+ int drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data);
+ bool loadPath(const char *resourceName);
+ byte *makePath(int heroId, int currX, int currY, int destX, int destY);
+ void findPoint(int x, int y);
+ int getPixelAddr(byte *pathBitmap, int x, int y);
+ static int plotTraceLine(int x, int y, void *data);
+ void specialPlotInside(int x, int y);
+ bool tracePath(int x1, int y1, int x2, int y2);
+ Direction makeDirection(int x1, int y1, int x2, int y2);
+ void specialPlot(int x, int y);
+ void specialPlot2(int x, int y);
+ void allocCoords2();
+ void freeCoords2();
+ void freeCoords3();
+ static int plotTracePoint(int x, int y, void *data);
+ void specialPlotInside2(int x, int y);
+ void approxPath();
+ void freeDirectionTable();
+ void scanDirections();
+ int scanDirectionsFindNext(byte *coords, int xDiff, int yDiff);
+ void moveShandria();
+ void walkTo();
+ void moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag);
+
+ int leftDownDir();
+ int leftDir();
+ int leftUpDir();
+ int rightDownDir();
+ int rightDir();
+ int rightUpDir();
+ int upLeftDir();
+ int upDir();
+ int upRightDir();
+ int downLeftDir();
+ int downDir();
+ int downRightDir();
+
+ int cpe();
+ int checkLeftDownDir();
+ int checkLeftDir();
+ int checkDownDir();
+ int checkUpDir();
+ int checkRightDir();
+ int checkLeftUpDir();
+ int checkRightDownDir();
+ int checkRightUpDir();
+
+private:
+ bool playNextFLCFrame();
+ void keyHandler(Common::Event event);
+ int checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList);
+ void drawScreen();
+ void showTexts(Graphics::Surface *screen);
+ void init();
+ void showLogo();
+ void showAnim(Anim &anim);
+ void showNormAnims();
+ void setBackAnim(Anim &backAnim);
+ void showBackAnims();
+ void clearBackAnimList();
+ bool spriteCheck(int sprWidth, int sprHeight, int destX, int destY);
+ void showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ);
+ void showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ);
+ void showObjects();
+ void showParallax();
+ static bool compareDrawNodes(DrawNode d1, DrawNode d2);
+ void runDrawNodes();
+ void makeShadowTable(int brightness);
+ void pause();
+ void pause2();
+
+ uint32 getTextWidth(const char *s);
+ void debugEngine(const char *s, ...);
+
+ uint8 _cursorNr;
+
+ Common::RandomSource *_rnd;
+ Cursor *_cursor1;
+ Graphics::Surface *_cursor2;
+ Cursor *_cursor3;
+ Debugger *_debugger;
+ Font *_font;
+ MusicPlayer *_midiPlayer;
+
+ static const uint32 kMaxSamples = 60;
+ Audio::RewindableAudioStream *_audioStream[kMaxSamples];
+ Audio::SoundHandle _soundHandle[kMaxSamples];
+
+ Common::Array<PScr *> _pscrList;
+ Common::Array<InvItem> _allInvList;
+ Common::Array<Mob> _invMobList;
+
+ bool _flicLooped;
+
+ void mainLoop();
+
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/pscr.cpp b/engines/prince/pscr.cpp
new file mode 100644
index 0000000000..d9d36a3356
--- /dev/null
+++ b/engines/prince/pscr.cpp
@@ -0,0 +1,75 @@
+/* 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/archive.h"
+#include "common/stream.h"
+
+#include "prince/pscr.h"
+
+namespace Prince {
+
+PScr::PScr() : _x(0), _y(0), _step(0), _surface(nullptr)
+{
+}
+
+PScr::~PScr() {
+ if (_surface != nullptr) {
+ _surface->free();
+ delete _surface;
+ _surface = nullptr;
+ }
+}
+
+void PScr::loadSurface(Common::SeekableReadStream &stream) {
+ stream.skip(4);
+ int width = stream.readUint16LE();
+ int height = stream.readUint16LE();
+ _surface = new Graphics::Surface();
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (int h = 0; h < _surface->h; h++) {
+ stream.read(_surface->getBasePtr(0, h), _surface->w);
+ }
+}
+
+bool PScr::loadFromStream(Common::SeekableReadStream &stream) {
+ int32 pos = stream.pos();
+ uint16 file = stream.readUint16LE();
+ if (file == 0xFFFF) {
+ return false;
+ }
+ _x = stream.readUint16LE();
+ _y = stream.readUint16LE();
+ _step = stream.readUint16LE();
+
+ const Common::String pscrStreamName = Common::String::format("PS%02d", file);
+ Common::SeekableReadStream *pscrStream = SearchMan.createReadStreamForMember(pscrStreamName);
+ if (pscrStream != nullptr) {
+ loadSurface(*pscrStream);
+ }
+ delete pscrStream;
+ stream.seek(pos + 12); // size of PScrList struct
+
+ return true;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/pscr.h b/engines/prince/pscr.h
new file mode 100644
index 0000000000..d59fa37d81
--- /dev/null
+++ b/engines/prince/pscr.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_PSCR_H
+#define PRINCE_PSCR_H
+
+#include "graphics/surface.h"
+
+namespace Prince {
+
+class PScr {
+public:
+ PScr();
+ ~PScr();
+ int16 _x;
+ int16 _y;
+ int16 _step;
+ static const int16 kPScrZ = 1000;
+
+ bool loadFromStream(Common::SeekableReadStream &stream);
+ Graphics::Surface *getSurface() const { return _surface; }
+private:
+ void loadSurface(Common::SeekableReadStream &stream);
+ Graphics::Surface *_surface;
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/resource.h b/engines/prince/resource.h
new file mode 100644
index 0000000000..b1fbd9c4f0
--- /dev/null
+++ b/engines/prince/resource.h
@@ -0,0 +1,100 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_RESOURCE_H
+#define PRINCE_RESOURCE_H
+
+#include "common/stream.h"
+#include "common/archive.h"
+#include "common/debug-channels.h"
+#include "common/ptr.h"
+
+namespace Prince {
+
+namespace Resource {
+
+ template <typename T>
+ bool loadFromStream(T &resource, Common::SeekableReadStream &stream) {
+ return resource.loadStream(stream);
+ }
+
+ template<typename T>
+ bool loadResource(T *resource, const char *resourceName, bool required) {
+ Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName));
+ if (!stream) {
+ if (required)
+ error("Can't load %s", resourceName);
+ return false;
+ }
+
+ return loadFromStream(*resource, *stream);
+ }
+
+ template <typename T>
+ bool loadResource(Common::Array<T> &array, Common::SeekableReadStream &stream, bool required = true) {
+ T t;
+ while (t.loadFromStream(stream))
+ array.push_back(t);
+
+ return true;
+ }
+
+
+ template <typename T>
+ bool loadResource(Common::Array<T> &array, const char *resourceName, bool required = true) {
+ Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName));
+ if (!stream) {
+ if (required)
+ error("Can't load %s", resourceName);
+ return false;
+ }
+
+ return loadResource(array, *stream, required);
+ }
+
+ template <typename T>
+ bool loadResource(Common::Array<T *> &array, const char *resourceName, bool required = true) {
+
+ Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName));
+ if (!stream) {
+ if (required)
+ error("Can't load %s", resourceName);
+ return false;
+ }
+
+ // FIXME: This is stupid. Maybe loadFromStream should be helper method that returns initiailzed object
+ while (true) {
+ T* t = new T();
+ if (!t->loadFromStream(*stream)) {
+ delete t;
+ break;
+ }
+ array.push_back(t);
+ }
+ return true;
+ }
+
+}
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp
new file mode 100644
index 0000000000..39a4002b40
--- /dev/null
+++ b/engines/prince/saveload.cpp
@@ -0,0 +1,514 @@
+/* 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 "prince/prince.h"
+#include "prince/graphics.h"
+#include "prince/detection.h"
+#include "prince/flags.h"
+#include "prince/script.h"
+#include "prince/hero.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+#include "common/memstream.h"
+
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "graphics/scaler.h"
+
+namespace Prince {
+
+#define kBadSVG 99
+#define kSavegameVersion 1
+#define kSavegameStrSize 14
+#define kSavegameStr "SCUMMVM_PRINCE"
+
+class InterpreterFlags;
+class Interpreter;
+
+struct SavegameHeader {
+ uint8 version;
+ Common::String saveName;
+ Graphics::Surface *thumbnail;
+ int saveYear, saveMonth, saveDay;
+ int saveHour, saveMinutes;
+};
+
+int PrinceMetaEngine::getMaximumSaveSlot() const {
+ return 99;
+}
+
+SaveStateList PrinceMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".???";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); filename++) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNum = atoi(filename->c_str() + filename->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= 99) {
+
+ Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+ if (file) {
+ Prince::SavegameHeader header;
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ file->read(buffer, kSavegameStrSize + 1);
+
+ if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) {
+ // Valid savegame
+ if (Prince::PrinceEngine::readSavegameHeader(file, header)) {
+ saveList.push_back(SaveStateDescriptor(slotNum, header.saveName));
+ if (header.thumbnail) {
+ header.thumbnail->free();
+ delete header.thumbnail;
+ }
+ }
+ } else {
+ // Must be an original format savegame
+ saveList.push_back(SaveStateDescriptor(slotNum, "Unknown"));
+ }
+
+ delete file;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (f) {
+ Prince::SavegameHeader header;
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ f->read(buffer, kSavegameStrSize + 1);
+
+ bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) &&
+ Prince::PrinceEngine::readSavegameHeader(f, header);
+ delete f;
+
+ if (!hasHeader) {
+ // Original savegame perhaps?
+ SaveStateDescriptor desc(slot, "Unknown");
+ return desc;
+ } else {
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, header.saveName);
+ desc.setThumbnail(header.thumbnail);
+ desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay);
+ desc.setSaveTime(header.saveHour, header.saveMinutes);
+
+ return desc;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) {
+ header.thumbnail = nullptr;
+
+ // Get the savegame version
+ header.version = in->readByte();
+ if (header.version > kSavegameVersion)
+ return false;
+
+ // Read in the string
+ header.saveName.clear();
+ char ch;
+ while ((ch = (char)in->readByte()) != '\0')
+ header.saveName += ch;
+
+ // Get the thumbnail
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ if (!header.thumbnail)
+ return false;
+
+ // Read in save date/time
+ header.saveYear = in->readSint16LE();
+ header.saveMonth = in->readSint16LE();
+ header.saveDay = in->readSint16LE();
+ header.saveHour = in->readSint16LE();
+ header.saveMinutes = in->readSint16LE();
+
+ return true;
+}
+
+void PrinceMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+// TODO
+bool PrinceEngine::canSaveGameStateCurrently() {
+ return true;
+}
+
+// TODO
+bool PrinceEngine::canLoadGameStateCurrently() {
+ return true;
+}
+
+Common::Error PrinceEngine::saveGameState(int slot, const Common::String &desc) {
+ // Set up the serializer
+ Common::String slotName = generateSaveName(slot);
+ Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
+
+ // Write out the ScummVM savegame header
+ SavegameHeader header;
+ header.saveName = desc;
+ header.version = kSavegameVersion;
+ writeSavegameHeader(saveFile, header);
+
+ // Write out the data of the savegame
+ syncGame(nullptr, saveFile);
+
+ // Finish writing out game data
+ saveFile->finalize();
+ delete saveFile;
+
+ return Common::kNoError;
+}
+
+Common::String PrinceEngine::generateSaveName(int slot) {
+ return Common::String::format("%s.%03d", _targetName.c_str(), slot);
+}
+
+void PrinceEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
+ // Write out a savegame header
+ out->write(kSavegameStr, kSavegameStrSize + 1);
+
+ out->writeByte(kSavegameVersion);
+
+ // Write savegame name
+ out->write(header.saveName.c_str(), header.saveName.size() + 1);
+
+ // Get the active palette
+ uint8 thumbPalette[256 * 3];
+ _system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
+
+ // Create a thumbnail and save it
+ Graphics::Surface *thumb = new Graphics::Surface();
+ Graphics::Surface *s = _graph->_frontScreen; // check inventory / map etc..
+ ::createThumbnail(thumb, (const byte *)s->getPixels(), s->w, s->h, thumbPalette);
+ Graphics::saveThumbnail(*out, *thumb);
+ thumb->free();
+ delete thumb;
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ out->writeSint16LE(td.tm_year + 1900);
+ out->writeSint16LE(td.tm_mon + 1);
+ out->writeSint16LE(td.tm_mday);
+ out->writeSint16LE(td.tm_hour);
+ out->writeSint16LE(td.tm_min);
+}
+
+void PrinceEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) {
+ int emptyRoom = 0x00;
+ int normRoom = 0xFF;
+ byte endInv = 0xFF;
+
+ Common::Serializer s(readStream, writeStream);
+
+ if (s.isSaving()) {
+ // Flag values
+ for (int i = 0; i < _flags->kMaxFlags; i++) {
+ uint32 value = _flags->getFlagValue((Flags::Id)(_flags->kFlagMask + i));
+ s.syncAsUint32LE(value);
+ }
+
+ // Dialog data
+ for (uint32 i = 0; i < _dialogDatSize; i++) {
+ byte value = _dialogDat[i];
+ s.syncAsByte(value);
+ }
+
+ // Location number
+ s.syncAsUint16LE(_locationNr);
+
+ // Rooms
+ for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) {
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+
+ if (room->_mobs) {
+ s.syncAsByte(normRoom);
+ } else {
+ s.syncAsByte(emptyRoom);
+ delete room;
+ continue;
+ }
+
+ // Mobs
+ for (int mobId = 0; mobId < kMaxMobs; mobId++) {
+ byte value = _script->getMobVisible(room->_mobs, mobId);
+ s.syncAsByte(value);
+ }
+
+ // Background animations
+ for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) {
+ uint32 value = _script->getBackAnimId(room->_backAnim, backAnimSlot);
+ s.syncAsUint32LE(value);
+ }
+
+ // Objects
+ for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) {
+ byte value = _script->getObjId(room->_obj, objectSlot);
+ s.syncAsByte(value);
+ }
+
+ delete room;
+ }
+
+ // Main hero
+ s.syncAsUint16LE(_mainHero->_visible);
+ s.syncAsUint16LE(_mainHero->_middleX);
+ s.syncAsUint16LE(_mainHero->_middleY);
+ s.syncAsUint16LE(_mainHero->_lastDirection);
+ s.syncAsUint32LE(_mainHero->_color);
+ s.syncAsUint16LE(_mainHero->_maxBoredom);
+ s.syncAsUint32LE(_mainHero->_animSetNr);
+
+ for (uint inv1Slot = 0; inv1Slot < _mainHero->_inventory.size(); inv1Slot++) {
+ s.syncAsByte(_mainHero->_inventory[inv1Slot]);
+ }
+ s.syncAsByte(endInv);
+
+ for (uint inv2Slot = 0; inv2Slot < _mainHero->_inventory2.size(); inv2Slot++) {
+ s.syncAsByte(_mainHero->_inventory2[inv2Slot]);
+ }
+ s.syncAsByte(endInv);
+
+ // Second hero
+ s.syncAsUint16LE(_secondHero->_visible);
+ s.syncAsUint16LE(_secondHero->_middleX);
+ s.syncAsUint16LE(_secondHero->_middleY);
+ s.syncAsUint16LE(_secondHero->_lastDirection);
+ s.syncAsUint32LE(_secondHero->_color);
+ s.syncAsUint16LE(_secondHero->_maxBoredom);
+ s.syncAsUint32LE(_secondHero->_animSetNr);
+
+ for (uint inv1Slot = 0; inv1Slot < _secondHero->_inventory.size(); inv1Slot++) {
+ s.syncAsByte(_secondHero->_inventory[inv1Slot]);
+ }
+ s.syncAsByte(endInv);
+
+ for (uint inv2Slot = 0; inv2Slot < _secondHero->_inventory2.size(); inv2Slot++) {
+ s.syncAsByte(_secondHero->_inventory2[inv2Slot]);
+ }
+ s.syncAsByte(endInv);
+
+ } else {
+ // Cursor reset
+ changeCursor(1);
+ _currentPointerNumber = 1;
+
+ // Flag values
+ for (int i = 0; i < _flags->kMaxFlags; i++) {
+ uint32 value = 0;
+ s.syncAsUint32LE(value);
+ _flags->setFlagValue((Flags::Id)(_flags->kFlagMask + i), value);
+ }
+
+ // Dialog data
+ for (uint32 i = 0; i < _dialogDatSize; i++) {
+ byte value = 0;
+ s.syncAsByte(value);
+ _dialogDat[i] = value;
+ }
+
+ // Location number
+ int restoreRoom = 0;
+ s.syncAsUint16LE(restoreRoom);
+ _flags->setFlagValue(Flags::RESTOREROOM, restoreRoom);
+
+ // Rooms
+ for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) {
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+
+ byte roomType = emptyRoom;
+ s.syncAsByte(roomType);
+ if (roomType == emptyRoom) {
+ delete room;
+ continue;
+ }
+
+ // Mobs
+ for (int mobId = 0; mobId < kMaxMobs; mobId++) {
+ byte value = 0;
+ s.syncAsByte(value);
+ _script->setMobVisible(room->_mobs, mobId, value);
+ }
+
+ // Background animations
+ for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) {
+ uint32 value = 0;
+ s.syncAsUint32LE(value);
+ _script->setBackAnimId(room->_backAnim, backAnimSlot, value);
+ }
+
+ // Objects
+ for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) {
+ byte value = 0;
+ s.syncAsByte(value);
+ _script->setObjId(room->_obj, objectSlot, value);
+ }
+
+ delete room;
+ }
+
+ // Main hero
+ s.syncAsUint16LE(_mainHero->_visible);
+ s.syncAsUint16LE(_mainHero->_middleX);
+ s.syncAsUint16LE(_mainHero->_middleY);
+ s.syncAsUint16LE(_mainHero->_lastDirection);
+ s.syncAsUint32LE(_mainHero->_color);
+ s.syncAsUint16LE(_mainHero->_maxBoredom);
+ s.syncAsUint32LE(_mainHero->_animSetNr);
+ _mainHero->loadAnimSet(_mainHero->_animSetNr);
+
+ _mainHero->_inventory.clear();
+ byte invId = endInv;
+ while (1) {
+ s.syncAsByte(invId);
+ if (invId == endInv) {
+ break;
+ }
+ _mainHero->_inventory.push_back(invId);
+ }
+
+ _mainHero->_inventory2.clear();
+ invId = endInv;
+ while (1) {
+ s.syncAsByte(invId);
+ if (invId == endInv) {
+ break;
+ }
+ _mainHero->_inventory2.push_back(invId);
+ }
+
+ // Second hero
+ s.syncAsUint16LE(_secondHero->_visible);
+ s.syncAsUint16LE(_secondHero->_middleX);
+ s.syncAsUint16LE(_secondHero->_middleY);
+ s.syncAsUint16LE(_secondHero->_lastDirection);
+ s.syncAsUint32LE(_secondHero->_color);
+ s.syncAsUint16LE(_secondHero->_maxBoredom);
+ s.syncAsUint32LE(_secondHero->_animSetNr);
+ _secondHero->loadAnimSet(_secondHero->_animSetNr);
+
+ _secondHero->_inventory.clear();
+ invId = endInv;
+ while (1) {
+ s.syncAsByte(invId);
+ if (invId == endInv) {
+ break;
+ }
+ _secondHero->_inventory.push_back(invId);
+ }
+
+ _secondHero->_inventory2.clear();
+ invId = endInv;
+ while (1) {
+ s.syncAsByte(invId);
+ if (invId == endInv) {
+ break;
+ }
+ _secondHero->_inventory2.push_back(invId);
+ }
+
+ // Script
+ _interpreter->setBgOpcodePC(0);
+ _interpreter->setFgOpcodePC(_script->_scriptInfo.restoreGame);
+
+ }
+}
+
+Common::Error PrinceEngine::loadGameState(int slot) {
+ if (!loadGame(slot)) {
+ return Common::kReadingFailed;
+ }
+ return Common::kNoError;
+}
+
+bool PrinceEngine::loadGame(int slotNumber) {
+ Common::MemoryReadStream *readStream;
+
+ // Open up the savegame file
+ Common::String slotName = generateSaveName(slotNumber);
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
+
+ // Read the data into a data buffer
+ int size = saveFile->size();
+ byte *dataBuffer = (byte *)malloc(size);
+ saveFile->read(dataBuffer, size);
+ readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
+ delete saveFile;
+
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[kSavegameStrSize + 1];
+ readStream->read(buffer, kSavegameStrSize + 1);
+
+ if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) {
+ delete readStream;
+ return false;
+ } else {
+ SavegameHeader saveHeader;
+
+ if (!readSavegameHeader(readStream, saveHeader)) {
+ delete readStream;
+ return false;
+ }
+
+ // Delete the thumbnail
+ saveHeader.thumbnail->free();
+ delete saveHeader.thumbnail;
+ }
+
+ // Get in the savegame
+ syncGame(readStream, nullptr);
+ delete readStream;
+
+ // TODO
+ //syncSpeechSettings();
+
+ return true;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/script.cpp b/engines/prince/script.cpp
new file mode 100644
index 0000000000..75d084f639
--- /dev/null
+++ b/engines/prince/script.cpp
@@ -0,0 +1,2071 @@
+/* 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 "prince/script.h"
+#include "prince/prince.h"
+#include "prince/flags.h"
+#include "prince/variatxt.h"
+#include "prince/font.h"
+#include "prince/hero.h"
+#include "prince/resource.h"
+#include "prince/animation.h"
+
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/archive.h"
+#include "common/memstream.h"
+
+namespace Prince {
+
+static const uint16 kNumOpcodes = 144;
+
+Room::Room() {}
+
+bool Room::loadRoom(byte *roomData) {
+ int roomSize = 64;
+ Common::MemoryReadStream roomStream(roomData, roomSize);
+
+ _mobs = roomStream.readSint32LE();
+ _backAnim = roomStream.readSint32LE();
+ _obj = roomStream.readSint32LE();
+ _nak = roomStream.readSint32LE();
+ _itemUse = roomStream.readSint32LE();
+ _itemGive = roomStream.readSint32LE();
+ _walkTo = roomStream.readSint32LE();
+ _examine = roomStream.readSint32LE();
+ _pickup = roomStream.readSint32LE();
+ _use = roomStream.readSint32LE();
+ _pushOpen = roomStream.readSint32LE();
+ _pullClose = roomStream.readSint32LE();
+ _talk = roomStream.readSint32LE();
+ _give = roomStream.readSint32LE();
+
+ return true;
+}
+
+int Room::getOptionOffset(int option) {
+ switch (option) {
+ case 0:
+ return _walkTo;
+ case 1:
+ return _examine;
+ case 2:
+ return _pickup;
+ case 3:
+ return _use;
+ case 4:
+ return _pushOpen;
+ case 5:
+ return _pullClose;
+ case 6:
+ return _talk;
+ case 7:
+ return _give;
+ default:
+ error("Wrong option - nr %d", option);
+ }
+}
+
+Script::Script(PrinceEngine *vm) : _vm(vm), _data(nullptr), _dataSize(0) {
+}
+
+Script::~Script() {
+ if (_data != nullptr) {
+ free(_data);
+ _dataSize = 0;
+ _data = nullptr;
+ }
+}
+
+bool Script::loadStream(Common::SeekableReadStream &stream) {
+ _dataSize = stream.size();
+ if (!_dataSize) {
+ return false;
+ }
+
+ _data = (byte *)malloc(_dataSize);
+
+ if (!_data) {
+ return false;
+ }
+
+ stream.read(_data, _dataSize);
+
+ Common::MemoryReadStream scriptDataStream(_data, _dataSize);
+ _scriptInfo.rooms = scriptDataStream.readSint32LE();
+ _scriptInfo.startGame = scriptDataStream.readSint32LE();
+ _scriptInfo.restoreGame = scriptDataStream.readSint32LE();
+ _scriptInfo.stdExamine = scriptDataStream.readSint32LE();
+ _scriptInfo.stdPickup = scriptDataStream.readSint32LE();
+ _scriptInfo.stdUse = scriptDataStream.readSint32LE();
+ _scriptInfo.stdOpen = scriptDataStream.readSint32LE();
+ _scriptInfo.stdClose = scriptDataStream.readSint32LE();
+ _scriptInfo.stdTalk = scriptDataStream.readSint32LE();
+ _scriptInfo.stdGive = scriptDataStream.readSint32LE();
+ _scriptInfo.usdCode = scriptDataStream.readSint32LE();
+ _scriptInfo.invObjExam = scriptDataStream.readSint32LE();
+ _scriptInfo.invObjUse = scriptDataStream.readSint32LE();
+ _scriptInfo.invObjUU = scriptDataStream.readSint32LE();
+ _scriptInfo.stdUseItem = scriptDataStream.readSint32LE();
+ _scriptInfo.lightSources = scriptDataStream.readSint32LE();
+ _scriptInfo.specRout = scriptDataStream.readSint32LE();
+ _scriptInfo.invObjGive = scriptDataStream.readSint32LE();
+ _scriptInfo.stdGiveItem = scriptDataStream.readSint32LE();
+ _scriptInfo.goTester = scriptDataStream.readSint32LE();
+
+ return true;
+}
+
+uint16 Script::readScript16(uint32 address) {
+ assert((_data + address + sizeof(uint16)) <= (_data + _dataSize));
+ uint16 data = READ_LE_UINT16(&_data[address]);
+ return data;
+}
+
+uint32 Script::readScript32(uint32 address) {
+ assert((_data + address + sizeof(uint32)) <= (_data + _dataSize));
+ uint32 data = READ_LE_UINT32(&_data[address]);
+ return data;
+}
+
+int16 Script::getLightX(int locationNr) {
+ return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8]);
+}
+
+int16 Script::getLightY(int locationNr) {
+ return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 2]);
+}
+
+int32 Script::getShadowScale(int locationNr) {
+ return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 4]);
+}
+
+uint32 Script::getStartGameOffset() {
+ return _scriptInfo.startGame;
+}
+
+uint32 Script::getLocationInitScript(int initRoomTableOffset, int roomNr) {
+ return (uint32)READ_LE_UINT32(&_data[initRoomTableOffset + roomNr * 4]);
+}
+
+byte Script::getMobVisible(int roomMobOffset, uint16 mob) {
+ return _data[roomMobOffset + mob];
+}
+
+void Script::setMobVisible(int roomMobOffset, uint16 mob, byte value) {
+ _data[roomMobOffset + mob] = value;
+}
+
+uint8 *Script::getRoomOffset(int locationNr) {
+ return &_data[_scriptInfo.rooms + locationNr * 64];
+}
+
+int32 Script::getOptionStandardOffset(int option) {
+ switch (option) {
+ case 1:
+ return _scriptInfo.stdExamine;
+ case 2:
+ return _scriptInfo.stdPickup;
+ case 3:
+ return _scriptInfo.stdUse;
+ case 4:
+ return _scriptInfo.stdOpen;
+ case 5:
+ return _scriptInfo.stdClose;
+ case 6:
+ return _scriptInfo.stdTalk;
+ case 7:
+ return _scriptInfo.stdGive;
+ default:
+ error("Wrong standard option - nr %d", option);
+ }
+}
+
+uint8 *Script::getHeroAnimName(int offset) {
+ return &_data[offset];
+}
+
+uint32 Script::getBackAnimId(int roomBackAnimOffset, int slot) {
+ uint32 animId = READ_LE_UINT32(&_data[roomBackAnimOffset + slot * 4]);
+ return animId;
+}
+
+void Script::setBackAnimId(int roomBackAnimOffset, int slot, int animId) {
+ WRITE_LE_UINT32(&_data[roomBackAnimOffset + slot * 4], animId);
+}
+
+byte Script::getObjId(int roomObjOffset, int slot) {
+ return _data[roomObjOffset + slot];
+}
+
+void Script::setObjId(int roomObjOffset, int slot, byte objectId) {
+ _data[roomObjOffset + slot] = objectId;
+}
+
+int Script::scanMobEvents(int mobMask, int dataEventOffset) {
+ debug("mobMask: %d", mobMask);
+ int i = 0;
+ int16 mob;
+ int32 code;
+ do {
+ mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 6]);
+ if (mob == mobMask) {
+ code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 6 + 2]);
+ debug("code: %d", code);
+ return code;
+ }
+ i++;
+ } while (mob != -1);
+ return -1;
+}
+
+int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask) {
+ debug("mobMask: %d", mobMask);
+ int i = 0;
+ int16 mob;
+ int16 item;
+ int32 code;
+ do {
+ mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8]);
+ if (mob == mobMask) {
+ item = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8 + 2]);
+ if (item == itemMask) {
+ code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 8 + 4]);
+ debug("itemMask: %d", item);
+ debug("code: %d", code);
+ return code;
+ }
+ }
+ i++;
+ } while (mob != -1);
+ return -1;
+}
+
+void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset) {
+
+ _vm->removeSingleBackAnim(slot); // free slot before loading
+
+ int offset = roomBackAnimOffset + slot * 4; // BackgroundAnim offset for selected slot number
+
+ BackgroundAnim newBackgroundAnim; // BackgroundAnim seq data and its array of Anim
+
+ int animOffset = READ_LE_UINT32(&_data[offset]); // pos of BackgroundAnim data in script
+ int anims = READ_LE_UINT32(&_data[animOffset + 8]); // amount of Anim in BackgroundAnim
+
+ if (anims == 0) {
+ anims = 1; // anims with 0 as amount in game data has just 1 animation
+ }
+
+ if (animOffset != 0) {
+ Common::MemoryReadStream stream(_data, _dataSize); // stream from script data
+ for (int i = 0; i < anims; i++) {
+ Anim newAnim;
+ stream.seek(animOffset + kStructSizeBAS + kStructSizeBASA * i);
+ // Anim BASA data
+ newAnim._basaData._num = stream.readUint16LE();
+ newAnim._basaData._start = stream.readUint16LE();
+ newAnim._basaData._end = stream.readUint16LE();
+
+ // Anim number in game files
+ int animNumber = newAnim._basaData._num;
+ const Common::String animName = Common::String::format("AN%02d", animNumber);
+ const Common::String shadowName = Common::String::format("AN%02dS", animNumber);
+ newAnim._animData = new Animation();
+ newAnim._shadowData = new Animation();
+ Resource::loadResource(newAnim._animData, animName.c_str(), true);
+ if (!Resource::loadResource(newAnim._shadowData, shadowName.c_str(), false)) {
+ delete newAnim._shadowData;
+ newAnim._shadowData = nullptr;
+ }
+
+ newAnim._usage = 0;
+ newAnim._state = 0; // enabled
+ if ((_vm->_animList[animNumber]._flags & 4)) {
+ newAnim._state = 1;
+ newAnim._frame = _vm->_animList[animNumber]._endPhase;
+ newAnim._showFrame = _vm->_animList[animNumber]._endPhase;
+ } else {
+ newAnim._frame = _vm->_animList[animNumber]._startPhase;
+ newAnim._showFrame = _vm->_animList[animNumber]._startPhase;
+ }
+ newAnim._flags = _vm->_animList[animNumber]._flags;
+ newAnim._lastFrame = _vm->_animList[animNumber]._endPhase;
+ newAnim._loopFrame = _vm->_animList[animNumber]._loopPhase;
+ newAnim._loopType = _vm->_animList[animNumber]._loopType;
+ newAnim._nextAnim = _vm->_animList[animNumber]._nextAnim;
+ newAnim._x = _vm->_animList[animNumber]._x;
+ newAnim._y = _vm->_animList[animNumber]._y;
+ newAnim._currFrame = 0;
+ newAnim._currX = _vm->_animList[animNumber]._x;
+ newAnim._currY = _vm->_animList[animNumber]._y;
+ newAnim._currW = 0;
+ newAnim._currH = 0;
+ newAnim._packFlag = 0;
+ newAnim._shadowBack = _vm->_animList[animNumber]._type;
+ newBackgroundAnim.backAnims.push_back(newAnim);
+ }
+
+ // Anim BAS data
+ stream.seek(animOffset);
+ newBackgroundAnim._seq._type = stream.readUint32LE();
+ newBackgroundAnim._seq._data = stream.readUint32LE();
+ newBackgroundAnim._seq._anims = stream.readUint32LE();
+ stream.skip(12);
+ newBackgroundAnim._seq._current = newBackgroundAnim.backAnims[0]._basaData._num;
+ newBackgroundAnim._seq._counter = 0;
+ newBackgroundAnim._seq._currRelative = 0;
+ newBackgroundAnim._seq._data2 = stream.readUint32LE();
+
+ int start = newBackgroundAnim.backAnims[0]._basaData._start; // BASA_Start of first frame
+ int end = newBackgroundAnim.backAnims[0]._basaData._end; // BASA_End of first frame
+
+ if (start != -1) {
+ newBackgroundAnim.backAnims[0]._frame = start;
+ newBackgroundAnim.backAnims[0]._showFrame = start;
+ newBackgroundAnim.backAnims[0]._loopFrame = start;
+ }
+
+ if (end != -1) {
+ newBackgroundAnim.backAnims[0]._lastFrame = end;
+ }
+
+ backAnimList[slot] = newBackgroundAnim;
+ }
+}
+
+void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset) {
+ for (int i = 0; i < _vm->kMaxBackAnims; i++) {
+ installSingleBackAnim(backAnimList, i, roomBackAnimOffset);
+ }
+}
+
+void Script::installObjects(int offset) {
+ for (int i = 0; i < _vm->kMaxObjects; i++) {
+ _vm->_objSlot[i] = _data[offset];
+ offset++;
+ }
+}
+
+bool Script::loadAllMasks(Common::Array<Mask> &maskList, int offset) {
+ Mask tempMask;
+ while (1) {
+ Common::MemoryReadStream maskStream(_data, _dataSize);
+ maskStream.seek(offset);
+ tempMask._state = maskStream.readUint16LE();
+ if (tempMask._state == -1) {
+ break;
+ }
+ tempMask._flags = maskStream.readUint16LE();
+ tempMask._x1 = maskStream.readUint16LE();
+ tempMask._y1 = maskStream.readUint16LE();
+ tempMask._x2 = maskStream.readUint16LE();
+ tempMask._y2 = maskStream.readUint16LE();
+ tempMask._z = maskStream.readUint16LE();
+ tempMask._number = maskStream.readUint16LE();
+
+ const Common::String msStreamName = Common::String::format("MS%02d", tempMask._number);
+ Common::SeekableReadStream *msStream = SearchMan.createReadStreamForMember(msStreamName);
+ if (!msStream) {
+ tempMask._width = 0;
+ tempMask._height = 0;
+ tempMask._data = nullptr;
+ debug("Can't load %s", msStreamName.c_str());
+ delete msStream;
+ } else {
+ uint32 dataSize = msStream->size();
+ if (dataSize != -1) {
+ tempMask._data = (byte *)malloc(dataSize);
+ if (msStream->read(tempMask._data, dataSize) != dataSize) {
+ free(tempMask._data);
+ delete msStream;
+ return false;
+ }
+ delete msStream;
+ }
+ tempMask._width = tempMask.getWidth();
+ tempMask._height = tempMask.getHeight();
+ }
+
+ maskList.push_back(tempMask);
+ offset += 16; // size of Mask (Nak) struct
+ }
+ return true;
+}
+
+InterpreterFlags::InterpreterFlags() {
+ resetAllFlags();
+}
+
+void InterpreterFlags::resetAllFlags() {
+ memset(_flags, 0, sizeof(_flags));
+}
+
+void InterpreterFlags::setFlagValue(Flags::Id flagId, int32 value) {
+ _flags[(uint32)flagId - kFlagMask] = value;
+}
+
+int32 InterpreterFlags::getFlagValue(Flags::Id flagId) {
+ return _flags[(uint32)flagId - kFlagMask];
+}
+
+Interpreter::Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags) :
+ _vm(vm), _script(script), _flags(flags),
+ _stacktop(0), _opcodeNF(false), _opcodeEnd(false),
+ _waitFlag(0), _result(true) {
+
+ // Initialize the script
+ _mode = "fg";
+ _fgOpcodePC = _script->getStartGameOffset();
+ _bgOpcodePC = 0;
+}
+
+void Interpreter::debugInterpreter(const char *s, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+ va_start(va, s);
+ vsnprintf(buf, STRINGBUFLEN, s, va);
+ va_end(va);
+
+ Common::String str = Common::String::format("@0x%08X: ", _lastInstruction);
+ str += Common::String::format("op %04d: ", _lastOpcode);
+ //debugC(10, DebugChannel::kScript, "PrinceEngine::Script %s %s", str.c_str(), buf);
+ if (!strcmp(_mode, "fg")) {
+ debug(10, "PrinceEngine::Script %s %s", str.c_str(), buf);
+ }
+ //debug("Prince::Script mode %s %s %s", _mode, str.c_str(), buf);
+}
+
+void Interpreter::stepBg() {
+ if (_bgOpcodePC) {
+ _mode = "bg";
+ _bgOpcodePC = step(_bgOpcodePC);
+ }
+}
+
+void Interpreter::stepFg() {
+ if (_fgOpcodePC) {
+ _mode = "fg";
+ _fgOpcodePC = step(_fgOpcodePC);
+ }
+}
+
+uint32 Interpreter::step(uint32 opcodePC) {
+ _currentInstruction = opcodePC;
+
+ while (!_opcodeNF) {
+ _lastInstruction = _currentInstruction;
+
+ // Get the current opcode
+ _lastOpcode = readScript16();
+
+ if (_lastOpcode > kNumOpcodes)
+ error(
+ "Trying to execute unknown opcode @0x%04X: %02d",
+ _currentInstruction,
+ _lastOpcode);
+
+ // Execute the current opcode
+ OpcodeFunc op = _opcodes[_lastOpcode];
+ (this->*op)();
+ if (_opcodeNF) {
+ _opcodeNF = 0;
+ break;
+ }
+ }
+
+ if (_opcodeEnd) {
+ _vm->quitGame();
+ }
+
+ return _currentInstruction;
+}
+
+void Interpreter::storeNewPC(int opcodePC) {
+ if (_flags->getFlagValue(Flags::GETACTION) == 1) {
+ _flags->setFlagValue(Flags::GETACTIONDATA, opcodePC);
+ opcodePC = _flags->getFlagValue(Flags::GETACTIONBACK);
+ }
+ _fgOpcodePC = opcodePC;
+}
+
+int Interpreter::getLastOPCode() {
+ return _lastOpcode;
+}
+
+int Interpreter::getFgOpcodePC() {
+ return _fgOpcodePC;
+}
+
+uint32 Interpreter::getCurrentString() {
+ return _currentString;
+}
+
+void Interpreter::setCurrentString(uint32 value) {
+ _currentString = value;
+}
+
+byte *Interpreter::getString() {
+ return _string;
+}
+
+void Interpreter::setString(byte *newString) {
+ _string = newString;
+}
+
+void Interpreter::increaseString() {
+ while (*_string) {
+ _string++;
+ }
+ _string++;
+}
+
+void Interpreter::setResult(byte value) {
+ _result = value;
+}
+
+void Interpreter::setBgOpcodePC(uint32 value) {
+ _bgOpcodePC = value;
+}
+
+void Interpreter::setFgOpcodePC(uint32 value) {
+ _fgOpcodePC = value;
+}
+
+uint16 Interpreter::readScript16() {
+ uint16 data = _script->readScript16(_currentInstruction);
+ _currentInstruction += sizeof(uint16);
+ return data;
+}
+
+uint32 Interpreter::readScript32() {
+ uint32 data = _script->readScript32(_currentInstruction);
+ _currentInstruction += sizeof(uint32);
+ return data;
+}
+
+int32 Interpreter::readScriptFlagValue() {
+ uint16 value = readScript16();
+ if (value & InterpreterFlags::kFlagMask) {
+ return _flags->getFlagValue((Flags::Id)value);
+ }
+ return value;
+}
+
+Flags::Id Interpreter::readScriptFlagId() {
+ return (Flags::Id)readScript16();
+}
+
+void Interpreter::O_WAITFOREVER() {
+ _vm->changeCursor(_vm->_currentPointerNumber);
+ _opcodeNF = 1;
+ _currentInstruction -= 2;
+ //debugInterpreter("O_WAITFOREVER");
+}
+
+void Interpreter::O_BLACKPALETTE() {
+ _vm->blackPalette();
+ debugInterpreter("O_BLACKPALETTE");
+}
+
+void Interpreter::O_SETUPPALETTE() {
+ _vm->setPalette(_vm->_roomBmp->getPalette());
+ debugInterpreter("O_SETUPPALETTE");
+}
+
+void Interpreter::O_INITROOM() {
+ int32 roomId = readScriptFlagValue();
+ _vm->loadLocation(roomId);
+ _opcodeNF = 1;
+ debugInterpreter("O_INITROOM %d", roomId);
+}
+
+void Interpreter::O_SETSAMPLE() {
+ int32 sampleId = readScriptFlagValue();
+ int32 sampleNameOffset = readScript32();
+ const char *sampleName = _script->getString(_currentInstruction + sampleNameOffset - 4);
+ _vm->loadSample(sampleId, sampleName);
+ debugInterpreter("O_SETSAMPLE %d %s", sampleId, sampleName);
+}
+
+void Interpreter::O_FREESAMPLE() {
+ int32 sampleId = readScriptFlagValue();
+ _vm->freeSample(sampleId);
+ debugInterpreter("O_FREESAMPLE sampleId: %d", sampleId);
+}
+
+void Interpreter::O_PLAYSAMPLE() {
+ int32 sampleId = readScriptFlagValue();
+ uint16 loopType = readScript16();
+ _vm->playSample(sampleId, loopType);
+ debugInterpreter("O_PLAYSAMPLE sampleId %d loopType %d", sampleId, loopType);
+}
+
+void Interpreter::O_PUTOBJECT() {
+ int32 roomId = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ int32 objectId = readScriptFlagValue();
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+ _vm->_script->setObjId(room->_obj, slot, objectId);
+ if (_vm->_locationNr == roomId) {
+ _vm->_objSlot[slot] = objectId;
+ }
+ delete room;
+ debugInterpreter("O_PUTOBJECT roomId %d, slot %d, objectId %d", roomId, slot, objectId);
+}
+
+void Interpreter::O_REMOBJECT() {
+ int32 roomId = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+ _vm->_script->setObjId(room->_obj, slot, 0xFF);
+ if (_vm->_locationNr == roomId) {
+ _vm->_objSlot[slot] = 0xFF;
+ }
+ delete room;
+ debugInterpreter("O_REMOBJECT roomId %d slot %d", roomId, slot);
+}
+
+void Interpreter::O_SHOWANIM() {
+ int32 slot = readScriptFlagValue();
+ int32 animId = readScriptFlagValue();
+ _vm->freeNormAnim(slot);
+ Anim &anim = _vm->_normAnimList[slot];
+ AnimListItem &animList = _vm->_animList[animId];
+ anim._currFrame = 0;
+ anim._packFlag = 0;
+ anim._state = 0;
+ anim._frame = animList._startPhase;
+ anim._showFrame = animList._startPhase;
+ anim._lastFrame = animList._endPhase;
+ anim._loopFrame = animList._loopPhase;
+ anim._x = animList._x;
+ anim._y = animList._y;
+ anim._loopType = animList._loopType;
+ anim._shadowBack = animList._type;
+ anim._flags = animList._flags;
+ anim._nextAnim = animList._nextAnim;
+ int fileNumber = animList._fileNumber;
+ const Common::String animName = Common::String::format("AN%02d", fileNumber);
+ const Common::String shadowName = Common::String::format("AN%02dS", fileNumber);
+ anim._animData = new Animation();
+ anim._shadowData = new Animation();
+ Resource::loadResource(anim._animData, animName.c_str(), true);
+ if (!Resource::loadResource(anim._shadowData, shadowName.c_str(), false)) {
+ delete anim._shadowData;
+ anim._shadowData = nullptr;
+ }
+
+ // WALKAROUND: fix for turning off bard's wife background animation
+ // in front of bard's house (location 7) after giving her poem (item 33)
+ // in script: GiveLetter (line 11082)
+ if (_currentInstruction == kGiveLetterScriptFix) {
+ _vm->_backAnimList[1].backAnims[0]._state = 1;
+ }
+
+ debugInterpreter("O_SHOWANIM slot %d, animId %d", slot, animId);
+}
+
+void Interpreter::O_CHECKANIMEND() {
+ int32 slot = readScriptFlagValue();
+ if (_vm->_normAnimList[slot]._frame != _vm->_normAnimList[slot]._lastFrame - 1) {
+ _currentInstruction -= 4;
+ _opcodeNF = 1;
+ }
+ debugInterpreter("O_CHECKANIMEND slot %d", slot);
+}
+
+void Interpreter::O_FREEANIM() {
+ int32 slot = readScriptFlagValue();
+ _vm->freeNormAnim(slot);
+ debugInterpreter("O_FREEANIM slot %d", slot);
+}
+
+void Interpreter::O_CHECKANIMFRAME() {
+ int32 slot = readScriptFlagValue();
+ int32 frameNumber = readScriptFlagValue();
+ if (_vm->_normAnimList[slot]._frame != frameNumber - 1) {
+ _currentInstruction -= 6;
+ _opcodeNF = 1;
+ }
+ debugInterpreter("O_CHECKANIMFRAME slot %d, frameNumber %d", slot, frameNumber);
+}
+
+void Interpreter::O_PUTBACKANIM() {
+ int32 roomId = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ int32 animId = readScript32();
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+ _vm->_script->setBackAnimId(room->_backAnim, slot, animId);
+ if (_vm->_locationNr == roomId) {
+ _vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, room->_backAnim);
+ }
+ delete room;
+ debugInterpreter("O_PUTBACKANIM roomId %d, slot %d, animId %d", roomId, slot, animId);
+}
+
+void Interpreter::O_REMBACKANIM() {
+ int32 roomId = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ if (_vm->_locationNr == roomId) {
+ _vm->removeSingleBackAnim(slot);
+ }
+ Room *room = new Room();
+ room->loadRoom(_script->getRoomOffset(roomId));
+ _vm->_script->setBackAnimId(room->_backAnim, slot, 0);
+ delete room;
+ debugInterpreter("O_REMBACKANIM roomId %d, slot %d", roomId, slot);
+}
+
+void Interpreter::O_CHECKBACKANIMFRAME() {
+ int32 slotId = readScriptFlagValue();
+ int32 frameId = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
+ if (_vm->_backAnimList[slotId].backAnims[currAnim]._frame != frameId - 1) {
+ _currentInstruction -= 6;
+ _opcodeNF = 1;
+ }
+ debugInterpreter("O_CHECKBACKANIMFRAME slotId %d, frameId %d", slotId, frameId);
+}
+
+// Not used in script
+void Interpreter::O_FREEALLSAMPLES() {
+ error("O_FREEALLSAMPLES");
+}
+
+void Interpreter::O_SETMUSIC() {
+ uint16 musicId = readScript16();
+ _vm->loadMusic(musicId);
+ debugInterpreter("O_SETMUSIC musicId %d", musicId);
+}
+
+void Interpreter::O_STOPMUSIC() {
+ _vm->stopMusic();
+ debugInterpreter("O_STOPMUSIC");
+}
+
+void Interpreter::O__WAIT() {
+ int32 pause = readScriptFlagValue();
+ debugInterpreter("O__WAIT pause %d", pause);
+ if (!_waitFlag) {
+ // set new wait flag value and continue
+ _waitFlag = pause;
+ _opcodeNF = 1;
+ _currentInstruction -= 4;
+ return;
+ }
+ _waitFlag--;
+ if (_waitFlag > 0) {
+ _opcodeNF = 1;
+ _currentInstruction -= 4;
+ return;
+ }
+}
+
+// Not used in script
+void Interpreter::O_UPDATEOFF() {
+ error("O_UPDATEOFF");
+}
+
+// Not used in script
+void Interpreter::O_UPDATEON() {
+ error("O_UPDATEON");
+}
+
+// Not used in script
+void Interpreter::O_UPDATE () {
+ error("O_UPDATE");
+}
+
+// Not used in script
+void Interpreter::O_CLS() {
+ error("O_CLS");
+}
+
+void Interpreter::O__CALL() {
+ int32 address = readScript32();
+ _stack[_stacktop] = _currentInstruction;
+ _stacktop++;
+ _currentInstruction += address - 4;
+ debugInterpreter("O__CALL 0x%04X", _currentInstruction);
+}
+
+void Interpreter::O_RETURN() {
+ if (_stacktop > 0) {
+ _stacktop--;
+ _currentInstruction = _stack[_stacktop];
+ debugInterpreter("O_RETURN 0x%04X", _currentInstruction);
+ } else {
+ error("O_RETURN: Stack is empty");
+ }
+}
+
+void Interpreter::O_GO() {
+ int32 opPC = readScript32();
+ _currentInstruction += opPC - 4;
+ debugInterpreter("O_GO 0x%04X", opPC);
+}
+
+void Interpreter::O_BACKANIMUPDATEOFF() {
+ int32 slotId = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
+ if (!_vm->_backAnimList[slotId].backAnims.empty()) {
+ _vm->_backAnimList[slotId].backAnims[currAnim]._state = 1;
+ }
+ debugInterpreter("O_BACKANIMUPDATEOFF slotId %d", slotId);
+}
+
+void Interpreter::O_BACKANIMUPDATEON() {
+ int32 slotId = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
+ if (!_vm->_backAnimList[slotId].backAnims.empty()) {
+ _vm->_backAnimList[slotId].backAnims[currAnim]._state = 0;
+ }
+ debugInterpreter("O_BACKANIMUPDATEON slotId %d", slotId);
+}
+
+void Interpreter::O_CHANGECURSOR() {
+ int32 cursorId = readScriptFlagValue();
+ _vm->changeCursor(cursorId);
+ debugInterpreter("O_CHANGECURSOR %x", cursorId);
+}
+
+// Not used in script
+void Interpreter::O_CHANGEANIMTYPE() {
+ error("O_CHANGEANIMTYPE");
+}
+
+void Interpreter::O__SETFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue((Flags::Id)(flagId), value);
+ debugInterpreter("O__SETFLAG 0x%04X (%s) = %d", flagId, Flags::getFlagName(flagId), value);
+}
+
+void Interpreter::O_COMPARE() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _result = _flags->getFlagValue(flagId) != value;
+ debugInterpreter("O_COMPARE flagId 0x%04X (%s), value %d == %d (%d)", flagId, Flags::getFlagName(flagId), value, _flags->getFlagValue(flagId), _result);
+}
+
+void Interpreter::O_JUMPZ() {
+ int32 offset = readScript32();
+ if (!_result) {
+ _currentInstruction += offset - 4;
+ }
+ debugInterpreter("O_JUMPZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset);
+}
+
+void Interpreter::O_JUMPNZ() {
+ int32 offset = readScript32();
+ if (_result) {
+ _currentInstruction += offset - 4;
+ }
+ debugInterpreter("O_JUMPNZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset);
+}
+
+void Interpreter::O_EXIT() {
+ int32 exitCode = readScriptFlagValue();
+ _opcodeEnd = true;
+ _opcodeNF = 1;
+ if (exitCode == 0x2EAD) {
+ _vm->scrollCredits();
+ }
+ debugInterpreter("O_EXIT exitCode %d", exitCode);
+}
+
+void Interpreter::O_ADDFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) + value);
+ if (_flags->getFlagValue(flagId)) {
+ _result = 1;
+ }
+ else {
+ _result = 0;
+ }
+ debugInterpreter("O_ADDFLAG flagId %04x (%s), value %d", flagId, Flags::getFlagName(flagId), value);
+}
+
+void Interpreter::O_TALKANIM() {
+ int32 animNumber = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ _vm->doTalkAnim(animNumber, slot, kNormalAnimation);
+ debugInterpreter("O_TALKANIM animNumber %d, slot %d", animNumber, slot);
+}
+
+void Interpreter::O_SUBFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) - value);
+ if (_flags->getFlagValue(flagId)) {
+ _result = 1;
+ }
+ else {
+ _result = 0;
+ }
+ debugInterpreter("O_SUBFLAG flagId %d, value %d", flagId, value);
+}
+
+void Interpreter::O_SETSTRING() {
+ int32 offset = readScript32();
+ _currentString = offset;
+ if (offset >= 80000) {
+ _string = _vm->_variaTxt->getString(offset - 80000);
+ debugInterpreter("GetVaria %s", _string);
+ }
+ else if (offset < 2000) {
+ _vm->_dialogData = &_vm->_dialogDat[offset * 4 - 4];
+ uint32 of = READ_LE_UINT32(_vm->_talkTxt + offset * 4);
+ const char *txt = (const char *)&_vm->_talkTxt[of];
+ _string = &_vm->_talkTxt[of];
+ debugInterpreter("TalkTxt %d %s", of, txt);
+ }
+ debugInterpreter("O_SETSTRING %04d", offset);
+}
+
+void Interpreter::O_ANDFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) & value);
+ if (_flags->getFlagValue(flagId)) {
+ _result = 1;
+ } else {
+ _result = 0;
+ }
+ debugInterpreter("O_ANDFLAG flagId %d, value %d", flagId, value);
+}
+
+void Interpreter::O_GETMOBDATA() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 mobId = readScriptFlagValue();
+ int32 mobOffset = readScriptFlagValue();
+ int16 value = _vm->_mobList[mobId].getData((Mob::AttrId)mobOffset);
+ _flags->setFlagValue(flagId, value);
+ debugInterpreter("O_GETMOBDATA flagId %d, modId %d, mobOffset %d", flagId, mobId, mobOffset);
+}
+
+void Interpreter::O_ORFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) | value);
+ if (_flags->getFlagValue(flagId)) {
+ _result = 1;
+ } else {
+ _result = 0;
+ }
+ debugInterpreter("O_ORFLAG flagId %d, value %d", flagId, value);
+}
+
+void Interpreter::O_SETMOBDATA() {
+ int32 mobId = readScriptFlagValue();
+ int32 mobOffset = readScriptFlagValue();
+ int32 value = readScriptFlagValue();
+ _vm->_mobList[mobId].setData((Mob::AttrId)mobOffset, value);
+ debugInterpreter("O_SETMOBDATA mobId %d, mobOffset %d, value %d", mobId, mobOffset, value);
+}
+
+void Interpreter::O_XORFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) ^ value);
+ if (_flags->getFlagValue(flagId)) {
+ _result = 1;
+ } else {
+ _result = 0;
+ }
+ debugInterpreter("O_XORFLAG flagId %d, value %d", flagId, value);
+}
+
+void Interpreter::O_GETMOBTEXT() {
+ int32 mob = readScriptFlagValue();
+ _currentString = _vm->_locationNr * 100 + mob + 60001;
+ _string = (byte *)_vm->_mobList[mob]._examText.c_str();
+ debugInterpreter("O_GETMOBTEXT mob %d", mob);
+}
+
+void Interpreter::O_MOVEHERO() {
+ int32 heroId = readScriptFlagValue();
+ int32 x = readScriptFlagValue();
+ int32 y = readScriptFlagValue();
+ int32 dir = readScriptFlagValue();
+ _vm->moveRunHero(heroId, x, y, dir, false);
+ debugInterpreter("O_MOVEHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
+}
+
+void Interpreter::O_WALKHERO() {
+ int32 heroId = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else if (heroId == 1) {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ if (hero->_state != Hero::kHeroStateStay) {
+ _currentInstruction -= 4;
+ _opcodeNF = 1;
+ }
+ }
+ debugInterpreter("O_WALKHERO %d", heroId);
+}
+
+void Interpreter::O_SETHERO() {
+ int32 heroId = readScriptFlagValue();
+ int32 x = readScriptFlagValue();
+ int32 y = readScriptFlagValue();
+ int32 dir = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else if (heroId == 1) {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ hero->setPos(x, y);
+ hero->_lastDirection = dir;
+ hero->_visible = 1;
+ hero->countDrawPosition();
+ }
+ debugInterpreter("O_SETHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
+}
+
+void Interpreter::O_HEROOFF() {
+ int32 heroId = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else if (heroId == 1) {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ hero->setVisible(false);
+ }
+ debugInterpreter("O_HEROOFF %d", heroId);
+}
+
+void Interpreter::O_HEROON() {
+ int32 heroId = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else if (heroId == 1) {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ hero->setVisible(true);
+ }
+ debugInterpreter("O_HEROON %d", heroId);
+}
+
+void Interpreter::O_CLSTEXT() {
+ int32 slot = readScriptFlagValue();
+ _vm->_textSlots[slot]._str = nullptr;
+ _vm->_textSlots[slot]._time = 0;
+ debugInterpreter("O_CLSTEXT slot %d", slot);
+}
+
+void Interpreter::O_CALLTABLE() {
+ Flags::Id flagId = readScriptFlagId();
+ int roomNr = _flags->getFlagValue(flagId);
+ int32 tableOffset = readScript32();
+ int initLocationScript = _script->getLocationInitScript(tableOffset, roomNr);
+ if (initLocationScript) {
+ _stack[_stacktop] = _currentInstruction;
+ _stacktop++;
+ _currentInstruction = initLocationScript;
+ }
+ debugInterpreter("O_CALLTABLE loc %d", roomNr);
+}
+
+void Interpreter::O_CHANGEMOB() {
+ int32 mob = readScriptFlagValue();
+ int32 value = readScriptFlagValue();
+ value ^= 1;
+ _vm->_script->setMobVisible(_vm->_room->_mobs, mob, value);
+ _vm->_mobList[mob]._visible = value;
+ debugInterpreter("O_CHANGEMOB mob %d, value %d", mob, value);
+}
+
+void Interpreter::O_ADDINV() {
+ int32 hero = readScriptFlagValue();
+ int32 item = readScriptFlagValue();
+ _vm->addInv(hero, item, false);
+ debugInterpreter("O_ADDINV hero %d, item %d", hero, item);
+}
+
+void Interpreter::O_REMINV() {
+ int32 hero = readScriptFlagValue();
+ int32 item = readScriptFlagValue();
+ _vm->remInv(hero, item);
+ debugInterpreter("O_REMINV hero %d, item %d", hero, item);
+}
+
+// Not used in script
+void Interpreter::O_REPINV() {
+ error("O_REPINV");
+}
+
+// Not used in script
+void Interpreter::O_OBSOLETE_GETACTION() {
+ error("O_OBSOLETE_GETACTION");
+}
+
+// Not used in script
+void Interpreter::O_ADDWALKAREA() {
+ error("O_ADDWALKAREA");
+}
+
+// Not used in script
+void Interpreter::O_REMWALKAREA() {
+ error("O_REMWALKAREA");
+}
+
+ // Not used in script
+void Interpreter::O_RESTOREWALKAREA() {
+ error("O_RESTOREWALKAREA");
+}
+
+void Interpreter::O_WAITFRAME() {
+ _opcodeNF = true;
+ debugInterpreter("O_WAITFRAME");
+}
+
+void Interpreter::O_SETFRAME() {
+ int32 anim = readScriptFlagValue();
+ int32 frame = readScriptFlagValue();
+ _vm->_normAnimList[anim]._frame = frame;
+ debugInterpreter("O_SETFRAME anim %d, frame %d", anim, frame);
+}
+
+// Not used in script
+void Interpreter::O_RUNACTION() {
+ error("O_RUNACTION");
+}
+
+void Interpreter::O_COMPAREHI() {
+ Flags::Id flag = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ int32 flagValue = _flags->getFlagValue(flag);
+ if (flagValue > value) {
+ _result = 0;
+ } else {
+ _result = 1;
+ }
+ debugInterpreter("O_COMPAREHI flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result);
+}
+
+void Interpreter::O_COMPARELO() {
+ Flags::Id flag = readScriptFlagId();
+ int32 value = readScriptFlagValue();
+ int32 flagValue = _flags->getFlagValue(flag);
+ if (flagValue < value) {
+ _result = 0;
+ } else {
+ _result = 1;
+ }
+ debugInterpreter("O_COMPARELO flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result);
+}
+
+// Not used in script
+void Interpreter::O_PRELOADSET() {
+ error("O_PRELOADSET");
+}
+
+// Not used in script
+void Interpreter::O_FREEPRELOAD() {
+ error("O_FREEPRELOAD");
+}
+
+// Not used in script
+void Interpreter::O_CHECKINV() {
+ error("O_CHECKINV");
+}
+
+void Interpreter::O_TALKHERO() {
+ int32 hero = readScriptFlagValue();
+ _vm->talkHero(hero);
+ debugInterpreter("O_TALKHERO hero %d", hero);
+}
+
+void Interpreter::O_WAITTEXT() {
+ int32 slot = readScriptFlagValue();
+ Text &text = _vm->_textSlots[slot];
+ if (text._time && text._str) {
+ if (_flags->getFlagValue(Flags::ESCAPED)) {
+ text._time = 1;
+ if (!slot) {
+ _vm->_mainHero->_talkTime = 1;
+ } else if (slot == 1) {
+ _vm->_secondHero->_talkTime = 1;
+ }
+ } else {
+ _opcodeNF = 1;
+ _currentInstruction -= 4;
+ }
+ }
+ //debugInterpreter("O_WAITTEXT slot %d", slot);
+}
+
+void Interpreter::O_SETHEROANIM() {
+ int32 heroId = readScriptFlagValue();
+ int32 offset = readScript32();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ hero->freeHeroAnim();
+ if (hero ->_specAnim == nullptr) {
+ hero->_specAnim = new Animation();
+ if (offset < 100) {
+ const Common::String animName = Common::String::format("AN%02d", offset);
+ Resource::loadResource(hero->_specAnim, animName.c_str(), true);
+ } else {
+ const Common::String animName = Common::String((const char *)_script->getHeroAnimName(offset));
+ Common::String normalizedPath = lastPathComponent(animName, '\\');
+ Resource::loadResource(hero->_specAnim, normalizedPath.c_str(), true);
+ }
+ hero->_phase = 0;
+ hero->_state = Hero::kHeroStateSpec;
+ }
+ }
+ debugInterpreter("O_SETHEROANIM hero %d, offset %d", hero, offset);
+}
+
+void Interpreter::O_WAITHEROANIM() {
+ int32 heroId = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ if (hero->_state == Hero::kHeroStateSpec) {
+ _currentInstruction -= 4;
+ _opcodeNF = 1;
+ }
+ }
+ debugInterpreter("O_WAITHEROANIM heroId %d", heroId);
+}
+
+void Interpreter::O_GETHERODATA() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 heroId = readScriptFlagValue();
+ int32 heroOffset = readScriptFlagValue();
+ Hero *hero = nullptr;
+ if (!heroId) {
+ hero = _vm->_mainHero;
+ } else {
+ hero = _vm->_secondHero;
+ }
+ if (hero != nullptr) {
+ _flags->setFlagValue(flagId, hero->getData((Hero::AttrId)heroOffset));
+ }
+ debugInterpreter("O_GETHERODATA flag %04x - (%s), heroId %d, heroOffset %d", flagId, Flags::getFlagName(flagId), heroId, heroOffset);
+}
+
+// No need of implementation here
+void Interpreter::O_GETMOUSEBUTTON() {
+ debugInterpreter("O_GETMOUSEBUTTON");
+}
+
+void Interpreter::O_CHANGEFRAMES() {
+ int32 anim = readScriptFlagValue();
+ int32 frame = readScriptFlagValue();
+ int32 lastFrame = readScriptFlagValue();
+ int32 loopFrame = readScriptFlagValue();
+ _vm->_normAnimList[anim]._frame = frame;
+ _vm->_normAnimList[anim]._lastFrame = lastFrame;
+ _vm->_normAnimList[anim]._loopFrame = loopFrame;
+ debugInterpreter("O_CHANGFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame);
+}
+
+void Interpreter::O_CHANGEBACKFRAMES() {
+ int32 anim = readScriptFlagValue();
+ int32 frame = readScriptFlagValue();
+ int32 lastFrame = readScriptFlagValue();
+ int32 loopFrame = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[anim]._seq._currRelative;
+ Anim &backAnim = _vm->_backAnimList[anim].backAnims[currAnim];
+ backAnim._frame = frame;
+ backAnim._lastFrame = lastFrame;
+ backAnim._loopFrame = loopFrame;
+ debugInterpreter("O_CHANGEBACKFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame);
+}
+
+void Interpreter::O_GETBACKANIMDATA() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 animNumber = readScriptFlagValue();
+ int32 animDataOffset = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative;
+ int16 value = _vm->_backAnimList[animNumber].backAnims[currAnim].getAnimData((Anim::AnimOffsets)(animDataOffset));
+ _flags->setFlagValue((Flags::Id)(flagId), value);
+ debugInterpreter("O_GETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value);
+}
+
+void Interpreter::O_GETANIMDATA() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 anim = readScriptFlagValue();
+ int32 animOffset = readScriptFlagValue();
+ if (_vm->_normAnimList[anim]._animData != nullptr) {
+ _flags->setFlagValue(flagId, _vm->_normAnimList[anim].getAnimData((Anim::AnimOffsets)(animOffset)));
+ }
+ debugInterpreter("O_GETANIMDATA flag %04X (%s), anim %d, animOffset %d", flagId, Flags::getFlagName(flagId), anim, animOffset);
+}
+
+void Interpreter::O_SETBGCODE() {
+ int32 offset = readScript32();
+ _bgOpcodePC = _currentInstruction + offset - 4;
+ debugInterpreter("O_SETBGCODE next %08x, offset %08x", _bgOpcodePC, offset);
+}
+
+void Interpreter::O_SETBACKFRAME() {
+ int32 anim = readScriptFlagValue();
+ int32 frame = readScriptFlagValue();
+ int currAnim = _vm->_backAnimList[anim]._seq._currRelative;
+ if (_vm->_backAnimList[anim].backAnims[currAnim]._animData != nullptr) {
+ _vm->_backAnimList[anim].backAnims[currAnim]._frame = frame;
+ }
+ debugInterpreter("O_SETBACKFRAME anim %d, frame %d", anim, frame);
+}
+
+void Interpreter::O_GETRND() {
+ Flags::Id flag = readScriptFlagId();
+ uint16 rndSeed = readScript16();
+ int value = _vm->_randomSource.getRandomNumber(rndSeed - 1);
+ _flags->setFlagValue(flag, value);
+ debugInterpreter("O_GETRND flag %d, rndSeed %d, value %d", flag, rndSeed, value);
+}
+
+void Interpreter::O_TALKBACKANIM() {
+ int32 animNumber = readScriptFlagValue();
+ int32 slot = readScriptFlagValue();
+ _vm->doTalkAnim(animNumber, slot, kBackgroundAnimation);
+ debugInterpreter("O_TALKBACKANIM animNumber %d, slot %d", animNumber, slot);
+}
+
+// Simplifying, because used only once in Location 20
+void Interpreter::O_LOADPATH() {
+ readScript32();
+ _vm->loadPath("path2");
+ debugInterpreter("O_LOADPATH - path2");
+}
+
+void Interpreter::O_GETCHAR() {
+ Flags::Id flagId = readScriptFlagId();
+ _flags->setFlagValue(flagId, *_string);
+ _string++;
+ debugInterpreter("O_GETCHAR %04X (%s) %02x", flagId, Flags::getFlagName(flagId), _flags->getFlagValue(flagId));
+}
+
+void Interpreter::O_SETDFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 address = readScript32();
+ _flags->setFlagValue((Flags::Id)(flagId), _currentInstruction + address - 4);
+ debugInterpreter("O_SETDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction + address - 4);
+}
+
+void Interpreter::O_CALLDFLAG() {
+ Flags::Id flagId = readScriptFlagId();
+ _stack[_stacktop] = _currentInstruction;
+ _stacktop++;
+ _currentInstruction = _flags->getFlagValue(flagId);
+ debugInterpreter("O_CALLDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction);
+}
+
+void Interpreter::O_PRINTAT() {
+ int32 slot = readScriptFlagValue();
+ int32 x = readScriptFlagValue();
+ int32 y = readScriptFlagValue();
+ int32 color = _flags->getFlagValue(Flags::KOLOR);
+ _vm->printAt(slot, color, (char *)_string, x, y);
+ increaseString();
+ debugInterpreter("O_PRINTAT slot %d, x %d, y %d", slot, x, y);
+}
+
+void Interpreter::O_ZOOMIN() {
+ int32 slot = readScriptFlagValue();
+ _vm->initZoomIn(slot);
+ debugInterpreter("O_ZOOMIN slot %04d", slot);
+}
+
+void Interpreter::O_ZOOMOUT() {
+ int32 slot = readScriptFlagValue();
+ _vm->initZoomOut(slot);
+ debugInterpreter("O_ZOOMOUT slot %d", slot);
+}
+
+// Not used in script
+void Interpreter::O_SETSTRINGOFFSET() {
+ error("O_SETSTRINGOFFSET");
+}
+
+void Interpreter::O_GETOBJDATA() {
+ Flags::Id flag = readScriptFlagId();
+ int32 slot = readScriptFlagValue();
+ int32 objOffset = readScriptFlagValue();
+ int nr = _vm->_objSlot[slot];
+ if (nr != 0xFF) {
+ int16 value = _vm->_objList[nr]->getData((Object::AttrId)objOffset);
+ _flags->setFlagValue(flag, value);
+ }
+ debugInterpreter("O_GETOBJDATA flag %d, objSlot %d, objOffset %d", flag, slot, objOffset);
+}
+
+void Interpreter::O_SETOBJDATA() {
+ int32 slot = readScriptFlagValue();
+ int32 objOffset = readScriptFlagValue();
+ int32 value = readScriptFlagValue();
+ int nr = _vm->_objSlot[slot];
+ if (nr != 0xFF) {
+ _vm->_objList[nr]->setData((Object::AttrId)objOffset, value);
+ }
+ debugInterpreter("O_SETOBJDATA objSlot %d, objOffset %d, value %d", slot, objOffset, value);
+}
+
+// Not used in script
+void Interpreter::O_SWAPOBJECTS() {
+ error("O_SWAPOBJECTS");
+}
+
+void Interpreter::O_CHANGEHEROSET() {
+ int32 heroId = readScriptFlagValue();
+ int32 heroSet = readScriptFlagValue();
+ if (!heroId) {
+ _vm->_mainHero->loadAnimSet(heroSet);
+ } else if (heroId == 1) {
+ _vm->_secondHero->loadAnimSet(heroSet);
+ }
+ debugInterpreter("O_CHANGEHEROSET hero %d, heroSet %d", heroId, heroSet);
+}
+
+// Not used in script
+void Interpreter::O_ADDSTRING() {
+ error("O_ADDSTRING");
+}
+
+void Interpreter::O_SUBSTRING() {
+ int32 value = readScriptFlagValue();
+ _string -= value;
+ debugInterpreter("O_SUBSTRING value %d", value);
+}
+
+int Interpreter::checkSeq(byte *string) {
+ int freeHSlotIncrease = 0;
+ byte c;
+ while ((c = string[0]) != 0xFF) {
+ string++;
+ if (c < 0xF0) {
+ freeHSlotIncrease++;
+ while ((c = string[0])) {
+ string++;
+ }
+ string++;
+ } else if (c != 0xFE) {
+ string++;
+ }
+ }
+ return freeHSlotIncrease;
+}
+
+void Interpreter::O_INITDIALOG() {
+ if (_string[0] == 255) {
+ byte *stringCurrOff = _string;
+ byte *string = _string;
+ stringCurrOff++;
+ int32 adressOfFirstSequence = (int)READ_LE_UINT16(stringCurrOff);
+ stringCurrOff += 2;
+ _string = string + adressOfFirstSequence;
+
+ for (int i = 0; i < 32; i++) {
+ _vm->_dialogBoxAddr[i] = 0;
+ _vm->_dialogOptAddr[i] = 0;
+ }
+
+ for (int i = 0; i < 4 * 32; i++) {
+ _vm->_dialogOptLines[i] = 0;
+ }
+
+ int16 off;
+ byte *line = nullptr;
+
+ int dialogBox = 0;
+ while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) {
+ stringCurrOff += 2;
+ if (off) {
+ line = string + off;
+ }
+ _vm->_dialogBoxAddr[dialogBox] = line;
+ dialogBox++;
+ }
+ stringCurrOff += 2;
+
+ int dialogOpt = 0;
+ while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) {
+ stringCurrOff += 2;
+ if (off) {
+ line = string + off;
+ }
+ _vm->_dialogOptAddr[dialogOpt] = line;
+ dialogOpt++;
+ }
+
+ _flags->setFlagValue(Flags::VOICE_A_LINE, 0);
+ _flags->setFlagValue(Flags::VOICE_B_LINE, 0); // bx in original?
+
+ int freeHSlot = 0;
+ for (int i = 31; i >= 0; i--) {
+ if (_vm->_dialogOptAddr[i] != 0) {
+ i++;
+ freeHSlot = i;
+ _flags->setFlagValue(Flags::VOICE_H_LINE, i);
+ break;
+ }
+ }
+
+ freeHSlot += checkSeq(_string);
+
+ for (int i = 0; i < 32; i++) {
+ _vm->_dialogOptLines[i * 4] = freeHSlot;
+ _vm->_dialogOptLines[i * 4 + 1] = freeHSlot;
+ _vm->_dialogOptLines[i * 4 + 2] = freeHSlot;
+ if (_vm->_dialogOptAddr[i]) {
+ freeHSlot += checkSeq(_vm->_dialogOptAddr[i]);
+ }
+ }
+ }
+ debugInterpreter("O_INITDIALOG");
+}
+
+void Interpreter::O_ENABLEDIALOGOPT() {
+ int32 opt = readScriptFlagValue();
+ int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData);
+ dialogDataValue &= ~(1u << opt);
+ WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue);
+ debugInterpreter("O_ENABLEDIALOGOPT opt %d", opt);
+}
+
+void Interpreter::O_DISABLEDIALOGOPT() {
+ int32 opt = readScriptFlagValue();
+ int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData);
+ dialogDataValue |= (1u << opt);
+ WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue);
+ debugInterpreter("O_DISABLEDIALOGOPT opt %d", opt);
+}
+
+void Interpreter::O_SHOWDIALOGBOX() {
+ int32 box = readScriptFlagValue();
+ uint32 currInstr = _currentInstruction;
+ _vm->createDialogBox(box);
+ _flags->setFlagValue(Flags::DIALINES, _vm->_dialogLines);
+ if (_vm->_dialogLines) {
+ _vm->changeCursor(1);
+ _vm->runDialog();
+ _vm->changeCursor(0);
+ }
+ _currentInstruction = currInstr;
+ debugInterpreter("O_SHOWDIALOGBOX box %d", box);
+}
+
+void Interpreter::O_STOPSAMPLE() {
+ int32 slot = readScriptFlagValue();
+ _vm->stopSample(slot);
+ debugInterpreter("O_STOPSAMPLE slot %d", slot);
+}
+
+void Interpreter::O_BACKANIMRANGE() {
+ int32 slotId = readScriptFlagValue();
+ uint16 animId = readScript16();
+ int32 low = readScriptFlagValue();
+ int32 high = readScriptFlagValue();
+ if (animId != 0xFFFF) {
+ if (animId & InterpreterFlags::kFlagMask) {
+ animId = _flags->getFlagValue((Flags::Id)animId);
+ }
+ }
+ _result = 1;
+ if (!_vm->_backAnimList[slotId].backAnims.empty()) {
+ int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
+ if (_vm->_backAnimList[slotId].backAnims[currAnim]._animData != nullptr) {
+ if (animId == 0xFFFF || _vm->_backAnimList[slotId]._seq._current == animId) {
+ int currAnim = _vm->_backAnimList[slotId]._seq._currRelative;
+ Anim &backAnim = _vm->_backAnimList[slotId].backAnims[currAnim];
+ if (!backAnim._state) {
+ if (backAnim._frame >= low) {
+ if (backAnim._frame <= high) {
+ _result = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ debugInterpreter("O_BACKANIMRANGE slotId %d, animId %d, low %d, high %d, _result %d", slotId, animId, low, high, _result);
+}
+
+void Interpreter::O_CLEARPATH() {
+ for (int i = 0; i < _vm->kPathBitmapLen; i++) {
+ _vm->_roomPathBitmap[i] = 255;
+ }
+ debugInterpreter("O_CLEARPATH");
+}
+
+void Interpreter::O_SETPATH() {
+ _vm->loadPath("path");
+ debugInterpreter("O_SETPATH");
+}
+
+void Interpreter::O_GETHEROX() {
+ int32 heroId = readScriptFlagValue();
+ Flags::Id flagId = readScriptFlagId();
+ if (!heroId) {
+ _flags->setFlagValue(flagId, _vm->_mainHero->_middleX);
+ } else if (heroId == 1) {
+ _flags->setFlagValue(flagId, _vm->_secondHero->_middleX);
+ }
+ debugInterpreter("O_GETHEROX heroId %d, flagId %d", heroId, flagId);
+}
+
+void Interpreter::O_GETHEROY() {
+ int32 heroId = readScriptFlagValue();
+ Flags::Id flagId = readScriptFlagId();
+ if (!heroId) {
+ _flags->setFlagValue(flagId, _vm->_mainHero->_middleY);
+ } else if (heroId == 1) {
+ _flags->setFlagValue(flagId, _vm->_secondHero->_middleY);
+ }
+ debugInterpreter("O_GETHEROY heroId %d, flagId %d", heroId, flagId);
+}
+
+void Interpreter::O_GETHEROD() {
+ int32 heroId = readScriptFlagValue();
+ Flags::Id flagId = readScriptFlagId();
+ if (!heroId) {
+ _flags->setFlagValue(flagId, _vm->_mainHero->_lastDirection);
+ } else if (heroId == 1) {
+ _flags->setFlagValue(flagId, _vm->_secondHero->_lastDirection);
+ }
+ debugInterpreter("O_GETHEROD heroId %d, flagId %d", heroId, flagId);
+}
+
+void Interpreter::O_PUSHSTRING() {
+ _stringStack.string = _string;
+ _stringStack.dialogData = _vm->_dialogData;
+ _stringStack.currentString = _currentString;
+ debugInterpreter("O_PUSHSTRING");
+}
+
+void Interpreter::O_POPSTRING() {
+ _string = _stringStack.string;
+ _vm->_dialogData = _stringStack.dialogData;
+ _currentString = _stringStack.currentString;
+ debugInterpreter("O_POPSTRING");
+}
+
+void Interpreter::O_SETFGCODE() {
+ int32 offset = readScript32();
+ _fgOpcodePC = _currentInstruction + offset - 4;
+ debugInterpreter("O_SETFGCODE next %08x, offset %08x", _fgOpcodePC, offset);
+}
+
+void Interpreter::O_STOPHERO() {
+ int32 heroId = readScriptFlagValue();
+ if (!heroId) {
+ _vm->_mainHero->freeOldMove();
+ } else if (heroId == 1) {
+ _vm->_secondHero->freeOldMove();
+ }
+ debugInterpreter("O_STOPHERO heroId %d", heroId);
+}
+
+void Interpreter::O_ANIMUPDATEOFF() {
+ int32 slotId = readScriptFlagValue();
+ _vm->_normAnimList[slotId]._state = 1;
+ debugInterpreter("O_ANIMUPDATEOFF slotId %d", slotId);
+}
+
+void Interpreter::O_ANIMUPDATEON() {
+ int32 slotId = readScriptFlagValue();
+ _vm->_normAnimList[slotId]._state = 0;
+ debugInterpreter("O_ANIMUPDATEON slotId %d", slotId);
+}
+
+void Interpreter::O_FREECURSOR() {
+ _vm->changeCursor(0);
+ _vm->_currentPointerNumber = 1;
+ // free memory here?
+ debugInterpreter("O_FREECURSOR");
+}
+
+void Interpreter::O_ADDINVQUIET() {
+ int32 hero = readScriptFlagValue();
+ int32 item = readScriptFlagValue();
+ _vm->addInv(hero, item, true);
+ debugInterpreter("O_ADDINVQUIET hero %d, item %d", hero, item);
+}
+
+void Interpreter::O_RUNHERO() {
+ int32 heroId = readScriptFlagValue();
+ int32 x = readScriptFlagValue();
+ int32 y = readScriptFlagValue();
+ int32 dir = readScriptFlagValue();
+ _vm->moveRunHero(heroId, x, y, dir, true);
+ debugInterpreter("O_RUNHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir);
+}
+
+void Interpreter::O_SETBACKANIMDATA() {
+ uint16 animNumber = readScript16();
+ uint16 animDataOffset = readScript16();
+ Flags::Id flagId = readScriptFlagId();
+ uint16 value = _flags->getFlagValue((Flags::Id)(flagId));
+ int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative;
+ _vm->_backAnimList[animNumber].backAnims[currAnim].setAnimData((Anim::AnimOffsets)(animDataOffset), value);
+ debugInterpreter("O_SETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value);
+}
+
+void Interpreter::O_VIEWFLC() {
+ int32 animNr = readScriptFlagValue();
+ _vm->_flcFrameSurface = nullptr;
+ _vm->loadAnim(animNr, false);
+ debugInterpreter("O_VIEWFLC animNr %d", animNr);
+}
+
+void Interpreter::O_CHECKFLCFRAME() {
+ int32 frameNr = readScriptFlagValue();
+ debugInterpreter("O_CHECKFLCFRAME frame number %d", frameNr);
+ if (_vm->_flicPlayer.getCurFrame() != frameNr) {
+ _currentInstruction -= 4;
+ _opcodeNF = 1;
+ }
+}
+
+void Interpreter::O_CHECKFLCEND() {
+ const Video::FlicDecoder &flicPlayer = _vm->_flicPlayer;
+ debugInterpreter("O_CHECKFLCEND frameCount %d, currentFrame %d", flicPlayer.getFrameCount(), flicPlayer.getCurFrame());
+ if (flicPlayer.getFrameCount() - flicPlayer.getCurFrame() > 1) {
+ _currentInstruction -= 2;
+ _opcodeNF = 1;
+ }
+}
+
+void Interpreter::O_FREEFLC() {
+ _vm->_flcFrameSurface = nullptr;
+ debugInterpreter("O_FREEFLC");
+}
+
+void Interpreter::O_TALKHEROSTOP() {
+ int32 heroId = readScriptFlagValue();
+ if (!heroId) {
+ _vm->_mainHero->_state = Hero::kHeroStateStay;
+ } else if (heroId == 1) {
+ _vm->_secondHero->_state = Hero::kHeroStateStay;
+ }
+ debugInterpreter("O_TALKHEROSTOP %d", heroId);
+}
+
+void Interpreter::O_HEROCOLOR() {
+ int32 heroId = readScriptFlagValue();
+ int32 color = readScriptFlagValue();
+ if (!heroId) {
+ _vm->_mainHero->_color = color;
+ } else if (heroId == 1) {
+ _vm->_secondHero->_color = color;
+ }
+ debugInterpreter("O_HEROCOLOR heroId %d, color %d", heroId, color);
+}
+
+void Interpreter::O_GRABMAPA() {
+ _vm->grabMap();
+ debugInterpreter("O_GRABMAPA");
+}
+
+void Interpreter::O_ENABLENAK() {
+ int32 nakId = readScriptFlagValue();
+ _vm->_maskList[nakId]._flags = 0;
+ debugInterpreter("O_ENABLENAK nakId %d", nakId);
+}
+
+void Interpreter::O_DISABLENAK() {
+ int32 nakId = readScriptFlagValue();
+ _vm->_maskList[nakId]._flags = 1;
+ debugInterpreter("O_DISABLENAK nakId %d", nakId);
+}
+
+void Interpreter::O_GETMOBNAME() {
+ int32 modId = readScriptFlagValue();
+ _string = (byte *)_vm->_mobList[modId]._name.c_str();
+ debugInterpreter("O_GETMOBNAME modId %d", modId);
+}
+
+void Interpreter::O_SWAPINVENTORY() {
+ int32 hero = readScriptFlagValue();
+ _vm->swapInv(hero);
+ debugInterpreter("O_SWAPINVENTORY hero %d", hero);
+}
+
+void Interpreter::O_CLEARINVENTORY() {
+ int32 hero = readScriptFlagValue();
+ _vm->clearInv(hero);
+ debugInterpreter("O_CLEARINVENTORY hero %d", hero);
+}
+
+void Interpreter::O_SKIPTEXT() {
+ increaseString();
+ debugInterpreter("O_SKIPTEXT");
+}
+
+void Interpreter::O_SETVOICEH() {
+ int32 slot = readScriptFlagValue();
+ static const uint32 VOICE_H_SLOT = 28;
+ uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
+ _vm->setVoice(slot, VOICE_H_SLOT, voiceLineH);
+}
+
+void Interpreter::O_SETVOICEA() {
+ int32 slot = readScriptFlagValue();
+ static const uint32 VOICE_A_SLOT = 29;
+ uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
+ _vm->setVoice(slot, VOICE_A_SLOT, voiceLineH);
+}
+
+void Interpreter::O_SETVOICEB() {
+ int32 slot = readScriptFlagValue();
+ static const uint32 VOICE_B_SLOT = 30;
+ uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
+ _vm->setVoice(slot, VOICE_B_SLOT, voiceLineH);
+}
+
+void Interpreter::O_SETVOICEC() {
+ int32 slot = readScriptFlagValue();
+ static const uint32 VOICE_C_SLOT = 31;
+ uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
+ _vm->setVoice(slot, VOICE_C_SLOT, voiceLineH);
+}
+
+void Interpreter::O_SETVOICED() {
+ int32 slot = readScriptFlagValue();
+ static const uint32 VOICE_D_SLOT = 32;
+ uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE);
+ _vm->setVoice(slot, VOICE_D_SLOT, voiceLineH);
+}
+
+void Interpreter::O_VIEWFLCLOOP() {
+ int32 animId = readScriptFlagValue();
+ _vm->loadAnim(animId, true);
+ debugInterpreter("O_VIEWFLCLOOP animId %d", animId);
+}
+
+// Not used in script
+void Interpreter::O_FLCSPEED() {
+ error("O_FLCSPEED speed %d");
+}
+
+void Interpreter::O_OPENINVENTORY() {
+ _vm->_showInventoryFlag = true;
+ _opcodeNF = 1;
+ debugInterpreter("O_OPENINVENTORY");
+}
+
+void Interpreter::O_KRZYWA() {
+ _vm->makeCurve();
+ debugInterpreter("O_KRZYWA");
+}
+
+void Interpreter::O_GETKRZYWA() {
+ _vm->getCurve();
+ debugInterpreter("O_GETKRZYWA");
+}
+
+void Interpreter::O_GETMOB() {
+ Flags::Id flagId = readScriptFlagId();
+ int32 posX = readScriptFlagValue();
+ int32 posY = readScriptFlagValue();
+ int mobNumber = _vm->getMob(_vm->_mobList, true, posX, posY);
+ _flags->setFlagValue(flagId, mobNumber + 1);
+ debugInterpreter("O_GETMOB flagId %d, posX %d, posY %d", flagId, posX, posY);
+}
+
+// Not used in game
+void Interpreter::O_INPUTLINE() {
+ error("O_INPUTLINE");
+}
+
+// Not used in script
+void Interpreter::O_BREAK_POINT() {
+ error("O_BREAK_POINT");
+}
+
+Interpreter::OpcodeFunc Interpreter::_opcodes[kNumOpcodes] = {
+ &Interpreter::O_WAITFOREVER,
+ &Interpreter::O_BLACKPALETTE,
+ &Interpreter::O_SETUPPALETTE,
+ &Interpreter::O_INITROOM,
+ &Interpreter::O_SETSAMPLE,
+ &Interpreter::O_FREESAMPLE,
+ &Interpreter::O_PLAYSAMPLE,
+ &Interpreter::O_PUTOBJECT,
+ &Interpreter::O_REMOBJECT,
+ &Interpreter::O_SHOWANIM,
+ &Interpreter::O_CHECKANIMEND,
+ &Interpreter::O_FREEANIM,
+ &Interpreter::O_CHECKANIMFRAME,
+ &Interpreter::O_PUTBACKANIM,
+ &Interpreter::O_REMBACKANIM,
+ &Interpreter::O_CHECKBACKANIMFRAME,
+ &Interpreter::O_FREEALLSAMPLES,
+ &Interpreter::O_SETMUSIC,
+ &Interpreter::O_STOPMUSIC,
+ &Interpreter::O__WAIT,
+ &Interpreter::O_UPDATEOFF,
+ &Interpreter::O_UPDATEON,
+ &Interpreter::O_UPDATE ,
+ &Interpreter::O_CLS,
+ &Interpreter::O__CALL,
+ &Interpreter::O_RETURN,
+ &Interpreter::O_GO,
+ &Interpreter::O_BACKANIMUPDATEOFF,
+ &Interpreter::O_BACKANIMUPDATEON,
+ &Interpreter::O_CHANGECURSOR,
+ &Interpreter::O_CHANGEANIMTYPE,
+ &Interpreter::O__SETFLAG,
+ &Interpreter::O_COMPARE,
+ &Interpreter::O_JUMPZ,
+ &Interpreter::O_JUMPNZ,
+ &Interpreter::O_EXIT,
+ &Interpreter::O_ADDFLAG,
+ &Interpreter::O_TALKANIM,
+ &Interpreter::O_SUBFLAG,
+ &Interpreter::O_SETSTRING,
+ &Interpreter::O_ANDFLAG,
+ &Interpreter::O_GETMOBDATA,
+ &Interpreter::O_ORFLAG,
+ &Interpreter::O_SETMOBDATA,
+ &Interpreter::O_XORFLAG,
+ &Interpreter::O_GETMOBTEXT,
+ &Interpreter::O_MOVEHERO,
+ &Interpreter::O_WALKHERO,
+ &Interpreter::O_SETHERO,
+ &Interpreter::O_HEROOFF,
+ &Interpreter::O_HEROON,
+ &Interpreter::O_CLSTEXT,
+ &Interpreter::O_CALLTABLE,
+ &Interpreter::O_CHANGEMOB,
+ &Interpreter::O_ADDINV,
+ &Interpreter::O_REMINV,
+ &Interpreter::O_REPINV,
+ &Interpreter::O_OBSOLETE_GETACTION,
+ &Interpreter::O_ADDWALKAREA,
+ &Interpreter::O_REMWALKAREA,
+ &Interpreter::O_RESTOREWALKAREA,
+ &Interpreter::O_WAITFRAME,
+ &Interpreter::O_SETFRAME,
+ &Interpreter::O_RUNACTION,
+ &Interpreter::O_COMPAREHI,
+ &Interpreter::O_COMPARELO,
+ &Interpreter::O_PRELOADSET,
+ &Interpreter::O_FREEPRELOAD,
+ &Interpreter::O_CHECKINV,
+ &Interpreter::O_TALKHERO,
+ &Interpreter::O_WAITTEXT,
+ &Interpreter::O_SETHEROANIM,
+ &Interpreter::O_WAITHEROANIM,
+ &Interpreter::O_GETHERODATA,
+ &Interpreter::O_GETMOUSEBUTTON,
+ &Interpreter::O_CHANGEFRAMES,
+ &Interpreter::O_CHANGEBACKFRAMES,
+ &Interpreter::O_GETBACKANIMDATA,
+ &Interpreter::O_GETANIMDATA,
+ &Interpreter::O_SETBGCODE,
+ &Interpreter::O_SETBACKFRAME,
+ &Interpreter::O_GETRND,
+ &Interpreter::O_TALKBACKANIM,
+ &Interpreter::O_LOADPATH,
+ &Interpreter::O_GETCHAR,
+ &Interpreter::O_SETDFLAG,
+ &Interpreter::O_CALLDFLAG,
+ &Interpreter::O_PRINTAT,
+ &Interpreter::O_ZOOMIN,
+ &Interpreter::O_ZOOMOUT,
+ &Interpreter::O_SETSTRINGOFFSET,
+ &Interpreter::O_GETOBJDATA,
+ &Interpreter::O_SETOBJDATA,
+ &Interpreter::O_SWAPOBJECTS,
+ &Interpreter::O_CHANGEHEROSET,
+ &Interpreter::O_ADDSTRING,
+ &Interpreter::O_SUBSTRING,
+ &Interpreter::O_INITDIALOG,
+ &Interpreter::O_ENABLEDIALOGOPT,
+ &Interpreter::O_DISABLEDIALOGOPT,
+ &Interpreter::O_SHOWDIALOGBOX,
+ &Interpreter::O_STOPSAMPLE,
+ &Interpreter::O_BACKANIMRANGE,
+ &Interpreter::O_CLEARPATH,
+ &Interpreter::O_SETPATH,
+ &Interpreter::O_GETHEROX,
+ &Interpreter::O_GETHEROY,
+ &Interpreter::O_GETHEROD,
+ &Interpreter::O_PUSHSTRING,
+ &Interpreter::O_POPSTRING,
+ &Interpreter::O_SETFGCODE,
+ &Interpreter::O_STOPHERO,
+ &Interpreter::O_ANIMUPDATEOFF,
+ &Interpreter::O_ANIMUPDATEON,
+ &Interpreter::O_FREECURSOR,
+ &Interpreter::O_ADDINVQUIET,
+ &Interpreter::O_RUNHERO,
+ &Interpreter::O_SETBACKANIMDATA,
+ &Interpreter::O_VIEWFLC,
+ &Interpreter::O_CHECKFLCFRAME,
+ &Interpreter::O_CHECKFLCEND,
+ &Interpreter::O_FREEFLC,
+ &Interpreter::O_TALKHEROSTOP,
+ &Interpreter::O_HEROCOLOR,
+ &Interpreter::O_GRABMAPA,
+ &Interpreter::O_ENABLENAK,
+ &Interpreter::O_DISABLENAK,
+ &Interpreter::O_GETMOBNAME,
+ &Interpreter::O_SWAPINVENTORY,
+ &Interpreter::O_CLEARINVENTORY,
+ &Interpreter::O_SKIPTEXT,
+ &Interpreter::O_SETVOICEH,
+ &Interpreter::O_SETVOICEA,
+ &Interpreter::O_SETVOICEB,
+ &Interpreter::O_SETVOICEC,
+ &Interpreter::O_VIEWFLCLOOP,
+ &Interpreter::O_FLCSPEED,
+ &Interpreter::O_OPENINVENTORY,
+ &Interpreter::O_KRZYWA,
+ &Interpreter::O_GETKRZYWA,
+ &Interpreter::O_GETMOB,
+ &Interpreter::O_INPUTLINE,
+ &Interpreter::O_SETVOICED,
+ &Interpreter::O_BREAK_POINT,
+};
+
+} // End of namespace Prince
diff --git a/engines/prince/script.h b/engines/prince/script.h
new file mode 100644
index 0000000000..4799e84944
--- /dev/null
+++ b/engines/prince/script.h
@@ -0,0 +1,397 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_SCRIPT_H
+#define PRINCE_SCRIPT_H
+
+#include "common/random.h"
+#include "common/endian.h"
+#include "common/array.h"
+#include "common/stream.h"
+
+#include "prince/flags.h"
+
+namespace Prince {
+
+class PrinceEngine;
+class Animation;
+class Object;
+struct Anim;
+struct BackgroundAnim;
+struct Mask;
+
+class Room {
+public:
+ Room();
+ int _mobs; // mob flag offset
+ int _backAnim; // offset to array of animation numbers
+ int _obj; // offset to array of object numbers
+ int _nak; // offset to array of masks
+ int _itemUse;
+ int _itemGive;
+ int _walkTo; // offset to array of WALKTO events or 0
+ int _examine; // offset to array of EXAMINE events or 0
+ int _pickup;
+ int _use;
+ int _pushOpen;
+ int _pullClose;
+ int _talk;
+ int _give;
+
+ bool loadStream(Common::SeekableReadStream &stream);
+ bool loadRoom(byte *roomData);
+ int getOptionOffset(int option);
+
+private:
+
+ typedef void (Room::*LoadingStep)(Common::SeekableReadStream &stream);
+
+ void nextLoadStep(Common::SeekableReadStream &stream, LoadingStep step);
+
+ void loadMobs(Common::SeekableReadStream &stream);
+ void loadBackAnim(Common::SeekableReadStream &stream);
+ void loadObj(Common::SeekableReadStream &stream);
+ void loadNak(Common::SeekableReadStream &stream);
+ void loadItemUse(Common::SeekableReadStream &stream);
+ void loadItemGive(Common::SeekableReadStream &stream);
+ void loadWalkTo(Common::SeekableReadStream &stream);
+ void loadExamine(Common::SeekableReadStream &stream);
+ void loadPickup(Common::SeekableReadStream &stream);
+ void loadUse(Common::SeekableReadStream &stream);
+ void loadPushOpen(Common::SeekableReadStream &stream);
+ void loadPullClose(Common::SeekableReadStream &stream);
+ void loadTalk(Common::SeekableReadStream &stream);
+ void loadGive(Common::SeekableReadStream &stream);
+};
+
+
+class Script {
+public:
+ static const int16 kMaxRooms = 60;
+
+ Script(PrinceEngine *vm);
+ ~Script();
+
+ struct ScriptInfo {
+ int rooms;
+ int startGame;
+ int restoreGame;
+ int stdExamine;
+ int stdPickup;
+ int stdUse;
+ int stdOpen;
+ int stdClose;
+ int stdTalk;
+ int stdGive;
+ int usdCode;
+ int invObjExam;
+ int invObjUse;
+ int invObjUU;
+ int stdUseItem;
+ int lightSources;
+ int specRout;
+ int invObjGive;
+ int stdGiveItem;
+ int goTester;
+ };
+
+ ScriptInfo _scriptInfo;
+
+ bool loadStream(Common::SeekableReadStream &stream);
+
+ uint16 readScript16(uint32 address);
+ uint32 readScript32(uint32 address);
+
+ uint32 getStartGameOffset();
+ uint32 getLocationInitScript(int initRoomTableOffset, int roomNr);
+ int16 getLightX(int locationNr);
+ int16 getLightY(int locationNr);
+ int32 getShadowScale(int locationNr);
+ uint8 *getRoomOffset(int locationNr);
+ int32 getOptionStandardOffset(int option);
+ uint8 *getHeroAnimName(int offset);
+
+ void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset);
+ void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset);
+ void installObjects(int offset);
+ bool loadAllMasks(Common::Array<Mask> &maskList, int offset);
+
+ int scanMobEvents(int mobMask, int dataEventOffset);
+ int scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask);
+
+ byte getMobVisible(int roomMobOffset, uint16 mob);
+ void setMobVisible(int roomMobOffset, uint16 mob, byte value);
+
+ uint32 getBackAnimId(int roomBackAnimOffset, int slot);
+ void setBackAnimId(int roomBackAnimOffset, int slot, int animId);
+
+ byte getObjId(int roomObjOffset, int slot);
+ void setObjId(int roomObjOffset, int slot, byte objectId);
+
+ const char *getString(uint32 offset) {
+ return (const char *)(&_data[offset]);
+ }
+
+private:
+ PrinceEngine *_vm;
+ uint8 *_data;
+ uint32 _dataSize;
+ Common::Array<Room> _roomList;
+};
+
+class InterpreterFlags {
+public:
+ InterpreterFlags();
+
+ void setFlagValue(Flags::Id flag, int32 value);
+ int32 getFlagValue(Flags::Id flag);
+
+ void resetAllFlags();
+
+ static const uint16 kFlagMask = 0x8000;
+ static const uint16 kMaxFlags = 2000;
+private:
+ int32 _flags[kMaxFlags];
+};
+
+class Interpreter {
+public:
+ Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags);
+
+ void stopBg() { _bgOpcodePC = 0; }
+
+ void stepBg();
+ void stepFg();
+ void storeNewPC(int opcodePC);
+ int getLastOPCode();
+ int getFgOpcodePC();
+
+ void setBgOpcodePC(uint32 value);
+ void setFgOpcodePC(uint32 value);
+
+ uint32 getCurrentString();
+ void setCurrentString(uint32 value);
+
+ byte *getString();
+ void setString(byte *newString);
+ void increaseString();
+
+ void setResult(byte value);
+
+private:
+ PrinceEngine *_vm;
+ Script *_script;
+ InterpreterFlags *_flags;
+
+ uint32 _currentInstruction;
+
+ uint32 _bgOpcodePC;
+ uint32 _fgOpcodePC;
+
+ uint16 _lastOpcode;
+ uint32 _lastInstruction;
+ byte _result;
+
+ bool _opcodeNF; // break interpreter loop
+ bool _opcodeEnd; // end of a game flag
+
+ static const uint32 _STACK_SIZE = 500;
+ uint32 _stack[_STACK_SIZE];
+ struct stringStack {
+ byte *string;
+ byte *dialogData;
+ uint32 currentString;
+ } _stringStack;
+ uint8 _stacktop;
+ uint32 _waitFlag;
+
+ byte *_string;
+ uint32 _currentString;
+ const char *_mode;
+
+ // Helper functions
+ uint32 step(uint32 opcodePC);
+ uint16 readScript16();
+ uint32 readScript32();
+ int32 readScriptFlagValue();
+ Flags::Id readScriptFlagId();
+ int checkSeq(byte *string);
+
+ void debugInterpreter(const char *s, ...);
+
+ typedef void (Interpreter::*OpcodeFunc)();
+ static OpcodeFunc _opcodes[];
+
+ static const int kGiveLetterScriptFix = 79002;
+
+ // Keep opcode handlers names as they are in original code
+ // making it easier to switch back and forth
+ void O_WAITFOREVER();
+ void O_BLACKPALETTE();
+ void O_SETUPPALETTE();
+ void O_INITROOM();
+ void O_SETSAMPLE();
+ void O_FREESAMPLE();
+ void O_PLAYSAMPLE();
+ void O_PUTOBJECT();
+ void O_REMOBJECT();
+ void O_SHOWANIM();
+ void O_CHECKANIMEND();
+ void O_FREEANIM();
+ void O_CHECKANIMFRAME();
+ void O_PUTBACKANIM();
+ void O_REMBACKANIM();
+ void O_CHECKBACKANIMFRAME();
+ void O_FREEALLSAMPLES();
+ void O_SETMUSIC();
+ void O_STOPMUSIC();
+ void O__WAIT();
+ void O_UPDATEOFF();
+ void O_UPDATEON();
+ void O_UPDATE ();
+ void O_CLS();
+ void O__CALL();
+ void O_RETURN();
+ void O_GO();
+ void O_BACKANIMUPDATEOFF();
+ void O_BACKANIMUPDATEON();
+ void O_CHANGECURSOR();
+ void O_CHANGEANIMTYPE();
+ void O__SETFLAG();
+ void O_COMPARE();
+ void O_JUMPZ();
+ void O_JUMPNZ();
+ void O_EXIT();
+ void O_ADDFLAG();
+ void O_TALKANIM();
+ void O_SUBFLAG();
+ void O_SETSTRING();
+ void O_ANDFLAG();
+ void O_GETMOBDATA();
+ void O_ORFLAG();
+ void O_SETMOBDATA();
+ void O_XORFLAG();
+ void O_GETMOBTEXT();
+ void O_MOVEHERO();
+ void O_WALKHERO();
+ void O_SETHERO();
+ void O_HEROOFF();
+ void O_HEROON();
+ void O_CLSTEXT();
+ void O_CALLTABLE();
+ void O_CHANGEMOB();
+ void O_ADDINV();
+ void O_REMINV();
+ void O_REPINV();
+ void O_OBSOLETE_GETACTION();
+ void O_ADDWALKAREA();
+ void O_REMWALKAREA();
+ void O_RESTOREWALKAREA();
+ void O_WAITFRAME();
+ void O_SETFRAME();
+ void O_RUNACTION();
+ void O_COMPAREHI();
+ void O_COMPARELO();
+ void O_PRELOADSET();
+ void O_FREEPRELOAD();
+ void O_CHECKINV();
+ void O_TALKHERO();
+ void O_WAITTEXT();
+ void O_SETHEROANIM();
+ void O_WAITHEROANIM();
+ void O_GETHERODATA();
+ void O_GETMOUSEBUTTON();
+ void O_CHANGEFRAMES();
+ void O_CHANGEBACKFRAMES();
+ void O_GETBACKANIMDATA();
+ void O_GETANIMDATA();
+ void O_SETBGCODE();
+ void O_SETBACKFRAME();
+ void O_GETRND();
+ void O_TALKBACKANIM();
+ void O_LOADPATH();
+ void O_GETCHAR();
+ void O_SETDFLAG();
+ void O_CALLDFLAG();
+ void O_PRINTAT();
+ void O_ZOOMIN();
+ void O_ZOOMOUT();
+ void O_SETSTRINGOFFSET();
+ void O_GETOBJDATA();
+ void O_SETOBJDATA();
+ void O_SWAPOBJECTS();
+ void O_CHANGEHEROSET();
+ void O_ADDSTRING();
+ void O_SUBSTRING();
+ void O_INITDIALOG();
+ void O_ENABLEDIALOGOPT();
+ void O_DISABLEDIALOGOPT();
+ void O_SHOWDIALOGBOX();
+ void O_STOPSAMPLE();
+ void O_BACKANIMRANGE();
+ void O_CLEARPATH();
+ void O_SETPATH();
+ void O_GETHEROX();
+ void O_GETHEROY();
+ void O_GETHEROD();
+ void O_PUSHSTRING();
+ void O_POPSTRING();
+ void O_SETFGCODE();
+ void O_STOPHERO();
+ void O_ANIMUPDATEOFF();
+ void O_ANIMUPDATEON();
+ void O_FREECURSOR();
+ void O_ADDINVQUIET();
+ void O_RUNHERO();
+ void O_SETBACKANIMDATA();
+ void O_VIEWFLC();
+ void O_CHECKFLCFRAME();
+ void O_CHECKFLCEND();
+ void O_FREEFLC();
+ void O_TALKHEROSTOP();
+ void O_HEROCOLOR();
+ void O_GRABMAPA();
+ void O_ENABLENAK();
+ void O_DISABLENAK();
+ void O_GETMOBNAME();
+ void O_SWAPINVENTORY();
+ void O_CLEARINVENTORY();
+ void O_SKIPTEXT();
+ void O_SETVOICEH();
+ void O_SETVOICEA();
+ void O_SETVOICEB();
+ void O_SETVOICEC();
+ void O_VIEWFLCLOOP();
+ void O_FLCSPEED();
+ void O_OPENINVENTORY();
+ void O_KRZYWA();
+ void O_GETKRZYWA();
+ void O_GETMOB();
+ void O_INPUTLINE();
+ void O_SETVOICED();
+ void O_BREAK_POINT();
+
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/sound.cpp b/engines/prince/sound.cpp
new file mode 100644
index 0000000000..032297ee43
--- /dev/null
+++ b/engines/prince/sound.cpp
@@ -0,0 +1,211 @@
+/* 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 "prince/prince.h"
+#include "prince/sound.h"
+#include "prince/musNum.h"
+
+#include "common/config-manager.h"
+#include "common/memstream.h"
+#include "common/archive.h"
+#include "audio/decoders/raw.h"
+#include "audio/audiostream.h"
+
+namespace Prince {
+
+const char *MusicPlayer::_musTable[] = {
+ "",
+ "Battlfld.mid",
+ "Cave.mid",
+ "Cemetery.mid",
+ "Credits.mid",
+ "Fjord.mid",
+ "Guitar.mid",
+ "Hell.mid",
+ "Jingle.mid",
+ "Main.mid",
+ "Night.mid",
+ "Reality.mid",
+ "Sunlord.mid",
+ "Tavern.mid",
+ "Temple.mid",
+ "Boruta.mid",
+ "Intro.mid"
+};
+
+const uint8 MusicPlayer::_musRoomTable[] = {
+ 0,
+ ROOM01MUS,
+ ROOM02MUS,
+ ROOM03MUS,
+ ROOM04MUS,
+ ROOM05MUS,
+ ROOM06MUS,
+ ROOM07MUS,
+ ROOM08MUS,
+ ROOM09MUS,
+ ROOM10MUS,
+ ROOM11MUS,
+ ROOM12MUS,
+ ROOM13MUS,
+ ROOM14MUS,
+ ROOM15MUS,
+ ROOM16MUS,
+ ROOM17MUS,
+ ROOM18MUS,
+ ROOM19MUS,
+ ROOM20MUS,
+ ROOM21MUS,
+ ROOM22MUS,
+ ROOM23MUS,
+ ROOM24MUS,
+ ROOM25MUS,
+ ROOM26MUS,
+ ROOM27MUS,
+ ROOM28MUS,
+ ROOM29MUS,
+ ROOM30MUS,
+ ROOM31MUS,
+ ROOM32MUS,
+ ROOM33MUS,
+ ROOM34MUS,
+ ROOM35MUS,
+ ROOM36MUS,
+ ROOM37MUS,
+ ROOM38MUS,
+ ROOM39MUS,
+ ROOM40MUS,
+ ROOM41MUS,
+ ROOM42MUS,
+ ROOM43MUS,
+ 0,
+ 0,
+ ROOM46MUS,
+ ROOM47MUS,
+ ROOM48MUS,
+ ROOM49MUS,
+ ROOM50MUS,
+ ROOM51MUS,
+ ROOM52MUS,
+ ROOM53MUS,
+ ROOM54MUS,
+ ROOM55MUS,
+ ROOM56MUS,
+ ROOM57MUS,
+ ROOM58MUS,
+ ROOM59MUS,
+ ROOM60MUS,
+ ROOM61MUS
+};
+
+
+MusicPlayer::MusicPlayer(PrinceEngine *vm) : _vm(vm) {
+ _data = nullptr;
+ _isGM = false;
+
+ MidiPlayer::createDriver();
+
+ int ret = _driver->open();
+ if (ret == 0) {
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
+ _driver->setTimerCallback(this, &timerCallback);
+ }
+}
+
+MusicPlayer::~MusicPlayer() {
+ killMidi();
+}
+
+void MusicPlayer::killMidi() {
+ Audio::MidiPlayer::stop();
+
+ free(_data);
+ _data = nullptr;
+}
+
+void MusicPlayer::loadMidi(const char *name) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name);
+ if (!stream) {
+ debug("Can't load midi stream %s", name);
+ return;
+ }
+
+ // Stop any currently playing MIDI file
+ killMidi();
+
+ // Read in the data for the file
+ _dataSize = stream->size();
+ _data = (byte *)malloc(_dataSize);
+ stream->read(_data, _dataSize);
+
+ delete stream;
+
+ // Start playing the music
+ sndMidiStart();
+}
+
+void MusicPlayer::sndMidiStart() {
+ _isGM = true;
+
+ MidiParser *parser = MidiParser::createParser_SMF();
+ if (parser->loadMusic(_data, _dataSize)) {
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+ _parser = parser;
+
+ syncVolume();
+
+ // Al the tracks are supposed to loop
+ _isLooping = true;
+ _isPlaying = true;
+ }
+}
+
+void MusicPlayer::send(uint32 b) {
+ if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+
+ Audio::MidiPlayer::send(b);
+}
+
+void MusicPlayer::sendToChannel(byte channel, uint32 b) {
+ if (!_channelsTable[channel]) {
+ _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ // If a new channel is allocated during the playback, make sure
+ // its volume is correctly initialized.
+ if (_channelsTable[channel])
+ _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
+ }
+
+ if (_channelsTable[channel])
+ _channelsTable[channel]->send(b);
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/sound.h b/engines/prince/sound.h
new file mode 100644
index 0000000000..cc44b0a110
--- /dev/null
+++ b/engines/prince/sound.h
@@ -0,0 +1,67 @@
+/* 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.
+ *
+ */
+
+#ifndef PRINCE_SOUND_H
+#define PRINCE_SOUND_H
+
+#include "audio/audiostream.h"
+#include "audio/decoders/wave.h"
+#include "audio/fmopl.h"
+#include "audio/mididrv.h"
+#include "audio/midiparser.h"
+#include "audio/midiplayer.h"
+#include "audio/mixer.h"
+#include "common/memstream.h"
+
+namespace Prince {
+
+class PrinceEngine;
+
+class MusicPlayer: public Audio::MidiPlayer {
+private:
+ PrinceEngine *_vm;
+ byte *_data;
+ int _dataSize;
+ bool _isGM;
+
+ // Start MIDI File
+ void sndMidiStart();
+
+ // Stop MIDI File
+ void sndMidiStop();
+public:
+ MusicPlayer(PrinceEngine *vm);
+ ~MusicPlayer();
+
+ void loadMidi(const char *);
+ void killMidi();
+
+ virtual void send(uint32 b);
+ virtual void sendToChannel(byte channel, uint32 b);
+
+ static const char *_musTable[];
+ static const uint8 _musRoomTable[];
+};
+
+} // End of namespace Prince
+
+#endif
diff --git a/engines/prince/variatxt.cpp b/engines/prince/variatxt.cpp
new file mode 100644
index 0000000000..c38c65ce5d
--- /dev/null
+++ b/engines/prince/variatxt.cpp
@@ -0,0 +1,55 @@
+/* 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 "prince/variatxt.h"
+#include "common/debug.h"
+
+namespace Prince {
+
+VariaTxt::VariaTxt() : _dataSize(0), _data(nullptr) {
+}
+
+VariaTxt::~VariaTxt() {
+ _dataSize = 0;
+ if (_data != nullptr) {
+ free(_data);
+ _data = nullptr;
+ }
+}
+
+
+bool VariaTxt::loadStream(Common::SeekableReadStream &stream) {
+ _dataSize = stream.size();
+ _data = (byte *)malloc(_dataSize);
+ stream.read(_data, _dataSize);
+ return true;
+}
+
+byte *VariaTxt::getString(uint32 stringId) {
+ uint32 stringOffset = READ_LE_UINT32(_data + stringId * 4);
+ if (stringOffset > _dataSize) {
+ assert(false);
+ }
+ return _data + stringOffset;
+}
+
+} // End of namespace Prince
diff --git a/engines/prince/variatxt.h b/engines/prince/variatxt.h
new file mode 100644
index 0000000000..04c34bc0ef
--- /dev/null
+++ b/engines/prince/variatxt.h
@@ -0,0 +1,40 @@
+/* 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/stream.h"
+
+namespace Prince {
+
+class VariaTxt {
+public:
+ VariaTxt();
+ ~VariaTxt();
+
+ bool loadStream(Common::SeekableReadStream &stream);
+ byte *getString(uint32 stringId);
+
+private:
+ uint32 _dataSize;
+ byte *_data;
+};
+
+} // End of namespace Prince