diff options
author | Eugene Sandulenko | 2014-10-10 13:49:05 +0200 |
---|---|---|
committer | Eugene Sandulenko | 2014-10-10 13:49:05 +0200 |
commit | bb8f66c2437a0f3a704ddc84e08dab7551b08253 (patch) | |
tree | a8167e4c8df92cb80004a6314fe31352330a217f | |
parent | c128365068660af764b4fdfc9e5256a7caed3839 (diff) | |
parent | c769c3b8855820122e6a7a6712cb7139445d8a0b (diff) | |
download | scummvm-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
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 |