diff --git a/engines/zvision/animation/meta_animation.cpp b/engines/zvision/animation/meta_animation.cpp new file mode 100644 index 000000000000..2e549ec83866 --- /dev/null +++ b/engines/zvision/animation/meta_animation.cpp @@ -0,0 +1,133 @@ +/* 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 "zvision/animation/meta_animation.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/animation/rlf_animation.h" +#include "zvision/video/zork_avi_decoder.h" + +#include "video/video_decoder.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +MetaAnimation::MetaAnimation(const Common::String &fileName, ZVision *engine) + : _fileType(RLF), + _curFrame(NULL) { + Common::String tmpFileName = fileName; + tmpFileName.toLowercase(); + if (tmpFileName.hasSuffix(".rlf")) { + _fileType = RLF; + Common::File *_file = engine->getSearchManager()->openFile(tmpFileName); + _animation.rlf = new RlfAnimation(_file, false); + _frmDelay = _animation.rlf->frameTime(); + } else if (tmpFileName.hasSuffix(".avi")) { + _fileType = AVI; + Common::File *_file = engine->getSearchManager()->openFile(tmpFileName); + _animation.avi = new ZorkAVIDecoder(); + _animation.avi->loadStream(_file); + _frmDelay = 1000.0 / _animation.avi->getDuration().framerate(); + } else { + warning("Unrecognized animation file type: %s", fileName.c_str()); + } +} + +MetaAnimation::~MetaAnimation() { + if (_fileType == RLF) { + delete _animation.rlf; + } else if (_fileType == AVI) { + delete _animation.avi; + } +} + +uint MetaAnimation::frameCount() { + if (_fileType == RLF) { + return _animation.rlf->frameCount(); + } else + return _animation.avi->getFrameCount(); + +} + +uint MetaAnimation::width() { + if (_fileType == RLF) { + return _animation.rlf->width(); + } else + return _animation.avi->getWidth(); +} +uint MetaAnimation::height() { + if (_fileType == RLF) { + return _animation.rlf->height(); + } else + return _animation.avi->getHeight(); +} +uint32 MetaAnimation::frameTime() { + return _frmDelay; +} + +void MetaAnimation::seekToFrame(int frameNumber) { + if (frameNumber >= (int)frameCount()) + frameNumber = frameCount() - 1; + + if (_fileType == RLF) { + _animation.rlf->seekToFrame(frameNumber); + } else + _animation.avi->seekToFrame(frameNumber); +} + +const Graphics::Surface *MetaAnimation::decodeNextFrame() { + if (_fileType == RLF) + _curFrame = _animation.rlf->decodeNextFrame(); + else + _curFrame = _animation.avi->decodeNextFrame(); + + return _curFrame; +} + +const Graphics::Surface *MetaAnimation::getFrameData(uint frameNumber) { + if (frameNumber >= frameCount()) + frameNumber = frameCount() - 1; + + if (_fileType == RLF) { + _curFrame = _animation.rlf->getFrameData(frameNumber); + return _curFrame; + } else { + _animation.avi->seekToFrame(frameNumber); + _curFrame = _animation.avi->decodeNextFrame(); + return _curFrame; + } +} + +bool MetaAnimation::endOfAnimation() { + if (_fileType == RLF) { + return _animation.rlf->endOfAnimation(); + } else + return _animation.avi->endOfVideo(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/animation_control.h b/engines/zvision/animation/meta_animation.h similarity index 64% rename from engines/zvision/scripting/controls/animation_control.h rename to engines/zvision/animation/meta_animation.h index bdb07d39ea88..4795a388e466 100644 --- a/engines/zvision/scripting/controls/animation_control.h +++ b/engines/zvision/animation/meta_animation.h @@ -8,22 +8,25 @@ * 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 ZVISION_ANIMATION_CONTROL_H -#define ZVISION_ANIMATION_CONTROL_H +#ifndef ZVISION_METAANIM_NODE_H +#define ZVISION_METAANIM_NODE_H -#include "zvision/scripting/control.h" +#include "zvision/scripting/sidefx.h" +#include "zvision/zvision.h" +#include "common/rect.h" +#include "common/list.h" namespace Common { @@ -43,10 +46,21 @@ namespace ZVision { class ZVision; class RlfAnimation; -class AnimationControl : public Control { +class MetaAnimation { public: - AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName); - ~AnimationControl(); + MetaAnimation(const Common::String &fileName, ZVision *engine); + ~MetaAnimation(); + + struct playnode { + Common::Rect pos; + int32 slot; + int32 start; + int32 stop; + int32 loop; + int32 _curFrame; + int32 _delay; + Graphics::Surface *_scaled; + }; private: enum FileType { @@ -55,31 +69,29 @@ class AnimationControl : public Control { }; private: - uint32 _animationKey; - union { RlfAnimation *rlf; Video::VideoDecoder *avi; } _animation; FileType _fileType; - uint _loopCount; - int32 _x; - int32 _y; - - uint _accumulatedTime; - uint _currentLoop; + int32 _frmDelay; - Graphics::Surface *_cachedFrame; - bool _cachedFrameNeedsDeletion; + const Graphics::Surface *_curFrame; public: - bool process(uint32 deltaTimeInMillis); - void setAnimationKey(uint32 animationKey) { _animationKey = animationKey; } - void setLoopCount(uint loopCount) { _loopCount = loopCount; } - void setXPos(int32 x) { _x = x; } - void setYPost(int32 y) { _y = y; } + uint frameCount(); + uint width(); + uint height(); + uint32 frameTime(); + + void seekToFrame(int frameNumber); + + const Graphics::Surface *decodeNextFrame(); + const Graphics::Surface *getFrameData(uint frameNumber); + + bool endOfAnimation(); }; } // End of namespace ZVision diff --git a/engines/zvision/animation/rlf_animation.cpp b/engines/zvision/animation/rlf_animation.cpp index 945461e7b22f..5972bdb3efe1 100644 --- a/engines/zvision/animation/rlf_animation.cpp +++ b/engines/zvision/animation/rlf_animation.cpp @@ -1,24 +1,24 @@ /* 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. - * - */ +* +* 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" @@ -36,20 +36,25 @@ namespace ZVision { RlfAnimation::RlfAnimation(const Common::String &fileName, bool stream) - : _stream(stream), - _lastFrameRead(0), - _frameCount(0), - _width(0), - _height(0), - _frameTime(0), - _frames(0), - _currentFrame(-1), - _frameBufferByteSize(0) { - if (!_file.open(fileName)) { + : _stream(stream), + _readStream(NULL), + _lastFrameRead(0), + _frameCount(0), + _width(0), + _height(0), + _frameTime(0), + _frames(0), + _nextFrame(0), + _frameBufferByteSize(0) { + + Common::File *_file = new Common::File; + if (!_file->open(fileName)) { warning("RLF animation file %s could not be opened", fileName.c_str()); return; } + _readStream = _file; + if (!readHeader()) { warning("%s is not a RLF animation file. Wrong magic number", fileName.c_str()); return; @@ -68,59 +73,90 @@ RlfAnimation::RlfAnimation(const Common::String &fileName, bool stream) } } +RlfAnimation::RlfAnimation(Common::SeekableReadStream *rstream, bool stream) + : _stream(stream), + _readStream(rstream), + _lastFrameRead(0), + _frameCount(0), + _width(0), + _height(0), + _frameTime(0), + _frames(0), + _nextFrame(0), + _frameBufferByteSize(0) { + + if (!readHeader()) { + warning("Stream is not a RLF animation. Wrong magic number"); + return; + } + + _currentFrameBuffer.create(_width, _height, Graphics::createPixelFormat<565>()); + _frameBufferByteSize = _width * _height * sizeof(uint16); + + if (!stream) { + _frames = new Frame[_frameCount]; + + // Read in each frame + for (uint i = 0; i < _frameCount; ++i) { + _frames[i] = readNextFrame(); + } + } +} + RlfAnimation::~RlfAnimation() { for (uint i = 0; i < _frameCount; ++i) { delete[] _frames[i].encodedData; } delete[] _frames; + delete _readStream; _currentFrameBuffer.free(); } bool RlfAnimation::readHeader() { - if (_file.readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { + if (_readStream->readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { return false; } // Read the header - _file.readUint32LE(); // Size1 - _file.readUint32LE(); // Unknown1 - _file.readUint32LE(); // Unknown2 - _frameCount = _file.readUint32LE(); // Frame count + _readStream->readUint32LE(); // Size1 + _readStream->readUint32LE(); // Unknown1 + _readStream->readUint32LE(); // Unknown2 + _frameCount = _readStream->readUint32LE(); // Frame count // Since we don't need any of the data, we can just seek right to the // entries we need rather than read in all the individual entries. - _file.seek(136, SEEK_CUR); + _readStream->seek(136, SEEK_CUR); //// Read CIN header - //_file.readUint32BE(); // Magic number FNIC - //_file.readUint32LE(); // Size2 - //_file.readUint32LE(); // Unknown3 - //_file.readUint32LE(); // Unknown4 - //_file.readUint32LE(); // Unknown5 - //_file.seek(0x18, SEEK_CUR); // VRLE - //_file.readUint32LE(); // LRVD - //_file.readUint32LE(); // Unknown6 - //_file.seek(0x18, SEEK_CUR); // HRLE - //_file.readUint32LE(); // ELHD - //_file.readUint32LE(); // Unknown7 - //_file.seek(0x18, SEEK_CUR); // HKEY - //_file.readUint32LE(); // ELRH + //_readStream->readUint32BE(); // Magic number FNIC + //_readStream->readUint32LE(); // Size2 + //_readStream->readUint32LE(); // Unknown3 + //_readStream->readUint32LE(); // Unknown4 + //_readStream->readUint32LE(); // Unknown5 + //_readStream->seek(0x18, SEEK_CUR); // VRLE + //_readStream->readUint32LE(); // LRVD + //_readStream->readUint32LE(); // Unknown6 + //_readStream->seek(0x18, SEEK_CUR); // HRLE + //_readStream->readUint32LE(); // ELHD + //_readStream->readUint32LE(); // Unknown7 + //_readStream->seek(0x18, SEEK_CUR); // HKEY + //_readStream->readUint32LE(); // ELRH //// Read MIN info header - //_file.readUint32BE(); // Magic number FNIM - //_file.readUint32LE(); // Size3 - //_file.readUint32LE(); // OEDV - //_file.readUint32LE(); // Unknown8 - //_file.readUint32LE(); // Unknown9 - //_file.readUint32LE(); // Unknown10 - _width = _file.readUint32LE(); // Width - _height = _file.readUint32LE(); // Height + //_readStream->readUint32BE(); // Magic number FNIM + //_readStream->readUint32LE(); // Size3 + //_readStream->readUint32LE(); // OEDV + //_readStream->readUint32LE(); // Unknown8 + //_readStream->readUint32LE(); // Unknown9 + //_readStream->readUint32LE(); // Unknown10 + _width = _readStream->readUint32LE(); // Width + _height = _readStream->readUint32LE(); // Height // Read time header - _file.readUint32BE(); // Magic number EMIT - _file.readUint32LE(); // Size4 - _file.readUint32LE(); // Unknown11 - _frameTime = _file.readUint32LE() / 10; // Frame time in microseconds + _readStream->readUint32BE(); // Magic number EMIT + _readStream->readUint32LE(); // Size4 + _readStream->readUint32LE(); // Unknown11 + _frameTime = _readStream->readUint32LE() / 10; // Frame time in microseconds return true; } @@ -128,17 +164,17 @@ bool RlfAnimation::readHeader() { RlfAnimation::Frame RlfAnimation::readNextFrame() { RlfAnimation::Frame frame; - _file.readUint32BE(); // Magic number MARF - uint32 size = _file.readUint32LE(); // Size - _file.readUint32LE(); // Unknown1 - _file.readUint32LE(); // Unknown2 - uint32 type = _file.readUint32BE(); // Either ELHD or ELRH - uint32 headerSize = _file.readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 - _file.readUint32LE(); // Unknown3 + _readStream->readUint32BE(); // Magic number MARF + uint32 size = _readStream->readUint32LE(); // Size + _readStream->readUint32LE(); // Unknown1 + _readStream->readUint32LE(); // Unknown2 + uint32 type = _readStream->readUint32BE(); // Either ELHD or ELRH + uint32 headerSize = _readStream->readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 + _readStream->readUint32LE(); // Unknown3 frame.encodedSize = size - headerSize; frame.encodedData = new int8[frame.encodedSize]; - _file.read(frame.encodedData, frame.encodedSize); + _readStream->read(frame.encodedData, frame.encodedSize); if (type == MKTAG('E', 'L', 'H', 'D')) { frame.type = Masked; @@ -157,26 +193,40 @@ void RlfAnimation::seekToFrame(int frameNumber) { assert(!_stream); assert(frameNumber < (int)_frameCount || frameNumber >= -1); - if (frameNumber == -1) { - _currentFrame = -1; + if (_nextFrame == frameNumber) + return; + + if (frameNumber < 0) { + _nextFrame = 0; return; } - int closestFrame = _currentFrame; - int distance = (int)frameNumber - _currentFrame; - for (uint i = 0; i < _completeFrames.size(); ++i) { - int newDistance = (int)frameNumber - (int)(_completeFrames[i]); - if (newDistance > 0 && (closestFrame == -1 || newDistance < distance)) { + int closestFrame = _nextFrame; + int distance = (int)frameNumber - _nextFrame; + + if (distance < 0) { + for (uint i = 0; i < _completeFrames.size(); ++i) { + if ((int)_completeFrames[i] > frameNumber) + break; closestFrame = _completeFrames[i]; - distance = newDistance; + } + } else { + for (uint i = 0; i < _completeFrames.size(); ++i) { + int newDistance = (int)frameNumber - (int)(_completeFrames[i]); + if (newDistance < 0) + break; + if (newDistance < distance) { + closestFrame = _completeFrames[i]; + distance = newDistance; + } } } - for (int i = closestFrame; i <= frameNumber; ++i) { + for (int i = closestFrame; i < frameNumber; ++i) { applyFrameToCurrent(i); } - _currentFrame = frameNumber; + _nextFrame = frameNumber; } const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) { @@ -184,27 +234,27 @@ const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) { assert(frameNumber < _frameCount); // Since this method is so expensive, first check to see if we can use - // getNextFrame() it's cheap. - if ((int)frameNumber == _currentFrame) { + // decodeNextFrame() it's cheap. + if ((int)frameNumber == _nextFrame - 1) { return &_currentFrameBuffer; - } else if (_currentFrame + 1 == (int)frameNumber) { - return getNextFrame(); + } else if (_nextFrame == (int)frameNumber) { + return decodeNextFrame(); } seekToFrame(frameNumber); - return &_currentFrameBuffer; + return decodeNextFrame(); } -const Graphics::Surface *RlfAnimation::getNextFrame() { - assert(_currentFrame + 1 < (int)_frameCount); +const Graphics::Surface *RlfAnimation::decodeNextFrame() { + assert(_nextFrame < (int)_frameCount); if (_stream) { applyFrameToCurrent(readNextFrame()); } else { - applyFrameToCurrent(_currentFrame + 1); + applyFrameToCurrent(_nextFrame); } - _currentFrame++; + _nextFrame++; return &_currentFrameBuffer; } @@ -227,6 +277,7 @@ void RlfAnimation::applyFrameToCurrent(const RlfAnimation::Frame &frame) { void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { uint32 sourceOffset = 0; uint32 destOffset = 0; + int16 numberOfCopy = 0; while (sourceOffset < sourceSize) { int8 numberOfSamples = source[sourceOffset]; @@ -235,9 +286,9 @@ void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint3 // If numberOfSamples is negative, the next abs(numberOfSamples) samples should // be copied directly from source to dest if (numberOfSamples < 0) { - numberOfSamples = ABS(numberOfSamples); + numberOfCopy = -numberOfSamples; - while (numberOfSamples > 0) { + while (numberOfCopy > 0) { if (sourceOffset + 1 >= sourceSize) { return; } else if (destOffset + 1 >= destSize) { @@ -252,11 +303,11 @@ void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint3 sourceOffset += 2; destOffset += 2; - numberOfSamples--; + numberOfCopy--; } - // If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2) - // This function assumes the dest buffer has been memset with 0's. + // If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2) + // This function assumes the dest buffer has been memset with 0's. } else { if (sourceOffset + 1 >= sourceSize) { return; @@ -273,6 +324,7 @@ void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint3 void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { uint32 sourceOffset = 0; uint32 destOffset = 0; + int16 numberOfCopy = 0; while (sourceOffset < sourceSize) { int8 numberOfSamples = source[sourceOffset]; @@ -281,9 +333,9 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 // If numberOfSamples is negative, the next abs(numberOfSamples) samples should // be copied directly from source to dest if (numberOfSamples < 0) { - numberOfSamples = ABS(numberOfSamples); + numberOfCopy = -numberOfSamples; - while (numberOfSamples > 0) { + while (numberOfCopy > 0) { if (sourceOffset + 1 >= sourceSize) { return; } else if (destOffset + 1 >= destSize) { @@ -298,11 +350,11 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 sourceOffset += 2; destOffset += 2; - numberOfSamples--; + numberOfCopy--; } - // If numberOfSamples is >= 0, copy one sample from source to the - // next (numberOfSamples + 2) dest spots + // If numberOfSamples is >= 0, copy one sample from source to the + // next (numberOfSamples + 2) dest spots } else { if (sourceOffset + 1 >= sourceSize) { return; @@ -313,8 +365,8 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 uint16 sampleColor = Graphics::RGBToColor >(r, g, b); sourceOffset += 2; - numberOfSamples += 2; - while (numberOfSamples > 0) { + numberOfCopy = numberOfSamples + 2; + while (numberOfCopy > 0) { if (destOffset + 1 >= destSize) { debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); return; @@ -322,7 +374,7 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 WRITE_UINT16(dest + destOffset, sampleColor); destOffset += 2; - numberOfSamples--; + numberOfCopy--; } } } diff --git a/engines/zvision/animation/rlf_animation.h b/engines/zvision/animation/rlf_animation.h index 4bb779301b85..00c2e9b3d59e 100644 --- a/engines/zvision/animation/rlf_animation.h +++ b/engines/zvision/animation/rlf_animation.h @@ -37,6 +37,7 @@ namespace ZVision { class RlfAnimation { public: RlfAnimation(const Common::String &fileName, bool stream = true); + RlfAnimation(Common::SeekableReadStream *rstream, bool stream); ~RlfAnimation(); private: @@ -52,7 +53,7 @@ class RlfAnimation { }; private: - Common::File _file; + Common::SeekableReadStream *_readStream; bool _stream; uint _lastFrameRead; @@ -63,15 +64,23 @@ class RlfAnimation { Frame *_frames; Common::Array _completeFrames; - int _currentFrame; + int _nextFrame; Graphics::Surface _currentFrameBuffer; uint32 _frameBufferByteSize; public: - uint frameCount() { return _frameCount; } - uint width() { return _width; } - uint height() { return _height; } - uint32 frameTime() { return _frameTime; } + uint frameCount() { + return _frameCount; + } + uint width() { + return _width; + } + uint height() { + return _height; + } + uint32 frameTime() { + return _frameTime; + } /** * Seeks to the frameNumber and updates the internal Surface with @@ -84,7 +93,7 @@ class RlfAnimation { /** * Returns the pixel data of the frame specified. It will try to use - * getNextFrame() if possible. If not, it uses seekToFrame() to + * decodeNextFrame() if possible. If not, it uses seekToFrame() to * update the internal Surface and then returns a pointer to it. * This function requires _stream = false * @@ -93,18 +102,19 @@ class RlfAnimation { */ const Graphics::Surface *getFrameData(uint frameNumber); /** - * Returns the pixel data of the next frame. It is up to the user to - * check if the next frame is valid before calling this. + * Returns the pixel data of current frame and go to next. It is up to the user to + * check if the current frame is valid before calling this. * IE. Use endOfAnimation() * * @return A pointer to the pixel data. Do NOT delete this. */ - const Graphics::Surface *getNextFrame(); - + const Graphics::Surface *decodeNextFrame(); /** * @return Is the currentFrame is the last frame in the animation? */ - bool endOfAnimation() { return _currentFrame == (int)_frameCount - 1; } + bool endOfAnimation() { + return _nextFrame == (int)_frameCount; + } private: /** diff --git a/engines/zvision/archives/zfs_archive.cpp b/engines/zvision/archives/zfs_archive.cpp index d18cc9966b7c..ed4dcec1feb2 100644 --- a/engines/zvision/archives/zfs_archive.cpp +++ b/engines/zvision/archives/zfs_archive.cpp @@ -52,7 +52,7 @@ ZfsArchive::ZfsArchive(const Common::String &fileName, Common::SeekableReadStrea ZfsArchive::~ZfsArchive() { debug(1, "ZfsArchive Destructor Called"); ZfsEntryHeaderMap::iterator it = _entryHeaders.begin(); - for ( ; it != _entryHeaders.end(); ++it) { + for (; it != _entryHeaders.end(); ++it) { delete it->_value; } } @@ -138,7 +138,7 @@ Common::SeekableReadStream *ZfsArchive::createReadStreamForMember(const Common:: zfsArchive.seek(entryHeader->offset); // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory - byte* buffer = (byte *)malloc(entryHeader->size); + byte *buffer = (byte *)malloc(entryHeader->size); zfsArchive.read(buffer, entryHeader->size); // Decrypt the data in place if (_header.xorKey != 0) diff --git a/engines/zvision/archives/zfs_archive.h b/engines/zvision/archives/zfs_archive.h index 3509cfee26cb..44e2c4b24019 100644 --- a/engines/zvision/archives/zfs_archive.h +++ b/engines/zvision/archives/zfs_archive.h @@ -53,7 +53,7 @@ struct ZfsEntryHeader { uint32 unknown; }; -typedef Common::HashMap ZfsEntryHeaderMap; +typedef Common::HashMap ZfsEntryHeaderMap; class ZfsArchive : public Common::Archive { public: diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp index e610f344748a..aac4e7b2bcc4 100644 --- a/engines/zvision/core/console.cpp +++ b/engines/zvision/core/console.cpp @@ -1,24 +1,24 @@ /* 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. - * - */ +* +* 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" @@ -27,7 +27,7 @@ #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" -#include "zvision/strings/string_manager.h" +#include "zvision/text/string_manager.h" #include "zvision/video/zork_avi_decoder.h" #include "zvision/sound/zork_raw.h" #include "zvision/utility/utility.h" @@ -60,12 +60,12 @@ Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { } bool Console::cmdLoadImage(int argc, const char **argv) { - if (argc == 4) - _engine->getRenderManager()->renderImageToScreen(argv[1], atoi(argv[2]), atoi(argv[3])); - else { - debugPrintf("Use loadimage to load an image to the screen\n"); - return true; - } +// if (argc == 4) +// _engine->getRenderManager()->renderImageToScreen(argv[1], atoi(argv[2]), atoi(argv[3])); +// else { +// DebugPrintf("Use loadimage to load an image to the screen\n"); +// return true; +// } return true; } @@ -197,7 +197,6 @@ bool Console::cmdParseAllScrFiles(int argc, const char **argv) { SearchMan.listMatchingMembers(list, "*.scr"); for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { - _engine->getScriptManager()->parseScrFile((*iter)->getName()); } return true; @@ -209,8 +208,8 @@ bool Console::cmdRenderText(int argc, const char **argv) { return true; } - StringManager::TextStyle style = _engine->getStringManager()->getTextStyle(atoi(argv[2])); - _engine->getRenderManager()->renderTextToWorkingWindow(333, Common::String(argv[1]), style.font, atoi(argv[3]), atoi(argv[4]), style.color, atoi(argv[5]), -1, Graphics::kTextAlignLeft, atoi(argv[6]) == 0 ? false : true); + //StringManager::TextStyle style = _engine->getStringManager()->getTextStyle(atoi(argv[2])); + //_engine->getRenderManager()->renderTextToWorkingWindow(333, Common::String(argv[1]), style.font, atoi(argv[3]), atoi(argv[4]), style.color, atoi(argv[5]), -1, Graphics::kTextAlignLeft, atoi(argv[6]) == 0 ? false : true); return true; } diff --git a/engines/zvision/core/events.cpp b/engines/zvision/core/events.cpp index 83d6c17decb1..873cdb0df75e 100644 --- a/engines/zvision/core/events.cpp +++ b/engines/zvision/core/events.cpp @@ -8,12 +8,12 @@ * 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. @@ -29,6 +29,10 @@ #include "zvision/graphics/render_manager.h" #include "zvision/scripting/script_manager.h" #include "zvision/animation/rlf_animation.h" +#include "zvision/core/menu.h" +#include "zvision/utility/win_keys.h" +#include "zvision/core/menu.h" +#include "zvision/sound/zork_raw.h" #include "common/events.h" #include "common/system.h" @@ -39,26 +43,146 @@ namespace ZVision { +void ZVision::shortKeys(Common::Event event) { + if (event.kbd.hasFlags(Common::KBD_CTRL)) { + switch (event.kbd.keycode) { + case Common::KEYCODE_s: + if (getMenuBarEnable() & menuBar_Save) + _scriptManager->changeLocation('g', 'j', 's', 'e', 0); + break; + case Common::KEYCODE_r: + if (getMenuBarEnable() & menuBar_Restore) + _scriptManager->changeLocation('g', 'j', 'r', 'e', 0); + break; + case Common::KEYCODE_p: + if (getMenuBarEnable() & menuBar_Settings) + _scriptManager->changeLocation('g', 'j', 'p', 'e', 0); + break; + case Common::KEYCODE_q: + if (getMenuBarEnable() & menuBar_Exit) + ifQuit(); + break; + default: + break; + } + } +} + +void ZVision::cheatCodes(uint8 key) { + pushKeyToCheatBuf(key); + + if (getGameId() == GID_GRANDINQUISITOR) { + + if (checkCode("IMNOTDEAF")) { + // Unknow cheat + showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented")); + } + + if (checkCode("3100OPB")) { + showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _scriptManager->getStateValue(StateKey_World), + _scriptManager->getStateValue(StateKey_Room), + _scriptManager->getStateValue(StateKey_Node), + _scriptManager->getStateValue(StateKey_View))); + } + + if (checkCode("KILLMENOW")) { + _scriptManager->changeLocation('g', 'j', 'd', 'e', 0); + _scriptManager->setStateValue(2201, 35); + } + + if (checkCode("MIKESPANTS")) { + _scriptManager->changeLocation('g', 'j', 't', 'm', 0); + } + } else if (getGameId() == GID_NEMESIS) { + + if (checkCode("CHLOE")) { + _scriptManager->changeLocation('t', 'm', '2', 'g', 0); + _scriptManager->setStateValue(224, 1); + } + + if (checkCode("77MASSAVE")) { + showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _scriptManager->getStateValue(StateKey_World), + _scriptManager->getStateValue(StateKey_Room), + _scriptManager->getStateValue(StateKey_Node), + _scriptManager->getStateValue(StateKey_View))); + } + + if (checkCode("IDKFA")) { + _scriptManager->changeLocation('t', 'w', '3', 'f', 0); + _scriptManager->setStateValue(249, 1); + } + + if (checkCode("309NEWDORMA")) { + _scriptManager->changeLocation('g', 'j', 'g', 'j', 0); + } + + if (checkCode("HELLOSAILOR")) { + Location loc = _scriptManager->getCurrentLocation(); + Audio::AudioStream *soundStream; + if (loc.world == 'v' && loc.room == 'b' && loc.node == '1' && loc.view == '0') { + soundStream = makeRawZorkStream("v000hpta.raw", this); + } else { + soundStream = makeRawZorkStream("v000hnta.raw", this); + } + Audio::SoundHandle handle; + _mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream); + } + } + + if (checkCode("FRAME")) + showDebugMsg(Common::String::format("FPS: ???, not implemented")); + + if (checkCode("XYZZY")) + _scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats)); + + if (checkCode("COMPUTERARCH")) + showDebugMsg(Common::String::format("COMPUTERARCH: var-viewer not implemented")); + + if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1) + if (checkCode("GO????")) + _scriptManager->changeLocation(getBufferedKey(3), + getBufferedKey(2), + getBufferedKey(1), + getBufferedKey(0), 0); +} + void ZVision::processEvents() { while (_eventMan->pollEvent(_event)) { switch (_event.type) { case Common::EVENT_LBUTTONDOWN: - onMouseDown(_event.mouse); + _cursorManager->cursorDown(true); + _scriptManager->setStateValue(StateKey_LMouse, 1); + _menu->onMouseDown(_event.mouse); + _scriptManager->addEvent(_event); break; case Common::EVENT_LBUTTONUP: - onMouseUp(_event.mouse); + _cursorManager->cursorDown(false); + _scriptManager->setStateValue(StateKey_LMouse, 0); + _menu->onMouseUp(_event.mouse); + _scriptManager->addEvent(_event); break; case Common::EVENT_RBUTTONDOWN: - // TODO: Inventory logic + _cursorManager->cursorDown(true); + _scriptManager->setStateValue(StateKey_RMouse, 1); + + if (getGameId() == GID_NEMESIS) + _scriptManager->inventoryCycle(); + break; + + case Common::EVENT_RBUTTONUP: + _cursorManager->cursorDown(false); + _scriptManager->setStateValue(StateKey_RMouse, 0); break; case Common::EVENT_MOUSEMOVE: onMouseMove(_event.mouse); break; - case Common::EVENT_KEYDOWN: + case Common::EVENT_KEYDOWN: { switch (_event.kbd.keycode) { case Common::KEYCODE_d: if (_event.kbd.hasFlags(Common::KBD_CTRL)) { @@ -67,18 +191,52 @@ void ZVision::processEvents() { _console->onFrame(); } break; - case Common::KEYCODE_q: - if (_event.kbd.hasFlags(Common::KBD_CTRL)) - quitGame(); + + case Common::KEYCODE_LEFT: + case Common::KEYCODE_RIGHT: + if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA) + _kbdVelocity = (_event.kbd.keycode == Common::KEYCODE_LEFT ? + -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : + _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; + break; + + case Common::KEYCODE_UP: + case Common::KEYCODE_DOWN: + if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT) + _kbdVelocity = (_event.kbd.keycode == Common::KEYCODE_UP ? + -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : + _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; break; + default: break; } - _scriptManager->onKeyDown(_event.kbd); - break; + uint8 vkKey = VKkey(_event.kbd.keycode); + + _scriptManager->setStateValue(StateKey_KeyPress, vkKey); + + _scriptManager->addEvent(_event); + shortKeys(_event); + cheatCodes(vkKey); + } + break; case Common::EVENT_KEYUP: - _scriptManager->onKeyUp(_event.kbd); + _scriptManager->addEvent(_event); + switch (_event.kbd.keycode) { + case Common::KEYCODE_LEFT: + case Common::KEYCODE_RIGHT: + if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA) + _kbdVelocity = 0; + break; + case Common::KEYCODE_UP: + case Common::KEYCODE_DOWN: + if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT) + _kbdVelocity = 0; + break; + default: + break; + } break; default: break; @@ -86,24 +244,11 @@ void ZVision::processEvents() { } } -void ZVision::onMouseDown(const Common::Point &pos) { - _cursorManager->cursorDown(true); - - Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - _scriptManager->onMouseDown(pos, imageCoord); -} - -void ZVision::onMouseUp(const Common::Point &pos) { - _cursorManager->cursorDown(false); - - Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - _scriptManager->onMouseUp(pos, imageCoord); -} - void ZVision::onMouseMove(const Common::Point &pos) { + _menu->onMouseMove(pos); Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); - bool cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord); + bool cursorWasChanged = false; // Graph of the function governing rotation velocity: // @@ -136,50 +281,62 @@ void ZVision::onMouseMove(const Common::Point &pos) { // ^ if (_workingWindow.contains(pos)) { + cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord); + RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); if (renderState == RenderTable::PANORAMA) { if (pos.x >= _workingWindow.left && pos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the left edge (y = -mx + b) - // We use fixed point math to get better accuracy - Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.left)) - MAX_ROTATION_SPEED; - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setLeftCursor(); + + int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; + if (mspeed <= 0) + mspeed = 400 >> 4; + _mouseVelocity = (((pos.x - (ROTATION_SCREEN_EDGE_OFFSET + _workingWindow.left)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + + _cursorManager->changeCursor(CursorIndex_Left); cursorWasChanged = true; } else if (pos.x <= _workingWindow.right && pos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the right edge (y = mx) - // We use fixed point math to get better accuracy - Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET); - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setRightCursor(); + + int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; + if (mspeed <= 0) + mspeed = 400 >> 4; + _mouseVelocity = (((pos.x - (_workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + + _cursorManager->changeCursor(CursorIndex_Right); cursorWasChanged = true; } else { - _renderManager->setBackgroundVelocity(0); + _mouseVelocity = 0; } } else if (renderState == RenderTable::TILT) { if (pos.y >= _workingWindow.top && pos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to top edge - // We use fixed point math to get better accuracy - Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - MAX_ROTATION_SPEED; - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setUpCursor(); + + int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; + if (mspeed <= 0) + mspeed = 400 >> 4; + _mouseVelocity = (((pos.y - (_workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + + _cursorManager->changeCursor(CursorIndex_UpArr); cursorWasChanged = true; } else if (pos.y <= _workingWindow.bottom && pos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { - // Linear function of distance to the bottom edge (y = mx) - // We use fixed point math to get better accuracy - Common::Rational velocity = Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET); - _renderManager->setBackgroundVelocity(velocity.toInt()); - _cursorManager->setDownCursor(); + + int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; + if (mspeed <= 0) + mspeed = 400 >> 4; + _mouseVelocity = (((pos.y - (_workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + + _cursorManager->changeCursor(CursorIndex_DownArr); cursorWasChanged = true; } else { - _renderManager->setBackgroundVelocity(0); + _mouseVelocity = 0; } + } else { + _mouseVelocity = 0; } } else { - _renderManager->setBackgroundVelocity(0); + _mouseVelocity = 0; } if (!cursorWasChanged) { - _cursorManager->revertToIdle(); + _cursorManager->changeCursor(CursorIndex_Idle); } } diff --git a/engines/zvision/core/menu.cpp b/engines/zvision/core/menu.cpp new file mode 100644 index 000000000000..5816b3f364a9 --- /dev/null +++ b/engines/zvision/core/menu.cpp @@ -0,0 +1,765 @@ +/* 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 "zvision/core/menu.h" + +#include "zvision/graphics/render_manager.h" + + +namespace ZVision { + +enum { + SLOT_START_SLOT = 151, + SLOT_SPELL_1 = 191, + SLOT_USER_CHOSE_THIS_SPELL = 205, + SLOT_REVERSED_SPELLBOOK = 206 +}; + +enum { + menu_MAIN_SAVE = 0, + menu_MAIN_REST = 1, + menu_MAIN_PREF = 2, + menu_MAIN_EXIT = 3 +}; + +MenuHandler::MenuHandler(ZVision *engine) { + _engine = engine; + menuBarFlag = 0xFFFF; +} + +MenuZGI::MenuZGI(ZVision *engine) : + MenuHandler(engine) { + menuMouseFocus = -1; + inmenu = false; + scrolled[0] = false; + scrolled[1] = false; + scrolled[2] = false; + scrollPos[0] = 0.0; + scrollPos[1] = 0.0; + scrollPos[2] = 0.0; + mouseOnItem = -1; + + char buf[24]; + for (int i = 1; i < 4; i++) { + sprintf(buf, "gmzau%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][0], false); + sprintf(buf, "gmzau%2.2x1.tga", i + 0x10); + _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][1], false); + } + for (int i = 0; i < 4; i++) { + sprintf(buf, "gmzmu%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menubar[i][0], false); + sprintf(buf, "gmznu%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menubar[i][1], false); + } + + for (int i = 0; i < 50; i++) { + items[i][0] = NULL; + items[i][1] = NULL; + itemId[i] = 0; + } + + for (int i = 0; i < 12; i++) { + magic[i][0] = NULL; + magic[i][1] = NULL; + magicId[i] = 0; + } +} + +MenuZGI::~MenuZGI() { + for (int i = 0; i < 3; i++) { + menuback[i][0].free(); + menuback[i][1].free(); + } + for (int i = 0; i < 4; i++) { + menubar[i][0].free(); + menubar[i][1].free(); + } + for (int i = 0; i < 50; i++) { + if (items[i][0]) { + items[i][0]->free(); + delete items[i][0]; + } + if (items[i][1]) { + items[i][1]->free(); + delete items[i][1]; + } + } + for (int i = 0; i < 12; i++) { + if (magic[i][0]) { + magic[i][0]->free(); + delete magic[i][0]; + } + if (magic[i][1]) { + magic[i][1]->free(); + delete magic[i][1]; + } + } +} + +void MenuZGI::onMouseUp(const Common::Point &Pos) { + if (Pos.y < 40) { + switch (menuMouseFocus) { + case menu_ITEM: + if (menuBarFlag & menuBar_Items) { + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, + scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + if (mouseItem >= 0 && mouseItem < 0xE0) { + _engine->getScriptManager()->inventoryDrop(mouseItem); + _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i)); + _engine->getScriptManager()->setStateValue(SLOT_START_SLOT + i, mouseItem); + + redraw = true; + } + } + } + } + break; + + case menu_MAGIC: + if (menuBarFlag & menuBar_Magic) { + for (int i = 0; i < 12; i++) { + + uint itemnum = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + if (itemnum != 0) { + if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + itemnum = 0xEE + i; + else + itemnum = 0xE0 + i; + } + if (itemnum) + if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0) + if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, + 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) + _engine->getScriptManager()->setStateValue(SLOT_USER_CHOSE_THIS_SPELL, itemnum); + } + + } + break; + + case menu_MAIN: + + // Exit + if (menuBarFlag & menuBar_Exit) + if (Common::Rect(320 + 135, + scrollPos[menu_MAIN], + 320 + 135 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + _engine->ifQuit(); + } + + // Settings + if (menuBarFlag & menuBar_Settings) + if (Common::Rect(320 , + scrollPos[menu_MAIN], + 320 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); + } + + // Load + if (menuBarFlag & menuBar_Restore) + if (Common::Rect(320 - 135, + scrollPos[menu_MAIN], + 320, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); + } + + // Save + if (menuBarFlag & menuBar_Save) + if (Common::Rect(320 - 135 * 2, + scrollPos[menu_MAIN], + 320 - 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); + } + break; + } + } +} + +void MenuZGI::onMouseMove(const Common::Point &Pos) { + if (Pos.y < 40) { + + if (!inmenu) + redraw = true; + inmenu = true; + switch (menuMouseFocus) { + case menu_ITEM: + if (menuBarFlag & menuBar_Items) { + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + else if (itemCount > 50) + itemCount = 50; + + int lastItem = mouseOnItem; + + mouseOnItem = -1; + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, + scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + mouseOnItem = i; + break; + } + } + + if (lastItem != mouseOnItem) + if (_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + mouseOnItem) || + _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + lastItem)) + redraw = true; + } + break; + + case menu_MAGIC: + if (menuBarFlag & menuBar_Magic) { + int lastItem = mouseOnItem; + mouseOnItem = -1; + for (int i = 0; i < 12; i++) { + if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, + 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) { + mouseOnItem = i; + break; + } + } + + if (lastItem != mouseOnItem) + if (_engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + mouseOnItem) || + _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + lastItem)) + redraw = true; + + } + break; + + case menu_MAIN: { + int lastItem = mouseOnItem; + mouseOnItem = -1; + + // Exit + if (menuBarFlag & menuBar_Exit) + if (Common::Rect(320 + 135, + scrollPos[menu_MAIN], + 320 + 135 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_EXIT; + } + + // Settings + if (menuBarFlag & menuBar_Settings) + if (Common::Rect(320 , + scrollPos[menu_MAIN], + 320 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_PREF; + } + + // Load + if (menuBarFlag & menuBar_Restore) + if (Common::Rect(320 - 135, + scrollPos[menu_MAIN], + 320, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_REST; + } + + // Save + if (menuBarFlag & menuBar_Save) + if (Common::Rect(320 - 135 * 2, + scrollPos[menu_MAIN], + 320 - 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_SAVE; + } + + if (lastItem != mouseOnItem) + redraw = true; + } + break; + + default: + int cur_menu = menuMouseFocus; + if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main + menuMouseFocus = menu_MAIN; + scrolled[menu_MAIN] = false; + scrollPos[menu_MAIN] = menuback[menu_MAIN][1].h - menuback[menu_MAIN][0].h; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); + } + + if (menuBarFlag & menuBar_Magic) + if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic + menuMouseFocus = menu_MAGIC; + scrolled[menu_MAGIC] = false; + scrollPos[menu_MAGIC] = 28; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3); + } + + if (menuBarFlag & menuBar_Items) + if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items + menuMouseFocus = menu_ITEM; + scrolled[menu_ITEM] = false; + scrollPos[menu_ITEM] = 28 - 600; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1); + } + + if (cur_menu != menuMouseFocus) + clean = true; + + break; + } + } else { + if (inmenu) + clean = true; + inmenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + menuMouseFocus = -1; + } +} + +void MenuZGI::process(uint32 deltatime) { + if (clean) { + _engine->getRenderManager()->clearMenuSurface(); + clean = false; + } + switch (menuMouseFocus) { + case menu_ITEM: + if (menuBarFlag & menuBar_Items) + if (!scrolled[menu_ITEM]) { + redraw = true; + float scrl = 600.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos [menu_ITEM] += scrl; + + if (scrollPos[menu_ITEM] >= 0) { + scrolled[menu_ITEM] = true; + scrollPos [menu_ITEM] = 0; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][0], scrollPos[menu_ITEM], 0); + + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + else if (itemCount > 50) + itemCount = 50; + + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + bool inrect = false; + + if (mouseOnItem == i) + inrect = true; + + uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i); + + if (curItemId != 0) { + if (itemId[i] != curItemId) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", curItemId); + items[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", curItemId); + items[i][1] = _engine->getRenderManager()->loadImage(buf, false); + itemId[i] = curItemId; + } + + if (inrect) + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + else + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + + } else { + if (items[i][0]) { + items[i][0]->free(); + delete items[i][0]; + items[i][0] = NULL; + } + if (items[i][1]) { + items[i][1]->free(); + delete items[i][1]; + items[i][1] = NULL; + } + itemId[i] = 0; + } + } + + redraw = false; + } + break; + + case menu_MAGIC: + if (menuBarFlag & menuBar_Magic) + if (!scrolled[menu_MAGIC]) { + redraw = true; + float scrl = 600.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos [menu_MAGIC] += scrl; + + if (scrollPos[menu_MAGIC] >= 600) { + scrolled[menu_MAGIC] = true; + scrollPos [menu_MAGIC] = 600; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][0], 640 - scrollPos[menu_MAGIC], 0); + + for (int i = 0; i < 12; i++) { + bool inrect = false; + + if (mouseOnItem == i) + inrect = true; + + uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + if (curItemId) { + if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + curItemId = 0xEE + i; + else + curItemId = 0xE0 + i; + } + + if (curItemId != 0) { + if (itemId[i] != curItemId) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", curItemId); + magic[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", curItemId); + magic[i][1] = _engine->getRenderManager()->loadImage(buf, false); + magicId[i] = curItemId; + } + + if (inrect) + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + else + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + + } else { + if (magic[i][0]) { + magic[i][0]->free(); + delete magic[i][0]; + magic[i][0] = NULL; + } + if (magic[i][1]) { + magic[i][1]->free(); + delete magic[i][1]; + magic[i][1] = NULL; + } + magicId[i] = 0; + } + } + redraw = false; + } + break; + + case menu_MAIN: + if (!scrolled[menu_MAIN]) { + redraw = true; + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos [menu_MAIN] += scrl; + + if (scrollPos[menu_MAIN] >= 0) { + scrolled[menu_MAIN] = true; + scrollPos [menu_MAIN] = 0; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][0], 30, scrollPos[menu_MAIN]); + + if (menuBarFlag & menuBar_Exit) { + if (mouseOnItem == menu_MAIN_EXIT) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][1], 320 + 135, scrollPos[menu_MAIN]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][0], 320 + 135, scrollPos[menu_MAIN]); + } + if (menuBarFlag & menuBar_Settings) { + if (mouseOnItem == menu_MAIN_PREF) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][1], 320, scrollPos[menu_MAIN]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][0], 320, scrollPos[menu_MAIN]); + } + if (menuBarFlag & menuBar_Restore) { + if (mouseOnItem == menu_MAIN_REST) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][1], 320 - 135, scrollPos[menu_MAIN]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][0], 320 - 135, scrollPos[menu_MAIN]); + } + if (menuBarFlag & menuBar_Save) { + if (mouseOnItem == menu_MAIN_SAVE) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][1], 320 - 135 * 2, scrollPos[menu_MAIN]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][0], 320 - 135 * 2, scrollPos[menu_MAIN]); + } + redraw = false; + } + break; + default: + if (redraw) { + if (inmenu) { + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][1], 30, 0); + + if (menuBarFlag & menuBar_Items) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][1], 0, 0); + + if (menuBarFlag & menuBar_Magic) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][1], 640 - 28, 0); + } + redraw = false; + } + break; + } +} + + +MenuNemesis::MenuNemesis(ZVision *engine) : + MenuHandler(engine) { + inmenu = false; + scrolled = false; + scrollPos = 0.0; + mouseOnItem = -1; + + char buf[24]; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 6; j++) { + sprintf(buf, "butfrm%d%d.tga", i + 1, j); + _engine->getRenderManager()->readImageToSurface(buf, but[i][j], false); + } + + _engine->getRenderManager()->readImageToSurface("bar.tga", menubar, false); + + frm = 0; +} + +MenuNemesis::~MenuNemesis() { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 6; j++) + but[i][j].free(); + + menubar.free(); +} + +static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} }; + +void MenuNemesis::onMouseUp(const Common::Point &Pos) { + if (Pos.y < 40) { + // Exit + if (menuBarFlag & menuBar_Exit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + _engine->ifQuit(); + frm = 5; + redraw = true; + } + + // Settings + if (menuBarFlag & menuBar_Settings) + if (Common::Rect(buts[2][1], + scrollPos, + buts[2][0] + buts[2][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); + frm = 5; + redraw = true; + } + + // Load + if (menuBarFlag & menuBar_Restore) + if (Common::Rect(buts[1][1], + scrollPos, + buts[1][0] + buts[1][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); + frm = 5; + redraw = true; + } + + // Save + if (menuBarFlag & menuBar_Save) + if (Common::Rect(buts[0][1], + scrollPos, + buts[0][0] + buts[0][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); + frm = 5; + redraw = true; + } + } +} + +void MenuNemesis::onMouseMove(const Common::Point &Pos) { + if (Pos.y < 40) { + + inmenu = true; + + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); + + int lastItem = mouseOnItem; + mouseOnItem = -1; + + // Exit + if (menuBarFlag & menuBar_Exit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_EXIT; + } + + // Settings + if (menuBarFlag & menuBar_Settings) + if (Common::Rect(buts[2][1], + scrollPos, + buts[2][0] + buts[2][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_PREF; + } + + // Load + if (menuBarFlag & menuBar_Restore) + if (Common::Rect(buts[1][1], + scrollPos, + buts[1][0] + buts[1][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_REST; + } + + // Save + if (menuBarFlag & menuBar_Save) + if (Common::Rect(buts[0][1], + scrollPos, + buts[0][0] + buts[0][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = menu_MAIN_SAVE; + } + + if (lastItem != mouseOnItem) { + redraw = true; + frm = 0; + delay = 200; + } + } else { + inmenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + mouseOnItem = -1; + } +} + +void MenuNemesis::process(uint32 deltatime) { + if (inmenu) { + if (!scrolled) { + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos += scrl; + redraw = true; + } + + if (scrollPos >= 0) { + scrolled = true; + scrollPos = 0; + } + + if (mouseOnItem != -1) { + delay -= deltatime; + if (delay <= 0 && frm < 4) { + delay = 200; + frm++; + redraw = true; + } + } + + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); + + if (menuBarFlag & menuBar_Exit) + if (mouseOnItem == menu_MAIN_EXIT) + _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos); + + if (menuBarFlag & menuBar_Settings) + if (mouseOnItem == menu_MAIN_PREF) + _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos); + + if (menuBarFlag & menuBar_Restore) + if (mouseOnItem == menu_MAIN_REST) + _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos); + + if (menuBarFlag & menuBar_Save) + if (mouseOnItem == menu_MAIN_SAVE) + _engine->getRenderManager()->blitSurfaceToMenu(but[0][frm], buts[0][1], scrollPos); + + redraw = false; + } + } else { + scrolled = false; + if (scrollPos > -32) { + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + Common::Rect cl(64, 32 + scrollPos - scrl, 64 + 512, 32 + scrollPos + 1); + _engine->getRenderManager()->clearMenuSurface(cl); + + scrollPos -= scrl; + redraw = true; + } else + scrollPos = -32; + + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); + redraw = false; + } + } +} + + +} // End of namespace ZVision diff --git a/engines/zvision/core/menu.h b/engines/zvision/core/menu.h index 3ab6d4c2ec4b..62683de912e4 100644 --- a/engines/zvision/core/menu.h +++ b/engines/zvision/core/menu.h @@ -23,6 +23,105 @@ #ifndef ZVISION_MENU_H #define ZVISION_MENU_H -// TODO: Implement MenuHandler +#include "graphics/surface.h" +#include "common/rect.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" + +namespace ZVision { + +enum menuBar { + menuBar_Exit = 0x1, + menuBar_Settings = 0x2, + menuBar_Restore = 0x4, + menuBar_Save = 0x8, + menuBar_Items = 0x100, + menuBar_Magic = 0x200 +}; + +class MenuHandler { +public: + MenuHandler(ZVision *engine); + virtual ~MenuHandler() {}; + virtual void onMouseMove(const Common::Point &Pos) {}; + virtual void onMouseDown(const Common::Point &Pos) {}; + virtual void onMouseUp(const Common::Point &Pos) {}; + virtual void process(uint32 deltaTimeInMillis) {}; + + void setEnable(uint16 flags) { + menuBarFlag = flags; + } + uint16 getEnable() { + return menuBarFlag; + } +protected: + uint16 menuBarFlag; + ZVision *_engine; +}; + +class MenuZGI: public MenuHandler { +public: + MenuZGI(ZVision *engine); + ~MenuZGI(); + void onMouseMove(const Common::Point &Pos); + void onMouseUp(const Common::Point &Pos); + void process(uint32 deltaTimeInMillis); +private: + Graphics::Surface menuback[3][2]; + Graphics::Surface menubar[4][2]; + + + Graphics::Surface *items[50][2]; + uint itemId[50]; + + Graphics::Surface *magic[12][2]; + uint magicId[12]; + + int menuMouseFocus; + bool inmenu; + + int mouseOnItem; + + bool scrolled[3]; + float scrollPos[3]; + + enum { + menu_ITEM = 0, + menu_MAGIC = 1, + menu_MAIN = 2 + }; + + bool clean; + bool redraw; + +}; + +class MenuNemesis: public MenuHandler { +public: + MenuNemesis(ZVision *engine); + ~MenuNemesis(); + void onMouseMove(const Common::Point &Pos); + void onMouseUp(const Common::Point &Pos); + void process(uint32 deltaTimeInMillis); +private: + Graphics::Surface but[4][6]; + Graphics::Surface menubar; + + bool inmenu; + + int mouseOnItem; + + bool scrolled; + float scrollPos; + + bool redraw; + + int frm; + int16 delay; + +}; + +} #endif diff --git a/engines/zvision/core/midi.cpp b/engines/zvision/core/midi.cpp new file mode 100644 index 000000000000..736be1311de2 --- /dev/null +++ b/engines/zvision/core/midi.cpp @@ -0,0 +1,89 @@ +/* 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 "zvision/core/midi.h" + +namespace ZVision { + +MidiManager::MidiManager() { + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB); + _driver = MidiDriver::createMidi(dev); + _driver->open(); +} + +MidiManager::~MidiManager() { + stop(); + _driver->close(); + delete _driver; +} + +void MidiManager::stop() { + for (int8 i = 0; i < 16; i++) + if (_playChannels[i].playing) + noteOff(i); +} + +void MidiManager::noteOn(int8 channel, int8 note, int8 velocity) { + assert(channel <= 15); + + _playChannels[channel].playing = true; + _playChannels[channel].note = note; + _driver->send(channel | (velocity << 16) | (note << 8) | 0x90); +} + +void MidiManager::noteOff(int8 channel) { + assert(channel <= 15); + + if (_playChannels[channel].playing) { + _playChannels[channel].playing = false; + _driver->send(channel | (_playChannels[channel].note << 8) | 0x80); + } +} + +int8 MidiManager::getFreeChannel() { + for (int8 i = 0; i < 16; i++) + if (!_playChannels[i].playing) + return i; + return -1; +} + +void MidiManager::setPan(int8 channel, int8 pan) { + assert(channel <= 15); + + _driver->send(channel | (pan << 16) | 0xAB0); +} + +void MidiManager::setVolume(int8 channel, int8 volume) { + assert(channel <= 15); + + _driver->send(channel | (volume << 16) | 0x7B0); +} + +void MidiManager::setProgram(int8 channel, int8 prog) { + assert(channel <= 15); + + _driver->send(channel | (prog << 8) | 0xC0); +} + +} // End of namespace ZVision diff --git a/engines/zvision/core/midi.h b/engines/zvision/core/midi.h new file mode 100644 index 000000000000..a3bac1963659 --- /dev/null +++ b/engines/zvision/core/midi.h @@ -0,0 +1,59 @@ +/* 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 ZVISION_MIDI_H +#define ZVISION_MIDI_H + +#include "audio/mididrv.h" + +namespace ZVision { + +class MidiManager { +public: + MidiManager(); + ~MidiManager(); + + void stop(); + void noteOn(int8 channel, int8 noteNumber, int8 velocity); + void noteOff(int8 channel); + void setPan(int8 channel, int8 pan); + void setVolume(int8 channel, int8 volume); + void setProgram(int8 channel, int8 prog); + + int8 getFreeChannel(); + +protected: + + struct chan { + bool playing; + int8 note; + + chan() : playing(false), note(0) {}; + }; + + MidiDriver *_driver; + chan _playChannels[16]; +}; + +} + +#endif diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/core/save_manager.cpp index e10201e02478..9b36824c3b8b 100644 --- a/engines/zvision/core/save_manager.cpp +++ b/engines/zvision/core/save_manager.cpp @@ -42,68 +42,61 @@ const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G'); void SaveManager::saveGame(uint slot, const Common::String &saveName) { // The games only support 20 slots - assert(slot <= 1 && slot <= 20); + //assert(slot <= 1 && slot <= 20); Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Write version - file->writeByte(SAVE_VERSION); + writeSaveGameHeader(file, saveName); - // Write savegame name - file->writeString(saveName); - file->writeByte(0); + _engine->getScriptManager()->serialize(file); - // We can't call writeGameSaveData because the save menu is actually - // a room, so writeGameSaveData would save us in the save menu. - // However, an auto save is performed before each room change, so we - // can copy the data from there. We can guarantee that an auto save file will - // exist before this is called because the save menu can only be accessed - // after the first room (the main menu) has loaded. - Common::InSaveFile *autoSaveFile = saveFileManager->openForLoading(_engine->generateAutoSaveFileName()); + file->finalize(); + delete file; +} - // Skip over the header info - autoSaveFile->readSint32BE(); // SAVEGAME_ID - autoSaveFile->readByte(); // Version - autoSaveFile->seek(5, SEEK_CUR); // The string "auto" with terminating NULL +void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { + Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); + Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - // Read the rest to a buffer - uint32 size = autoSaveFile->size() - autoSaveFile->pos(); - byte *buffer = new byte[size]; - autoSaveFile->read(buffer, size); + writeSaveGameHeader(file, saveName); - // Then write the buffer to the new file - file->write(buffer, size); + file->write(stream->getData(), stream->size()); - // Cleanup - delete[] buffer; file->finalize(); delete file; } +void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { + if (_tempSave) { + saveGame(slot, saveName, _tempSave); + flushSaveBuffer(); + } +} + void SaveManager::autoSave() { Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Version - file->writeByte(SAVE_VERSION); - - file->writeString("auto"); - file->writeByte(0); + writeSaveGameHeader(file, "auto"); - writeSaveGameData(file); + _engine->getScriptManager()->serialize(file); // Cleanup file->finalize(); delete file; } -void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { +void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { + + file->writeUint32BE(SAVEGAME_ID); + + // Write version + file->writeByte(SAVE_VERSION); + + // Write savegame name + file->writeString(saveName); + file->writeByte(0); + // Create a thumbnail and save it Graphics::saveThumbnail(*file); @@ -115,28 +108,13 @@ void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { file->writeSint16LE(td.tm_mday); file->writeSint16LE(td.tm_hour); file->writeSint16LE(td.tm_min); - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Write out the current location - Location currentLocation = scriptManager->getCurrentLocation(); - file->writeByte(currentLocation.world); - file->writeByte(currentLocation.room); - file->writeByte(currentLocation.node); - file->writeByte(currentLocation.view); - file->writeUint32LE(currentLocation.offset); - - // Write out the current state table values - scriptManager->serializeStateTable(file); - - // Write out any controls needing to save state - scriptManager->serializeControls(file); } Common::Error SaveManager::loadGame(uint slot) { // The games only support 20 slots - assert(slot <= 1 && slot <= 20); + //assert(slot <= 1 && slot <= 20); - Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + Common::SeekableReadStream *saveFile = getSlotFile(slot); if (saveFile == 0) { return Common::kPathDoesNotExist; } @@ -147,27 +125,60 @@ Common::Error SaveManager::loadGame(uint slot) { return Common::kUnknownError; } - char world = (char)saveFile->readByte(); - char room = (char)saveFile->readByte(); - char node = (char)saveFile->readByte(); - char view = (char)saveFile->readByte(); - uint32 offset = (char)saveFile->readUint32LE(); - ScriptManager *scriptManager = _engine->getScriptManager(); // Update the state table values - scriptManager->deserializeStateTable(saveFile); + scriptManager->deserialize(saveFile); + + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; + + return Common::kNoError; +} + +Common::Error SaveManager::loadGame(const Common::String &saveName) { + Common::File *saveFile = _engine->getSearchManager()->openFile(saveName); + if (saveFile == NULL) { + saveFile = new Common::File; + if (!saveFile->open(saveName)) { + delete saveFile; + return Common::kPathDoesNotExist; + } + } + + // Read the header + SaveGameHeader header; + if (!readSaveGameHeader(saveFile, header)) { + return Common::kUnknownError; + } - // Load the room - scriptManager->changeLocation(world, room, node, view, offset); + ScriptManager *scriptManager = _engine->getScriptManager(); + // Update the state table values + scriptManager->deserialize(saveFile); - // Update the controls - scriptManager->deserializeControls(saveFile); + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; return Common::kNoError; } bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { - if (in->readUint32BE() != SAVEGAME_ID) { + uint32 tag = in->readUint32BE(); + // Check if it's original savegame than fill header structure + if (tag == MKTAG('Z', 'N', 'S', 'G')) { + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.saveName = "Original Save"; + header.thumbnail = NULL; + header.version = SAVE_ORIGINAL; + in->seek(-4, SEEK_CUR); + return true; + } + if (tag != SAVEGAME_ID) { warning("File is not a ZVision save file. Aborting load"); return false; } @@ -203,4 +214,45 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea return true; } +Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { + Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + if (saveFile == NULL) { + // Try to load standard save file + Common::String filename; + if (_engine->getGameId() == GID_GRANDINQUISITOR) + filename = Common::String::format("inqsav%u.sav", slot); + else if (_engine->getGameId() == GID_NEMESIS) + filename = Common::String::format("nemsav%u.sav", slot); + + saveFile = _engine->getSearchManager()->openFile(filename); + if (saveFile == NULL) { + Common::File *tmpFile = new Common::File; + if (!tmpFile->open(filename)) { + delete tmpFile; + } else { + saveFile = tmpFile; + } + } + + } + + return saveFile; +} + +void SaveManager::prepareSaveBuffer() { + if (_tempSave) + delete _tempSave; + + _tempSave = new Common::MemoryWriteStreamDynamic; + + _engine->getScriptManager()->serialize(_tempSave); +} + +void SaveManager::flushSaveBuffer() { + if (_tempSave) + delete _tempSave; + + _tempSave = NULL; +} + } // End of namespace ZVision diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/core/save_manager.h index 43fb0c0faf52..5cd61c7aa95f 100644 --- a/engines/zvision/core/save_manager.h +++ b/engines/zvision/core/save_manager.h @@ -24,6 +24,7 @@ #define ZVISION_SAVE_MANAGER_H #include "common/savefile.h" +#include "common/memstream.h" namespace Common { class String; @@ -47,16 +48,22 @@ struct SaveGameHeader { class SaveManager { public: - SaveManager(ZVision *engine) : _engine(engine) {} + SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL) {} + ~SaveManager() { + flushSaveBuffer(); + } private: ZVision *_engine; static const uint32 SAVEGAME_ID; enum { + SAVE_ORIGINAL = 0, SAVE_VERSION = 1 }; + Common::MemoryWriteStreamDynamic *_tempSave; + public: /** * Called every room change. Saves the state of the room just before @@ -73,6 +80,8 @@ class SaveManager { * @param saveName The internal name for this save. This is NOT the name of the actual save file. */ void saveGame(uint slot, const Common::String &saveName); + void saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream); + void saveGameBuffered(uint slot, const Common::String &saveName); /** * Loads the state data from the save file that slot references. Uses * ZVision::generateSaveFileName(slot) to get the save file name. @@ -80,10 +89,15 @@ class SaveManager { * @param slot The save slot to load. Must be [1, 20] */ Common::Error loadGame(uint slot); + Common::Error loadGame(const Common::String &saveName); + + Common::SeekableReadStream *getSlotFile(uint slot); + bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); + void prepareSaveBuffer(); + void flushSaveBuffer(); private: - void writeSaveGameData(Common::OutSaveFile *file); - bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header); + void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName); }; } // End of namespace ZVision diff --git a/engines/zvision/core/search_manager.cpp b/engines/zvision/core/search_manager.cpp new file mode 100644 index 000000000000..83501895665f --- /dev/null +++ b/engines/zvision/core/search_manager.cpp @@ -0,0 +1,275 @@ +/* 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 "zvision/core/search_manager.h" +#include "zvision/archives/zfs_archive.h" +#include "common/fs.h" +#include "common/stream.h" + + +namespace ZVision { + +SearchManager::SearchManager(const Common::String &rootPath, int depth) { + _root = rootPath; + if (_root[_root.size() - 1] == '\\' || _root[_root.size() - 1] == '/') + _root.deleteLastChar(); + + Common::FSNode fsNode(_root); + + listDirRecursive(dirList, fsNode, depth); + + for (Common::List::iterator it = dirList.begin(); it != dirList.end();) + if (it->size() == _root.size()) + it = dirList.erase(it); + else if (it->size() > _root.size()) { + *it = Common::String(it->c_str() + _root.size() + 1); + it++; + } else + it++; +} + +SearchManager::~SearchManager() { + Common::List::iterator it = archList.begin(); + while (it != archList.end()) { + delete *it; + it++; + } + + archList.clear(); +} + +void SearchManager::addPatch(const Common::String &src, const Common::String &dst) { + Common::String lowerCaseName = dst; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator it = files.find(lowerCaseName); + + if (it != files.end()) { + lowerCaseName = src; + lowerCaseName.toLowercase(); + files[lowerCaseName] = it->_value; + } +} + +void SearchManager::addFile(const Common::String &name, Common::Archive *arch) { + bool addArch = true; + Common::List::iterator it = archList.begin(); + while (it != archList.end()) { + if (*it == arch) { + addArch = false; + break; + } + it++; + } + if (addArch) + archList.push_back(arch); + + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::Node nod; + nod.name = lowerCaseName; + nod.arch = arch; + + SearchManager::MatchList::iterator fit = files.find(lowerCaseName); + + if (fit == files.end()) { + files[lowerCaseName] = nod; + } else { + Common::SeekableReadStream *stream = fit->_value.arch->createReadStreamForMember(fit->_value.name); + if (stream) { + if (stream->size() < 10) + fit->_value.arch = arch; + delete stream; + } else { + files[lowerCaseName] = nod; + } + } +} + +Common::File *SearchManager::openFile(const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = files.find(lowerCaseName); + + if (fit != files.end()) { + Common::File *tmp = new Common::File(); + tmp->open(fit->_value.name, *fit->_value.arch); + return tmp; + } + return NULL; +} + +bool SearchManager::openFile(Common::File &file, const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = files.find(lowerCaseName); + + if (fit != files.end()) + return file.open(fit->_value.name, *fit->_value.arch); + return false; +} + +bool SearchManager::hasFile(const Common::String &name) { + Common::String lowerCaseName = name; + lowerCaseName.toLowercase(); + + SearchManager::MatchList::iterator fit = files.find(lowerCaseName); + + if (fit != files.end()) + return true; + return false; +} + +void SearchManager::loadZix(const Common::String &name) { + Common::File file; + file.open(name); + + Common::String line; + + while (!file.eos()) { + line = file.readLine(); + if (line.matchString("----------*", true)) + break; + } + + if (file.eos()) + return; + + Common::Array archives; + + while (!file.eos()) { + line = file.readLine(); + line.trim(); + if (line.matchString("----------*", true)) + break; + else if (line.matchString("DIR:*", true)) { + Common::String path(line.c_str() + 5); + Common::Archive *arc; + char tempPath[128]; + strcpy(tempPath, path.c_str()); + for (uint i = 0; i < path.size(); i++) + if (tempPath[i] == '\\') + tempPath[i] = '/'; + + path = Common::String(tempPath); + if (path.size() && path[0] == '.') + path.deleteChar(0); + if (path.size() && path[0] == '/') + path.deleteChar(0); + + if (path.matchString("*.zfs", true)) + arc = new ZfsArchive(path); + else { + if (path.size()) { + if (path[path.size() - 1] == '\\' || path[path.size() - 1] == '/') + path.deleteLastChar(); + if (path.size()) + for (Common::List::iterator it = dirList.begin(); it != dirList.end(); ++it) + if (path.equalsIgnoreCase(*it)) { + path = *it; + break; + } + } + + path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); + + arc = new Common::FSDirectory(path); + } + archives.push_back(arc); + } + } + + if (file.eos()) + return; + + while (!file.eos()) { + line = file.readLine(); + line.trim(); + uint dr = 0; + char buf[32]; + if (sscanf(line.c_str(), "%u %s", &dr, buf) == 2) { + if (dr <= archives.size() && dr > 0) { + addFile(Common::String(buf), archives[dr - 1]); + } + } + } +} + +void SearchManager::addDir(const Common::String &name) { + Common::String path; + for (Common::List::iterator it = dirList.begin(); it != dirList.end(); ++it) + if (name.equalsIgnoreCase(*it)) { + path = *it; + break; + } + + if (path.size() == 0) + return; + + path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); + + Common::FSDirectory *dir = new Common::FSDirectory(path); + + Common::ArchiveMemberList list; + dir->listMatchingMembers(list, "*.zfs"); + + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + Common::String flname = (*iter)->getName(); + + ZfsArchive *zfs = new ZfsArchive(Common::String::format("%s/%s", name.c_str(), flname.c_str())); + + Common::ArchiveMemberList zfslist; + zfs->listMembers(zfslist); + + for (Common::ArchiveMemberList::iterator ziter = zfslist.begin(); ziter != zfslist.end(); ++ziter) { + Common::String zfsFileName = (*ziter)->getName(); + addFile(zfsFileName, zfs); + } + } + + list.clear(); + dir->listMembers(list); + + for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { + Common::String flname = (*iter)->getName(); + addFile(flname, dir); + } +} + +void SearchManager::listDirRecursive(Common::List &_list, const Common::FSNode &fsNode, int depth) { + Common::FSList fsList; + fsNode.getChildren(fsList); + + _list.push_back(fsNode.getPath()); + + if (depth > 1) + for (Common::FSList::const_iterator it = fsList.begin(); it != fsList.end(); ++it) + listDirRecursive(_list, *it, depth - 1); +} + +} // End of namespace ZVision diff --git a/engines/zvision/core/search_manager.h b/engines/zvision/core/search_manager.h new file mode 100644 index 000000000000..180102eac615 --- /dev/null +++ b/engines/zvision/core/search_manager.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 ZVISION_SEARCH_MANAGER_H +#define ZVISION_SEARCH_MANAGER_H + +#include "common/str.h" +#include "common/hash-str.h" +#include "common/hashmap.h" +#include "common/archive.h" +#include "common/file.h" +#include "common/list.h" + +namespace ZVision { + +class SearchManager { +public: + SearchManager(const Common::String &rootPath, int depth); + ~SearchManager(); + + void addFile(const Common::String &name, Common::Archive *arch); + void addDir(const Common::String &name); + void addPatch(const Common::String &src, const Common::String &dst); + + Common::File *openFile(const Common::String &name); + bool openFile(Common::File &file, const Common::String &name); + bool hasFile(const Common::String &name); + + void loadZix(const Common::String &name); + +private: + + void listDirRecursive(Common::List &dirList, const Common::FSNode &fsNode, int depth); + + struct Node { + Common::String name; + Common::Archive *arch; + }; + + Common::List dirList; + + typedef Common::HashMap MatchList; + + Common::List archList; + MatchList files; + + Common::String _root; + +private: +}; + +} + +#endif // ZVISION_SEARCH_MANAGER_H diff --git a/engines/zvision/cursors/cursor.cpp b/engines/zvision/cursors/cursor.cpp index 9b9b9a3f710e..8c6027a1bbb8 100644 --- a/engines/zvision/cursors/cursor.cpp +++ b/engines/zvision/cursors/cursor.cpp @@ -1,24 +1,24 @@ /* 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. - * - */ +* +* 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" @@ -38,10 +38,10 @@ ZorkCursor::ZorkCursor() } ZorkCursor::ZorkCursor(const Common::String &fileName) - : _width(0), - _height(0), - _hotspotX(0), - _hotspotY(0) { + : _width(0), + _height(0), + _hotspotX(0), + _hotspotY(0) { Common::File file; if (!file.open(fileName)) return; @@ -66,6 +66,35 @@ ZorkCursor::ZorkCursor(const Common::String &fileName) _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); } +ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) + : _width(0), + _height(0), + _hotspotX(0), + _hotspotY(0) { + Common::File file; + if (!engine->getSearchManager()->openFile(file, fileName)) + return; + + uint32 magic = file.readUint32BE(); + if (magic != MKTAG('Z', 'C', 'R', '1')) { + warning("%s is not a Zork Cursor file", fileName.c_str()); + return; + } + + _hotspotX = file.readUint16LE(); + _hotspotY = file.readUint16LE(); + _width = file.readUint16LE(); + _height = file.readUint16LE(); + + uint dataSize = _width * _height * sizeof(uint16); + _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + uint32 bytesRead = file.read(_surface.getPixels(), dataSize); + assert(bytesRead == dataSize); + + // Convert to RGB 565 + _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); +} + ZorkCursor::ZorkCursor(const ZorkCursor &other) { _width = other._width; _height = other._height; diff --git a/engines/zvision/cursors/cursor.h b/engines/zvision/cursors/cursor.h index be9fae64da42..57db56165568 100644 --- a/engines/zvision/cursors/cursor.h +++ b/engines/zvision/cursors/cursor.h @@ -24,6 +24,7 @@ #define ZVISION_CURSOR_H #include "graphics/surface.h" +#include "zvision/zvision.h" namespace Common { @@ -40,6 +41,7 @@ class ZorkCursor { public: ZorkCursor(); ZorkCursor(const Common::String &fileName); + ZorkCursor(ZVision *engine, const Common::String &fileName); ZorkCursor(const ZorkCursor &other); ~ZorkCursor(); @@ -53,12 +55,24 @@ class ZorkCursor { public: ZorkCursor &operator=(const ZorkCursor &other); - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint16 getHotspotX() const { return _hotspotX; } - uint16 getHotspotY() const { return _hotspotY; } - byte getKeyColor() const { return 0; } - const byte *getSurface() const { return (const byte *)_surface.getPixels(); } + uint16 getWidth() const { + return _width; + } + uint16 getHeight() const { + return _height; + } + uint16 getHotspotX() const { + return _hotspotX; + } + uint16 getHotspotY() const { + return _hotspotY; + } + byte getKeyColor() const { + return 0; + } + const byte *getSurface() const { + return (const byte *)_surface.getPixels(); + } }; } // End of namespace ZVision diff --git a/engines/zvision/cursors/cursor_manager.cpp b/engines/zvision/cursors/cursor_manager.cpp index 7f70c8b4e38b..564ffd25c524 100644 --- a/engines/zvision/cursors/cursor_manager.cpp +++ b/engines/zvision/cursors/cursor_manager.cpp @@ -1,24 +1,24 @@ /* 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. - * - */ +* +* 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" @@ -35,82 +35,74 @@ namespace ZVision { const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "backward", "downarrow", "forward", "handpt", "handpu", "hdown", "hleft", - "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" }; + "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" + }; const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac001.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", - "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" }; + "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" + }; const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft", - "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" }; + "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" + }; CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat) - : _engine(engine), - _pixelFormat(pixelFormat), - _cursorIsPushed(false) { - // WARNING: The index IDLE_CURSOR_INDEX is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly - if (_engine->getGameId() == GID_NEMESIS) { - Common::String name(Common::String::format("%sa.zcr", _zNemCursorFileNames[IDLE_CURSOR_INDEX])); - _idleCursor = ZorkCursor(name); - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { - _idleCursor = ZorkCursor(_zgiCursorFileNames[IDLE_CURSOR_INDEX]); + : _engine(engine), + _pixelFormat(pixelFormat), + _cursorIsPushed(false), + _item(0), + _lastitem(0) { + for (int i = 0; i < NUM_CURSORS; i++) { + if (_engine->getGameId() == GID_NEMESIS) { + Common::String name; + name = Common::String::format("%sa.zcr", _zNemCursorFileNames[i]); + _cursors[i][0] = ZorkCursor(_engine, name); // Up cursor + name = Common::String::format("%sb.zcr", _zNemCursorFileNames[i]); + _cursors[i][1] = ZorkCursor(_engine, name); // Down cursor + } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + _cursors[i][0] = ZorkCursor(_engine, _zgiCursorFileNames[i]); // Up cursor + char buffer[25]; + strcpy(buffer, _zgiCursorFileNames[i]); + buffer[3] += 2; + _cursors[i][1] = ZorkCursor(_engine, buffer); // Down cursor + } } } -void CursorManager::initialize() { - revertToIdle(); - CursorMan.showMouse(true); -} - -void CursorManager::changeCursor(const Common::String &cursorName) { - changeCursor(cursorName, _cursorIsPushed); -} - -void CursorManager::changeCursor(const Common::String &cursorName, bool pushed) { - if (_currentCursor.equals(cursorName) && _cursorIsPushed == pushed) - return; - - if (_cursorIsPushed != pushed) - _cursorIsPushed = pushed; - - if (cursorName == "idle" && !pushed) { - CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); - return; - } - - for (int i = 0; i < NUM_CURSORS; ++i) { - if (_engine->getGameId() == GID_NEMESIS) { - if (cursorName.equals(_cursorNames[i])) { - _currentCursor = cursorName; - - // ZNem uses a/b at the end of the file to signify not pushed/pushed respectively - Common::String pushedFlag = pushed ? "b" : "a"; - Common::String name = Common::String::format("%s%s.zcr", _zNemCursorFileNames[i], pushedFlag.c_str()); - - changeCursor(ZorkCursor(name)); +void CursorManager::setItemID(int id) { + if (id != _item) { + if (id) { + Common::String file; + if (_engine->getGameId() == GID_NEMESIS) { + file = Common::String::format("%2.2d%s%c.zcr", id, "idle", 'a'); + _cursors[NUM_CURSORS][0] = ZorkCursor(_engine, file); + file = Common::String::format("%2.2d%s%c.zcr", id, "idle", 'b'); + _cursors[NUM_CURSORS][1] = ZorkCursor(_engine, file); + file = Common::String::format("%2.2d%s%c.zcr", id, "act", 'a'); + _cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file); + file = Common::String::format("%2.2d%s%c.zcr", id, "act", 'b'); + _cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file); + } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + file = Common::String::format("g0b%cc%2.2x1.zcr", 'a' , id); + _cursors[NUM_CURSORS][0] = ZorkCursor(_engine, file); + file = Common::String::format("g0b%cc%2.2x1.zcr", 'c' , id); + _cursors[NUM_CURSORS][1] = ZorkCursor(_engine, file); + file = Common::String::format("g0b%cc%2.2x1.zcr", 'b' , id); + _cursors[NUM_CURSORS + 1][0] = ZorkCursor(_engine, file); + file = Common::String::format("g0b%cc%2.2x1.zcr", 'd' , id); + _cursors[NUM_CURSORS + 1][1] = ZorkCursor(_engine, file); + } else return; - } - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { - if (cursorName.equals(_cursorNames[i])) { - _currentCursor = cursorName; - - if (!pushed) { - changeCursor(ZorkCursor(_zgiCursorFileNames[i])); - } else { - // ZGI flips not pushed/pushed between a/c and b/d - // It flips the 4th character of the name - char buffer[25]; - strcpy(buffer, _zgiCursorFileNames[i]); - buffer[3] += 2; - changeCursor(ZorkCursor(buffer)); - } - return; - } } + _item = id; + changeCursor(CursorIndex_Idle); } +} - // If we get here, something went wrong - warning("No cursor found for identifier %s", cursorName.c_str()); +void CursorManager::initialize() { + changeCursor(_cursors[CursorIndex_Idle][_cursorIsPushed]); + showMouse(true); } void CursorManager::changeCursor(const ZorkCursor &cursor) { @@ -122,31 +114,41 @@ void CursorManager::cursorDown(bool pushed) { return; _cursorIsPushed = pushed; - changeCursor(_currentCursor, pushed); -} -void CursorManager::setLeftCursor() { - changeCursor("leftarrow"); + changeCursor(_cursors[_currentCursor][_cursorIsPushed]); } -void CursorManager::setRightCursor() { - changeCursor("rightarrow"); -} +void CursorManager::changeCursor(int id) { + int _id = id; + + if (_item && + (_id == CursorIndex_Active || + _id == CursorIndex_Idle || + _id == CursorIndex_HandPu)) { -void CursorManager::setUpCursor() { - changeCursor("zuparrow"); + if (_id == CursorIndex_Idle) + _id = CursorIndex_ItemIdle; + else + _id = CursorIndex_ItemAct; + } + + if (_currentCursor != _id || + ((_id == CursorIndex_ItemAct || _id == CursorIndex_ItemIdle) && _lastitem != _item)) { + _currentCursor = _id; + _lastitem = _item; + changeCursor(_cursors[_currentCursor][_cursorIsPushed]); + } } -void CursorManager::setDownCursor() { - changeCursor("downarrow"); +int CursorManager::getCursorId(const Common::String &name) { + for (int i = 0; i < NUM_CURSORS; i++) + if (name.equals(_cursorNames[i])) + return i; + return CursorIndex_Idle; } -void CursorManager::revertToIdle() { - _currentCursor = "idle"; - if (!_cursorIsPushed) - CursorMan.replaceCursor(_idleCursor.getSurface(), _idleCursor.getWidth(), _idleCursor.getHeight(), _idleCursor.getHotspotX(), _idleCursor.getHotspotY(), _idleCursor.getKeyColor(), false, _pixelFormat); - else - changeCursor(_currentCursor, _cursorIsPushed); +void CursorManager::showMouse(bool vis) { + CursorMan.showMouse(vis); } } // End of namespace ZVision diff --git a/engines/zvision/cursors/cursor_manager.h b/engines/zvision/cursors/cursor_manager.h index 0576517f58de..a6f77cec64ef 100644 --- a/engines/zvision/cursors/cursor_manager.h +++ b/engines/zvision/cursors/cursor_manager.h @@ -8,12 +8,12 @@ * 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. @@ -36,6 +36,21 @@ namespace ZVision { class ZVision; +/** + * Mostly usable cursors + */ +enum CursorIndex { + CursorIndex_Active = 0, + CursorIndex_DownArr = 3, + CursorIndex_HandPu = 6, + CursorIndex_Idle = 11, + CursorIndex_Left = 12, + CursorIndex_Right = 13, + CursorIndex_UpArr = 17, + CursorIndex_ItemIdle = 18, + CursorIndex_ItemAct = 19 +}; + /** * Class to manage cursor changes. The actual changes have to be done * through CursorMan. Otherwise the cursor will disappear after GMM @@ -47,17 +62,17 @@ class CursorManager { CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat); private: - enum { - NUM_CURSORS = 18, - // WARNING: The index 11 is hardcoded. If you change the order of _cursorNames/_zgiCursorFileNames/_zNemCursorFileNames, you HAVE to change the index accordingly - IDLE_CURSOR_INDEX = 11 - }; + static const int NUM_CURSORS = 18; + + // 18 default cursors in up/down states, +2 for items idle/act cursors + ZorkCursor _cursors[NUM_CURSORS + 2][2]; ZVision *_engine; const Graphics::PixelFormat *_pixelFormat; - ZorkCursor _idleCursor; - Common::String _currentCursor; bool _cursorIsPushed; + int _item; + int _lastitem; + int _currentCursor; static const char *_cursorNames[]; static const char *_zgiCursorFileNames[]; @@ -68,19 +83,30 @@ class CursorManager { void initialize(); /** - * Parses a cursor name into a cursor file then creates and shows that cursor. - * It will use the current _isCursorPushed state to choose the correct cursor + * Change cursor to specified cursor ID. If item setted to not 0 and cursor id idle/acrive/handpu change cursor to item. * - * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] + * @param id Wanted cursor id. */ - void changeCursor(const Common::String &cursorName); + + void changeCursor(int id); + /** - * Parses a cursor name into a cursor file then creates and shows that cursor. + * Return founded id for string contains cursor name * - * @param cursorName The name of a cursor. This *HAS* to correspond to one of the entries in _cursorNames[] - * @param pushed Should the cursor be pushed (true) or not pushed (false) (Another way to say it: down or up) + * @param name Cursor name + * @return Id of cursor or idle cursor id if not found */ - void changeCursor(const Common::String &cursorName, bool pushed); + + int getCursorId(const Common::String &name); + + /** + * Load cursor for item by id, and try to change cursor to item cursor if it's not 0 + * + * @param id Item id or 0 for no item cursor + */ + + void setItemID(int id); + /** * Change the cursor to a certain push state. If the cursor is already in the specified push state, nothing will happen. * @@ -88,17 +114,12 @@ class CursorManager { */ void cursorDown(bool pushed); - /** Set the cursor to 'Left Arrow'. It will retain the current _isCursorPushed state */ - void setLeftCursor(); - /** Set the cursor to 'Right Arrow'. It will retain the current _isCursorPushed state */ - void setRightCursor(); - /** Set the cursor to 'Up Arrow'. It will retain the current _isCursorPushed state */ - void setUpCursor(); - /** Set the cursor to 'Down Arrow'. It will retain the current _isCursorPushed state */ - void setDownCursor(); - - /** Set the cursor to 'Idle'. It will retain the current _isCursorPushed state */ - void revertToIdle(); + /** + * Show or hide mouse cursor. + * + * @param vis Should the cursor be showed (true) or hide (false) + */ + void showMouse(bool vis); private: /** diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index 9961db121563..cd696557d9a1 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -73,10 +73,24 @@ static const ZVisionGameDescription gameDescriptions[] = { }, { - // Zork Grand Inquisitor English version + // Zork Nemesis English demo version + { + "znemesis", + "Demo", + AD_ENTRY1s("SCRIPTS.ZFS", "64f1e881394e9462305104f99513c833", 380539), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + GID_NEMESIS + }, + + { + // Zork Grand Inquisitor English CD version { "zgi", - 0, + "CD", AD_ENTRY1s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944), Common::EN_ANY, Common::kPlatformWindows, @@ -86,6 +100,34 @@ static const ZVisionGameDescription gameDescriptions[] = { GID_GRANDINQUISITOR }, + { + // Zork Grand Inquisitor English demo version + { + "zgi", + "Demo", + AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor English DVD version + { + "zgi", + "DVD", + AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + GID_GRANDINQUISITOR + }, + { AD_TABLE_END_MARKER, GID_NONE @@ -106,6 +148,13 @@ static const ExtraGuiOption ZVisionExtraGuiOption = { false }; +static const ExtraGuiOption ZVisionExtraGuiOption2 = { + _s("Double FPS"), + _s("Halve the update delay"), + "doublefps", + false +}; + class ZVisionMetaEngine : public AdvancedMetaEngine { public: ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames) { @@ -133,22 +182,22 @@ class ZVisionMetaEngine : public AdvancedMetaEngine { bool ZVisionMetaEngine::hasFeature(MetaEngineFeature f) const { return false; - /* - (f == kSupportsListSaves) || - (f == kSupportsLoadingDuringStartup) || - (f == kSupportsDeleteSave) || - (f == kSavesSupportMetaInfo) || - (f == kSavesSupportThumbnail) || - (f == kSavesSupportCreationDate) || - (f == kSavesSupportPlayTime); - */ + /* + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime); + */ } /*bool ZVision::ZVision::hasFeature(EngineFeature f) const { - return - (f == kSupportsRTL) || - (f == kSupportsLoadingDuringRuntime) || - (f == kSupportsSavingDuringRuntime); + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); }*/ bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { @@ -162,6 +211,7 @@ bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG const ExtraGuiOptions ZVisionMetaEngine::getExtraGuiOptions(const Common::String &target) const { ExtraGuiOptions options; options.push_back(ZVisionExtraGuiOption); + options.push_back(ZVisionExtraGuiOption2); return options; } @@ -173,23 +223,23 @@ SaveStateList ZVisionMetaEngine::listSaves(const char *target) const { Common::StringArray filenames; filenames = saveFileMan->listSavefiles(pattern.c_str()); - Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)*/ + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)*/ SaveStateList saveList; -/* for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) { - // Obtain the last 3 digits of the filename, since they correspond to the save slot - int slotNum = atoi(file->c_str() + file->size() - 3); - - if (slotNum >= 0 && slotNum <= 999) { - Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); - if (in) { - if (ZVision::ZVision::readSaveHeader(in, false, header) == ZVision::ZVision::kRSHENoError) { - saveList.push_back(SaveStateDescriptor(slotNum, header.description)); - } - delete in; - } - } - }*/ + /* for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 3); + + if (slotNum >= 0 && slotNum <= 999) { + Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); + if (in) { + if (ZVision::ZVision::readSaveHeader(in, false, header) == ZVision::ZVision::kRSHENoError) { + saveList.push_back(SaveStateDescriptor(slotNum, header.description)); + } + delete in; + } + } + }*/ return saveList; } @@ -209,17 +259,17 @@ void ZVisionMetaEngine::removeSaveState(const char *target, int slot) const { Common::String pattern = target; pattern += ".???"; filenames = saveFileMan->listSavefiles(pattern.c_str()); - Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { - // Obtain the last 3 digits of the filename, since they correspond to the save slot - int slotNum = atoi(file->c_str() + file->size() - 3); - - // Rename every slot greater than the deleted slot, - if (slotNum > slot) { - saveFileMan->renameSavefile(file->c_str(), filename.c_str()); - filename = ZVision::ZVision::getSavegameFilename(target, ++slot); - } + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 3); + + // Rename every slot greater than the deleted slot, + if (slotNum > slot) { + saveFileMan->renameSavefile(file->c_str(), filename.c_str()); + filename = ZVision::ZVision::getSavegameFilename(target, ++slot); + } } */ } @@ -230,34 +280,34 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str()); if (in) { - ZVision::ZVision::SaveHeader header; - ZVision::ZVision::kReadSaveHeaderError error; + ZVision::ZVision::SaveHeader header; + ZVision::ZVision::kReadSaveHeaderError error; - error = ZVision::ZVision::readSaveHeader(in, true, header); - delete in; + error = ZVision::ZVision::readSaveHeader(in, true, header); + delete in; - if (error == ZVision::ZVision::kRSHENoError) { - SaveStateDescriptor desc(slot, header.description); + if (error == ZVision::ZVision::kRSHENoError) { + SaveStateDescriptor desc(slot, header.description); - desc.setThumbnail(header.thumbnail); + desc.setThumbnail(header.thumbnail); - if (header.version > 0) { - int day = (header.saveDate >> 24) & 0xFF; - int month = (header.saveDate >> 16) & 0xFF; - int year = header.saveDate & 0xFFFF; + if (header.version > 0) { + int day = (header.saveDate >> 24) & 0xFF; + int month = (header.saveDate >> 16) & 0xFF; + int year = header.saveDate & 0xFFFF; - desc.setSaveDate(year, month, day); + desc.setSaveDate(year, month, day); - int hour = (header.saveTime >> 16) & 0xFF; - int minutes = (header.saveTime >> 8) & 0xFF; + int hour = (header.saveTime >> 16) & 0xFF; + int minutes = (header.saveTime >> 8) & 0xFF; - desc.setSaveTime(hour, minutes); + desc.setSaveTime(hour, minutes); - desc.setPlayTime(header.playTime * 1000); - } + desc.setPlayTime(header.playTime * 1000); + } - return desc; - } + return desc; + } } */ @@ -265,7 +315,7 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in } #if PLUGIN_ENABLED_DYNAMIC(ZVISION) - REGISTER_PLUGIN_DYNAMIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine); +REGISTER_PLUGIN_DYNAMIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine); #else - REGISTER_PLUGIN_STATIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine); +REGISTER_PLUGIN_STATIC(ZVISION, PLUGIN_TYPE_ENGINE, ZVisionMetaEngine); #endif diff --git a/engines/zvision/fonts/truetype_font.cpp b/engines/zvision/fonts/truetype_font.cpp index 45eaeeb2b410..638a8ed23378 100644 --- a/engines/zvision/fonts/truetype_font.cpp +++ b/engines/zvision/fonts/truetype_font.cpp @@ -8,12 +8,12 @@ * 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. @@ -27,9 +27,11 @@ #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" +#include "common/config-manager.h" #include "common/debug.h" #include "common/file.h" #include "common/system.h" +#include "common/unzip.h" #include "graphics/font.h" #include "graphics/fonts/ttf.h" @@ -39,9 +41,12 @@ namespace ZVision { TruetypeFont::TruetypeFont(ZVision *engine, int32 fontHeight) - : _fontHeight(fontHeight), - _font(0), - _lineHeight(0) { + : _engine(engine), + _fontHeight(fontHeight), + _font(0), + _lineHeight(0), + _maxCharWidth(0), + _maxCharHeight(0) { } TruetypeFont::~TruetypeFont(void) { @@ -111,4 +116,230 @@ Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, u return surface; } +StyledTTFont::StyledTTFont(ZVision *engine) { + _engine = engine; + _style = 0; + _font = NULL; + _lineHeight = 0; +} + +StyledTTFont::~StyledTTFont() { + if (_font) + delete _font; +} + +bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) { + _style = style; + return loadFont(fontName, point); +} + +bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { + Common::String newFontName; + if (fontName.matchString("*times new roman*", true) || fontName.matchString("*times*", true)) { + if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) + newFontName = "timesbi.ttf"; + else if (_style & STTF_BOLD) + newFontName = "timesbd.ttf"; + else if (_style & STTF_ITALIC) + newFontName = "timesi.ttf"; + else + newFontName = "times.ttf"; + + } else if (fontName.matchString("*courier new*", true) || fontName.matchString("*courier*", true) || fontName.matchString("*ZorkDeath*", true)) { + if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) + newFontName = "courbi.ttf"; + else if (_style & STTF_BOLD) + newFontName = "courbd.ttf"; + else if (_style & STTF_ITALIC) + newFontName = "couri.ttf"; + else + newFontName = "cour.ttf"; + + } else if (fontName.matchString("*century schoolbook*", true)) { + if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) + newFontName = "censcbkbi.ttf"; + else if (_style & STTF_BOLD) + newFontName = "censcbkbd.ttf"; + else if (_style & STTF_ITALIC) + newFontName = "censcbki.ttf"; + else + newFontName = "censcbk.ttf"; + + } else if (fontName.matchString("*garamond*", true)) { + if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) + newFontName = "garabi.ttf"; + else if (_style & STTF_BOLD) + newFontName = "garabd.ttf"; + else if (_style & STTF_ITALIC) + newFontName = "garai.ttf"; + else + newFontName = "gara.ttf"; + + } else if (fontName.matchString("*arial*", true) || fontName.matchString("*ZorkNormal*", true)) { + if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) + newFontName = "arialbi.ttf"; + else if (_style & STTF_BOLD) + newFontName = "arialbd.ttf"; + else if (_style & STTF_ITALIC) + newFontName = "ariali.ttf"; + else + newFontName = "arial.ttf"; + + } else { + debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); + newFontName = "arial.ttf"; + } + + bool sharp = (_style & STTF_SHARP) == STTF_SHARP; + + Common::File *file = _engine->getSearchManager()->openFile(newFontName); + + if (!file) { + Common::SeekableReadStream *themeFile = nullptr; + if (ConfMan.hasKey("themepath")) { + Common::FSNode themePath(ConfMan.get("themepath")); + if (themePath.exists()) { + Common::FSNode scummModern = themePath.getChild("scummmodern.zip"); + if (scummModern.exists()) { + themeFile = scummModern.createReadStream(); + } + } + } + if (!themeFile) { // Fallback : Search for ScummModern.zip in SearchMan. + themeFile = SearchMan.createReadStreamForMember("scummmodern.zip"); + } + if (themeFile) { + Common::Archive *themeArchive = Common::makeZipArchive(themeFile); + if (themeArchive->hasFile("FreeSans.ttf")) { + Common::SeekableReadStream *stream = nullptr; + stream = themeArchive->createReadStreamForMember("FreeSans.ttf"); + Graphics::Font *_newFont = Graphics::loadTTFFont(*stream, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display + if (_newFont) { + if (!_font) + delete _font; + _font = _newFont; + } + if (stream) + delete stream; + } + delete themeArchive; + themeArchive = nullptr; + } + } else { + Graphics::Font *_newFont = Graphics::loadTTFFont(*file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display + if (_newFont) { + if (!_font) + delete _font; + _font = _newFont; + } + delete file; + } + + _fntName = fontName; + _lineHeight = point; + + if (_font) + return true; + return false; +} + +void StyledTTFont::setStyle(uint newStyle) { + if ((_style & (STTF_BOLD | STTF_ITALIC | STTF_SHARP)) != (newStyle & (STTF_BOLD | STTF_ITALIC | STTF_SHARP))) { + _style = newStyle; + loadFont(_fntName, _lineHeight); + } else { + _style = newStyle; + } +} + +int StyledTTFont::getFontHeight() { + if (_font) + return _font->getFontHeight(); + return 0; +} + +int StyledTTFont::getMaxCharWidth() { + if (_font) + return _font->getMaxCharWidth(); + return 0; +} + +int StyledTTFont::getCharWidth(byte chr) { + if (_font) + return _font->getCharWidth(chr); + return 0; +} + +int StyledTTFont::getKerningOffset(byte left, byte right) { + if (_font) + return _font->getKerningOffset(left, right); + return 0; +} + +void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) { + if (_font) { + _font->drawChar(dst, chr, x, y, color); + if (_style & STTF_UNDERLINE) { + int16 pos = floor(_font->getFontHeight() * 0.87); + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); + } + if (_style & STTF_STRIKEOUT) { + int16 pos = floor(_font->getFontHeight() * 0.60); + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); + } + } +} + +void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) { + if (_font) { + _font->drawString(dst, str, x, y, w, color, align); + if (_style & STTF_UNDERLINE) { + int16 pos = floor(_font->getFontHeight() * 0.87); + int16 wd = MIN(_font->getStringWidth(str), w); + int16 stX = x; + if (align == Graphics::kTextAlignCenter) + stX += (w - wd) / 2; + else if (align == Graphics::kTextAlignRight) + stX += (w - wd); + + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + + dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); + } + if (_style & STTF_STRIKEOUT) { + int16 pos = floor(_font->getFontHeight() * 0.60); + int16 wd = MIN(_font->getStringWidth(str), w); + int16 stX = x; + if (align == Graphics::kTextAlignCenter) + stX += (w - wd) / 2; + else if (align == Graphics::kTextAlignRight) + stX += (w - wd); + + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + + dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); + } + } +} + +int StyledTTFont::getStringWidth(const Common::String &str) { + if (_font) + return _font->getStringWidth(str); + return 0; +} + +Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint32 color) { + Graphics::Surface *tmp = new Graphics::Surface; + if (_font) { + int16 w = _font->getStringWidth(str); + if (w && w < 1024) { + tmp->create(w, _font->getFontHeight(), _engine->_pixelFormat); + drawString(tmp, str, 0, 0, w, color); + } + } + return tmp; +} + } // End of namespace ZVision diff --git a/engines/zvision/fonts/truetype_font.h b/engines/zvision/fonts/truetype_font.h index 64f53a2c3b10..771c02a6dcda 100644 --- a/engines/zvision/fonts/truetype_font.h +++ b/engines/zvision/fonts/truetype_font.h @@ -43,12 +43,12 @@ class TruetypeFont { ~TruetypeFont(); private: -// ZVision *_engine; + ZVision *_engine; Graphics::Font *_font; int _lineHeight; -// size_t _maxCharWidth; -// size_t _maxCharHeight; + size_t _maxCharWidth; + size_t _maxCharHeight; public: int32 _fontHeight; @@ -76,6 +76,49 @@ class TruetypeFont { Graphics::Surface *drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap); }; +// Styled TTF +class StyledTTFont { +public: + StyledTTFont(ZVision *engine); + ~StyledTTFont(); + + enum { + STTF_BOLD = 1, + STTF_ITALIC = 2, + STTF_UNDERLINE = 4, + STTF_STRIKEOUT = 8, + STTF_SHARP = 16 + }; + +private: + ZVision *_engine; + Graphics::Font *_font; + int _lineHeight; + uint _style; + Common::String _fntName; + +public: + bool loadFont(const Common::String &fontName, int32 point); + bool loadFont(const Common::String &fontName, int32 point, uint style); + void setStyle(uint newStyle); + + int getFontHeight(); + int getMaxCharWidth(); + int getCharWidth(byte chr); + int getKerningOffset(byte left, byte right); + + void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color); + + void drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft); + int getStringWidth(const Common::String &str); + + Graphics::Surface *renderSolidText(const Common::String &str, uint32 color); + + bool isLoaded() { + return _font != NULL; + }; +}; + } // End of namespace ZVision #endif diff --git a/engines/zvision/graphics/effect.h b/engines/zvision/graphics/effect.h new file mode 100644 index 000000000000..c6653c6037a9 --- /dev/null +++ b/engines/zvision/graphics/effect.h @@ -0,0 +1,83 @@ +/* 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 EFFECT_H_INCLUDED +#define EFFECT_H_INCLUDED + +#include "common/rect.h" +#include "common/list.h" +#include "graphics/surface.h" + +#include "zvision/zvision.h" + +namespace ZVision { + +class ZVision; + +class Effect { +public: + + Effect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) { + _surface.create(_region.width(), _region.height(), _engine->_pixelFormat); + } + virtual ~Effect() {} + + uint32 getKey() { + return _key; + } + + Common::Rect getRegion() { + return _region; + } + + bool isPort() { + return _ported; + } + + virtual const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) { + return &_surface; + } + + virtual void update() {} + +protected: + ZVision *_engine; + uint32 _key; + Common::Rect _region; + bool _ported; + Graphics::Surface _surface; + +// Static member functions +public: + +}; + +struct EffectMapUnit { + uint32 count; + bool inEffect; +}; + +typedef Common::List EffectMap; + +} // End of namespace ZVision + +#endif // EFFECT_H_INCLUDED diff --git a/engines/zvision/graphics/effects/fog.cpp b/engines/zvision/graphics/effects/fog.cpp new file mode 100644 index 000000000000..b6c5e7b01125 --- /dev/null +++ b/engines/zvision/graphics/effects/fog.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 "common/scummsys.h" + +#include "zvision/graphics/effects/fog.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/scripting/script_manager.h" + + +namespace ZVision { + +FogFx::FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds): + Effect(engine, key, region, ported) { + + _map = Map; + + _r = 0; + _g = 0; + _b = 0; + + _pos = 0; + + if (_engine->getSearchManager()->hasFile(clouds)) + _engine->getRenderManager()->readImageToSurface(clouds, _fog); + else + _engine->getRenderManager()->readImageToSurface("cloud.tga", _fog); + + _mp.resize(_fog.h); + for (int16 i = 0; i < _fog.h; i++) { + _mp[i].resize(_fog.w); + for (int16 j = 0; j < _fog.w; j++) + _mp[i][j] = true; + } + + for (uint8 i = 0; i < 32; i++) + _colorMap[i] = 0; +} + +FogFx::~FogFx() { + if (_map) + delete _map; + + for (uint16 i = 0; i < _mp.size(); i++) + _mp[i].clear(); + _mp.clear(); +} + +const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) { + _surface.copyFrom(srcSubRect); + EffectMap::iterator it = _map->begin(); + + uint32 cnt = 0; + + for (uint16 j = 0; j < _surface.h; j++) { + uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j); + + for (uint16 i = 0; i < _surface.w; i++) { + if (it->inEffect) { + // Not 100% equivalent, but looks nice and not buggy + uint8 sr, sg, sb; + _engine->_pixelFormat.colorToRGB(lineBuf[i], sr, sg, sb); + uint16 fogColor = *(uint16 *)_fog.getBasePtr((i + _pos) % _fog.w, j); + uint8 dr, dg, db; + _engine->_pixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db); + uint16 fr = dr + sr; + if (fr > 255) + fr = 255; + uint16 fg = dg + sg; + if (fg > 255) + fg = 255; + uint16 fb = db + sb; + if (fb > 255) + fb = 255; + lineBuf[i] = _engine->_pixelFormat.RGBToColor(fr, fg, fb); + } + cnt++; + if (cnt >= it->count) { + it++; + cnt = 0; + } + if (it == _map->end()) + break; + } + if (it == _map->end()) + break; + } + + return &_surface; +} + +void FogFx::update() { + _pos += _engine->getScriptManager()->getStateValue(StateKey_EF9_Speed); + _pos %= _fog.w; + + uint8 dr = _engine->getScriptManager()->getStateValue(StateKey_EF9_R); + uint8 dg = _engine->getScriptManager()->getStateValue(StateKey_EF9_G); + uint8 db = _engine->getScriptManager()->getStateValue(StateKey_EF9_B); + dr = CLIP((int)dr, 0, 31); + dg = CLIP((int)dg, 0, 31); + db = CLIP((int)db, 0, 31); + + if (dr != _r || dg != _g || db != _b) { + if (_r > dr) + _r--; + else if (_r < dr) + _r++; + + if (_g > dg) + _g--; + else if (_g < dg) + _g++; + + if (_b > db) + _b--; + else if (_b < db) + _b++; + + // Not 100% equivalent, but looks nice and not buggy + + _colorMap[31] = _engine->_pixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3); + + for (uint8 i = 0; i < 31; i++) { + float perc = (float)i / 31.0; + uint8 cr = (float)_r * perc; + uint8 cg = (float)_g * perc; + uint8 cb = (float)_b * perc; + _colorMap[i] = _engine->_pixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3); + } + } + + for (uint16 j = 0; j < _fog.h; j++) { + uint16 *pix = (uint16 *)_fog.getBasePtr(0, j); + + for (uint16 i = 0; i < _fog.w; i++) { + if (_mp[j][i]) { + if ((pix[i] & 0x1F) == 0x1F) { + pix[i]--; + _mp[j][i] = false; + } else + pix[i]++; + } else { + if ((pix[i] & 0x1F) == 0) { + pix[i]++; + _mp[j][i] = true; + } else + pix[i]--; + } + } + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/effects/fog.h b/engines/zvision/graphics/effects/fog.h new file mode 100644 index 000000000000..45d6f9596dea --- /dev/null +++ b/engines/zvision/graphics/effects/fog.h @@ -0,0 +1,52 @@ +/* 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 ZVISION_FOG_H +#define ZVISION_FOG_H + +#include "zvision/graphics/effect.h" + +namespace ZVision { + +class ZVision; + +class FogFx : public Effect { +public: + + FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds); + ~FogFx(); + + const Graphics::Surface *draw(const Graphics::Surface &srcSubRect); + + void update(); + +private: + EffectMap *_map; + Graphics::Surface _fog; + uint8 _r, _g, _b; + int32 _pos; + Common::Array< Common::Array< bool > > _mp; + uint16 _colorMap[32]; +}; +} // End of namespace ZVision + +#endif // ZVISION_FOG_H diff --git a/engines/zvision/graphics/effects/light.cpp b/engines/zvision/graphics/effects/light.cpp new file mode 100644 index 000000000000..9bff07705173 --- /dev/null +++ b/engines/zvision/graphics/effects/light.cpp @@ -0,0 +1,110 @@ +/* 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 "zvision/graphics/effects/light.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" + + +namespace ZVision { + +LightFx::LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD, int8 maxD): + Effect(engine, key, region, ported) { + _map = Map; + _delta = delta; + _up = true; + _pos = 0; + + _minD = minD; + if (_minD < -delta) + _minD = -delta; + + _maxD = maxD; + if (_maxD > delta) + _maxD = delta; +} + +LightFx::~LightFx() { + if (_map) + delete _map; +} + +const Graphics::Surface *LightFx::draw(const Graphics::Surface &srcSubRect) { + _surface.copyFrom(srcSubRect); + EffectMap::iterator it = _map->begin(); + uint32 cnt = 0; + + uint32 dcolor = 0; + + if (_pos < 0) { + uint8 cc = ((-_pos) & 0x1F) << 3; + dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + } else { + uint8 cc = (_pos & 0x1F) << 3; + dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + } + + for (uint16 j = 0; j < _surface.h; j++) { + uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j); + + for (uint16 i = 0; i < _surface.w; i++) { + if (it->inEffect) { + if (_pos < 0) { + lineBuf[i] -= dcolor; + } else { + lineBuf[i] += dcolor; + } + } + cnt++; + if (cnt >= it->count) { + it++; + cnt = 0; + } + if (it == _map->end()) + break; + } + if (it == _map->end()) + break; + } + + return &_surface; +} + +void LightFx::update() { + if (_up) + _pos++; + else + _pos--; + + if (_pos <= _minD) { + _up = !_up; + _pos = _minD; + } else if (_pos >= _maxD) { + _up = !_up; + _pos = _maxD; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/effects/light.h b/engines/zvision/graphics/effects/light.h new file mode 100644 index 000000000000..ae87d66cb37e --- /dev/null +++ b/engines/zvision/graphics/effects/light.h @@ -0,0 +1,53 @@ +/* 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 LIGHTFX_H_INCLUDED +#define LIGHTFX_H_INCLUDED + +#include "zvision/graphics/effect.h" + +namespace ZVision { + +class ZVision; + +class LightFx : public Effect { +public: + + LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD = -127, int8 maxD = 127); + ~LightFx(); + + const Graphics::Surface *draw(const Graphics::Surface &srcSubRect); + + void update(); + +private: + EffectMap *_map; + int32 _delta; + bool _up; + int32 _pos; + + int8 _minD; + int8 _maxD; +}; +} // End of namespace ZVision + +#endif // LIGHTFX_H_INCLUDED diff --git a/engines/zvision/graphics/effects/wave.cpp b/engines/zvision/graphics/effects/wave.cpp new file mode 100644 index 000000000000..9f2fbb285c39 --- /dev/null +++ b/engines/zvision/graphics/effects/wave.cpp @@ -0,0 +1,146 @@ +/* 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 "zvision/graphics/effects/wave.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" + + +namespace ZVision { + +WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd): + Effect(engine, key, region, ported) { + + _frame = 0; + _frameCount = frames; + + _ampls.resize(_frameCount); + _halfWidth = _region.width() / 2; + _halfHeight = _region.height() / 2; + + int32 frmsize = _halfWidth * _halfHeight; + + float phase = 0; + + int16 quarterWidth = _halfWidth / 2; + int16 quarterHeight = _halfHeight / 2; + + for (int16 i = 0; i < _frameCount; i++) { + _ampls[i].resize(frmsize); + + for (int16 y = 0; y < _halfHeight; y++) + for (int16 x = 0; x < _halfWidth; x++) { + int16 dx = (x - quarterWidth); + int16 dy = (y - quarterHeight); + + _ampls[i][x + y * _halfWidth] = ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase); + } + phase += spd; + } +} + +WaveFx::~WaveFx() { + for (uint16 i = 0; i < _ampls.size(); i++) + _ampls[i].clear(); + _ampls.clear(); +} + +const Graphics::Surface *WaveFx::draw(const Graphics::Surface &srcSubRect) { + for (int16 y = 0; y < _halfHeight; y++) { + uint16 *abc = (uint16 *)_surface.getBasePtr(0, y); + uint16 *abc2 = (uint16 *)_surface.getBasePtr(0, _halfHeight + y); + uint16 *abc3 = (uint16 *)_surface.getBasePtr(_halfWidth, y); + uint16 *abc4 = (uint16 *)_surface.getBasePtr(_halfWidth, _halfHeight + y); + + for (int16 x = 0; x < _halfWidth; x++) { + int8 amnt = _ampls[_frame][x + _halfWidth * y]; + + int16 nX = x + amnt; + int16 nY = y + amnt; + + if (nX < 0) + nX = 0; + if (nX >= _region.width()) + nX = _region.width() - 1; + if (nY < 0) + nY = 0; + if (nY >= _region.height()) + nY = _region.height() - 1; + *abc = *(const uint16 *)srcSubRect.getBasePtr(nX, nY); + + nX = x + amnt + _halfWidth; + nY = y + amnt; + + if (nX < 0) + nX = 0; + if (nX >= _region.width()) + nX = _region.width() - 1; + if (nY < 0) + nY = 0; + if (nY >= _region.height()) + nY = _region.height() - 1; + *abc3 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY); + + nX = x + amnt; + nY = y + amnt + _halfHeight; + + if (nX < 0) + nX = 0; + if (nX >= _region.width()) + nX = _region.width() - 1; + if (nY < 0) + nY = 0; + if (nY >= _region.height()) + nY = _region.height() - 1; + *abc2 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY); + + nX = x + amnt + _halfWidth; + nY = y + amnt + _halfHeight; + + if (nX < 0) + nX = 0; + if (nX >= _region.width()) + nX = _region.width() - 1; + if (nY < 0) + nY = 0; + if (nY >= _region.height()) + nY = _region.height() - 1; + *abc4 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY); + + abc++; + abc2++; + abc3++; + abc4++; + } + } + + return &_surface; +} + +void WaveFx::update() { + _frame = (_frame + 1) % _frameCount; +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/effects/wave.h b/engines/zvision/graphics/effects/wave.h new file mode 100644 index 000000000000..2e813ed5b624 --- /dev/null +++ b/engines/zvision/graphics/effects/wave.h @@ -0,0 +1,51 @@ +/* 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 WAVEFX_H_INCLUDED +#define WAVEFX_H_INCLUDED + +#include "common/array.h" +#include "zvision/graphics/effect.h" + +namespace ZVision { + +class ZVision; + +class WaveFx : public Effect { +public: + + WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd); + ~WaveFx(); + + const Graphics::Surface *draw(const Graphics::Surface &srcSubRect); + + void update(); + +private: + int16 _frame; + int16 _frameCount; + int16 _halfWidth, _halfHeight; + Common::Array< Common::Array< int8 > > _ampls; +}; +} // End of namespace ZVision + +#endif // WAVEFX_H_INCLUDED diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp index aed30ea12cf4..05f8dec93716 100644 --- a/engines/zvision/graphics/render_manager.cpp +++ b/engines/zvision/graphics/render_manager.cpp @@ -1,28 +1,31 @@ /* 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. - * - */ +* +* 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 "zvision/zvision.h" #include "zvision/graphics/render_manager.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/text/text.h" #include "zvision/utility/lzss_read_stream.h" @@ -37,197 +40,222 @@ namespace ZVision { -RenderManager::RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) - : _system(system), - _workingWidth(workingWindow.width()), - _workingHeight(workingWindow.height()), - _screenCenterX(_workingWidth / 2), - _screenCenterY(_workingHeight / 2), - _workingWindow(workingWindow), - _pixelFormat(pixelFormat), - _backgroundWidth(0), - _backgroundHeight(0), - _backgroundInverseVelocity(0), - _backgroundOffset(0, 0), - _accumulatedVelocityMilliseconds(0), - _renderTable(_workingWidth, _workingHeight) { - - _workingWindowBuffer.create(_workingWidth, _workingHeight, _pixelFormat); - _backBuffer.create(windowWidth, windowHeight, pixelFormat); +RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) + : _engine(engine), + _system(engine->_system), + _wrkWidth(workingWindow.width()), + _wrkHeight(workingWindow.height()), + _screenCenterX(_wrkWidth / 2), + _screenCenterY(_wrkHeight / 2), + _workingWindow(workingWindow), + _pixelFormat(pixelFormat), + _bkgWidth(0), + _bkgHeight(0), + _bkgOff(0), + _renderTable(_wrkWidth, _wrkHeight) { + + _wrkWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); + _effectWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); + _outWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); + _menuWnd.create(windowWidth, workingWindow.top, _pixelFormat); + _subWnd.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat); + + _menuWndRect = Common::Rect(0, 0, windowWidth, workingWindow.top); + _subWndRect = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight); + + _subid = 0; } RenderManager::~RenderManager() { - _workingWindowBuffer.free(); - _currentBackground.free(); - _backBuffer.free(); - - for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { - iter->_value.data->free(); - delete iter->_value.data; - } + _curBkg.free(); + _wrkWnd.free(); + _effectWnd.free(); + _outWnd.free(); + _menuWnd.free(); + _subWnd.free(); } -void RenderManager::update(uint deltaTimeInMillis) { - // An inverse velocity of 0 would be infinitely fast, so we'll let 0 mean no velocity. - if (_backgroundInverseVelocity != 0) { - _accumulatedVelocityMilliseconds += deltaTimeInMillis; - - uint absVelocity = uint(abs(_backgroundInverseVelocity)); - - int numberOfSteps = 0; - while (_accumulatedVelocityMilliseconds >= absVelocity) { - _accumulatedVelocityMilliseconds -= absVelocity; - numberOfSteps++; +void RenderManager::renderBackbufferToScreen() { + Graphics::Surface *out = &_outWnd; + Graphics::Surface *in = &_wrkWnd; + Common::Rect outWndDirtyRect; + + if (!_effects.empty()) { + bool copied = false; + Common::Rect windRect(_wrkWidth, _wrkHeight); + for (effectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { + Common::Rect rect = (*it)->getRegion(); + Common::Rect scrPlace = rect; + if ((*it)->isPort()) + scrPlace = bkgRectToScreen(scrPlace); + if (windRect.intersects(scrPlace)) { + if (!copied) { + copied = true; + _effectWnd.copyFrom(_wrkWnd); + in = &_effectWnd; + } + const Graphics::Surface *post; + if ((*it)->isPort()) + post = (*it)->draw(_curBkg.getSubArea(rect)); + else + post = (*it)->draw(_effectWnd.getSubArea(rect)); + blitSurfaceToSurface(*post, _effectWnd, scrPlace.left, scrPlace.top); + scrPlace.clip(windRect); + if (_wrkWndDirtyRect .isEmpty()) { + _wrkWndDirtyRect = scrPlace; + } else { + _wrkWndDirtyRect.extend(scrPlace); + } + } } - - // Choose the direction of movement using the sign of the velocity - moveBackground(_backgroundInverseVelocity < 0 ? -numberOfSteps : numberOfSteps); } -} -void RenderManager::renderBackbufferToScreen() { - if (!_workingWindowDirtyRect.isEmpty()) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - _renderTable.mutateImage((uint16 *)_workingWindowBuffer.getPixels(), (uint16 *)_backBuffer.getBasePtr(_workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top), _backBuffer.w, _workingWindowDirtyRect); - } else { - _backBuffer.copyRectToSurface(_workingWindowBuffer.getBasePtr(_workingWindowDirtyRect.left, _workingWindowDirtyRect.top), _workingWindowBuffer.pitch, _workingWindow.left + _workingWindowDirtyRect.left, _workingWindow.top + _workingWindowDirtyRect.top, _workingWindowDirtyRect.width(), _workingWindowDirtyRect.height()); + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (!_wrkWndDirtyRect.isEmpty()) { + _renderTable.mutateImage(&_outWnd, in); + out = &_outWnd; + outWndDirtyRect = Common::Rect(_wrkWidth, _wrkHeight); } + } else { + out = in; + outWndDirtyRect = _wrkWndDirtyRect; + } - // Translate the working window dirty rect to screen coords - _workingWindowDirtyRect.translate(_workingWindow.left, _workingWindow.top); - // Then extend the backbuffer dirty rect to contain it - if (_backBufferDirtyRect.isEmpty()) { - _backBufferDirtyRect = _workingWindowDirtyRect; - } else { - _backBufferDirtyRect.extend(_workingWindowDirtyRect); - } - // Clear the dirty rect - _workingWindowDirtyRect = Common::Rect(); + if (!outWndDirtyRect.isEmpty()) { + _system->copyRectToScreen(out->getBasePtr(outWndDirtyRect.left, outWndDirtyRect.top), out->pitch, + outWndDirtyRect.left + _workingWindow.left, + outWndDirtyRect.top + _workingWindow.top, + outWndDirtyRect.width(), + outWndDirtyRect.height()); } +} - // TODO: Add menu rendering - - // Render alpha entries - processAlphaEntries(); +void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); - if (!_backBufferDirtyRect.isEmpty()) { - _system->copyRectToScreen(_backBuffer.getBasePtr(_backBufferDirtyRect.left, _backBufferDirtyRect.top), _backBuffer.pitch, _backBufferDirtyRect.left, _backBufferDirtyRect.top, _backBufferDirtyRect.width(), _backBufferDirtyRect.height()); - _backBufferDirtyRect = Common::Rect(); - } + blitSurfaceToBkg(surface, destX, destY); + surface.free(); } -void RenderManager::processAlphaEntries() { - // TODO: Add dirty rectangling support. AKA only draw an entry if the _backbufferDirtyRect intersects/contains the entry Rect +void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 keycolor) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); - for (AlphaEntryMap::iterator iter = _alphaDataEntries.begin(); iter != _alphaDataEntries.end(); ++iter) { - uint32 destOffset = 0; - uint32 sourceOffset = 0; - uint16 *backbufferPtr = (uint16 *)_backBuffer.getBasePtr(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top); - uint16 *entryPtr = (uint16 *)iter->_value.data->getPixels(); + blitSurfaceToBkg(surface, destX, destY, keycolor); + surface.free(); +} - for (int32 y = 0; y < iter->_value.height; ++y) { - for (int32 x = 0; x < iter->_value.width; ++x) { - uint16 color = entryPtr[sourceOffset + x]; - if (color != iter->_value.alphaColor) { - backbufferPtr[destOffset + x] = color; - } - } +void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, int16 keyX, int16 keyY) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); - destOffset += _backBuffer.w; - sourceOffset += iter->_value.width; - } + uint16 keycolor = *(uint16 *)surface.getBasePtr(keyX, keyY); - if (_backBufferDirtyRect.isEmpty()) { - _backBufferDirtyRect = Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height); - } else { - _backBufferDirtyRect.extend(Common::Rect(iter->_value.destX + _workingWindow.left, iter->_value.destY + _workingWindow.top, iter->_value.destX + _workingWindow.left + iter->_value.width, iter->_value.destY + _workingWindow.top + iter->_value.height)); - } - } + blitSurfaceToBkg(surface, destX, destY, keycolor); + surface.free(); } -void RenderManager::clearWorkingWindowTo555Color(uint16 color) { - uint32 workingWindowSize = _workingWidth * _workingHeight; - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); - uint16 colorIn565 = _pixelFormat.RGBToColor(r, g, b); - uint16 *bufferPtr = (uint16 *)_workingWindowBuffer.getPixels(); +void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { + Common::File file; - for (uint32 i = 0; i < workingWindowSize; ++i) { - bufferPtr[i] = colorIn565; + if (!_engine->getSearchManager()->openFile(file, fileName)) { + warning("Could not open file %s", fileName.c_str()); + return; } -} -void RenderManager::renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { - int16 subRectX = 0; - int16 subRectY = 0; + // Read the magic number + // Some files are true TGA, while others are TGZ + uint32 fileType = file.readUint32BE(); - // Take care of negative destinations - if (destinationX < 0) { - subRectX = -destinationX; - destinationX = 0; - } else if (destinationX >= surface.w) { - // Take care of extreme positive destinations - destinationX -= surface.w; - } + uint32 imageWidth; + uint32 imageHeight; + Image::TGADecoder tga; + uint16 *buffer; + bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; + // All ZVision images are in RGB 555 + Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + destination.format = pixelFormat555; - // Take care of negative destinations - if (destinationY < 0) { - subRectY = -destinationY; - destinationY = 0; - } else if (destinationY >= surface.h) { - // Take care of extreme positive destinations - destinationY -= surface.h; - } + bool isTGZ; - if (wrap) { - _backgroundWidth = surface.w; - _backgroundHeight = surface.h; + // Check for TGZ files + if (fileType == MKTAG('T', 'G', 'Z', '\0')) { + isTGZ = true; - if (destinationX > 0) { - // Move destinationX to 0 - subRectX = surface.w - destinationX; - destinationX = 0; - } + // TGZ files have a header and then Bitmap data that is compressed with LZSS + uint32 decompressedSize = file.readSint32LE(); + imageWidth = file.readSint32LE(); + imageHeight = file.readSint32LE(); + + LzssReadStream lzssStream(&file); + buffer = (uint16 *)(new uint16[decompressedSize]); + lzssStream.read(buffer, decompressedSize); + } else { + isTGZ = false; + + // Reset the cursor + file.seek(0); - if (destinationY > 0) { - // Move destinationY to 0 - subRectY = surface.h - destinationY; - destinationY = 0; + // Decode + if (!tga.loadStream(file)) { + warning("Error while reading TGA image"); + return; } + + Graphics::Surface tgaSurface = *(tga.getSurface()); + imageWidth = tgaSurface.w; + imageHeight = tgaSurface.h; + + buffer = (uint16 *)tgaSurface.getPixels(); } - // Clip subRect to working window bounds - Common::Rect subRect(subRectX, subRectY, subRectX + _workingWidth, subRectY + _workingHeight); + // Flip the width and height if transposed + if (isTransposed) { + uint16 temp = imageHeight; + imageHeight = imageWidth; + imageWidth = temp; + } - if (!wrap) { - // Clip to image bounds - subRect.clip(surface.w, surface.h); + // If the destination internal buffer is the same size as what we're copying into it, + // there is no need to free() and re-create + if (imageWidth != destination.w || imageHeight != destination.h) { + destination.create(imageWidth, imageHeight, pixelFormat555); } - // Check destRect for validity - if (!subRect.isValidRect() || subRect.isEmpty()) - return; + // If transposed, 'un-transpose' the data while copying it to the destination + // Otherwise, just do a simple copy + if (isTransposed) { + uint16 *dest = (uint16 *)destination.getPixels(); - copyRectToWorkingWindow((const uint16 *)surface.getBasePtr(subRect.left, subRect.top), destinationX, destinationY, surface.w, subRect.width(), subRect.height()); -} + for (uint32 y = 0; y < imageHeight; ++y) { + uint32 columnIndex = y * imageWidth; -void RenderManager::renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap) { - Graphics::Surface surface; - readImageToSurface(fileName, surface); + for (uint32 x = 0; x < imageWidth; ++x) { + dest[columnIndex + x] = buffer[x * imageHeight + y]; + } + } + } else { + memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); + } - renderSubRectToScreen(surface, destinationX, destinationY, wrap); -} + // Cleanup + if (isTGZ) { + delete[] buffer; + } else { + tga.destroy(); + } -void RenderManager::renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) { - renderSubRectToScreen(surface, destinationX, destinationY, wrap); + // Convert in place to RGB 565 from RGB 555 + destination.convertToInPlace(_pixelFormat); } -void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { +void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) { Common::File file; - if (!file.open(fileName)) { + if (!_engine->getSearchManager()->openFile(file, fileName)) { warning("Could not open file %s", fileName.c_str()); return; } @@ -240,7 +268,6 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: uint32 imageHeight; Image::TGADecoder tga; uint16 *buffer; - bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; // All ZVision images are in RGB 555 Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); destination.format = pixelFormat555; @@ -279,7 +306,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: } // Flip the width and height if transposed - if (isTransposed) { + if (transposed) { uint16 temp = imageHeight; imageHeight = imageWidth; imageWidth = temp; @@ -293,7 +320,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: // If transposed, 'un-transpose' the data while copying it to the destination // Otherwise, just do a simple copy - if (isTransposed) { + if (transposed) { uint16 *dest = (uint16 *)destination.getPixels(); for (uint32 y = 0; y < imageHeight; ++y) { @@ -318,209 +345,726 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: destination.convertToInPlace(_pixelFormat); } -void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height) { - uint32 destOffset = 0; - uint32 sourceOffset = 0; - uint16 *workingWindowBufferPtr = (uint16 *)_workingWindowBuffer.getBasePtr(destX, destY); +const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { + if (_workingWindow.contains(point)) { + // Convert from screen space to working window space + Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top)); + + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint); + } - for (int32 y = 0; y < height; ++y) { - for (int32 x = 0; x < width; ++x) { - workingWindowBufferPtr[destOffset + x] = buffer[sourceOffset + x]; + if (state == RenderTable::PANORAMA) { + newPoint += (Common::Point(_bkgOff - _screenCenterX, 0)); + } else if (state == RenderTable::TILT) { + newPoint += (Common::Point(0, _bkgOff - _screenCenterY)); } - destOffset += _workingWidth; - sourceOffset += imageWidth; - } + if (_bkgWidth) + newPoint.x %= _bkgWidth; + if (_bkgHeight) + newPoint.y %= _bkgHeight; - if (_workingWindowDirtyRect.isEmpty()) { - _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height); + if (newPoint.x < 0) + newPoint.x += _bkgWidth; + if (newPoint.y < 0) + newPoint.y += _bkgHeight; + + return newPoint; } else { - _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height)); + return Common::Point(0, 0); } +} + +RenderTable *RenderManager::getRenderTable() { + return &_renderTable; +} + +void RenderManager::setBackgroundImage(const Common::String &fileName) { + readImageToSurface(fileName, _curBkg); + _bkgWidth = _curBkg.w; + _bkgHeight = _curBkg.h; + _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); +} + +void RenderManager::setBackgroundPosition(int offset) { + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::TILT || state == RenderTable::PANORAMA) + if (_bkgOff != offset) + _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); + _bkgOff = offset; - // TODO: Remove this from release. It's here to make sure code that uses this function clips their destinations correctly - assert(_workingWindowDirtyRect.width() <= _workingWidth && _workingWindowDirtyRect.height() <= _workingHeight); + _engine->getScriptManager()->setStateValue(StateKey_ViewPos, offset); } -void RenderManager::copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber) { - AlphaDataEntry entry; - entry.alphaColor = alphaColor; - entry.data = new Graphics::Surface(); - entry.data->create(width, height, _pixelFormat); - entry.destX = destX; - entry.destY = destY; - entry.width = width; - entry.height = height; +uint32 RenderManager::getCurrentBackgroundOffset() { + RenderTable::RenderState state = _renderTable.getRenderState(); + + if (state == RenderTable::PANORAMA) { + return _bkgOff; + } else if (state == RenderTable::TILT) { + return _bkgOff; + } else { + return 0; + } +} + +Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { + Graphics::Surface *tranposedSurface = new Graphics::Surface(); + tranposedSurface->create(surface->h, surface->w, surface->format); + + const uint16 *source = (const uint16 *)surface->getPixels(); + uint16 *dest = (uint16 *)tranposedSurface->getPixels(); - uint32 sourceOffset = 0; - uint32 destOffset = 0; - uint16 *surfacePtr = (uint16 *)entry.data->getPixels(); + for (uint32 y = 0; y < tranposedSurface->h; ++y) { + uint32 columnIndex = y * tranposedSurface->w; - for (int32 y = 0; y < height; ++y) { - for (int32 x = 0; x < width; ++x) { - surfacePtr[destOffset + x] = buffer[sourceOffset + x]; + for (uint32 x = 0; x < tranposedSurface->w; ++x) { + dest[columnIndex + x] = source[x * surface->w + y]; } + } + + return tranposedSurface; +} + +void RenderManager::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight) { + assert(bytesPerPixel == 1 || bytesPerPixel == 2); - destOffset += width; - sourceOffset += imageWidth; + const float xscale = (float)srcWidth / (float)dstWidth; + const float yscale = (float)srcHeight / (float)dstHeight; + + if (bytesPerPixel == 1) { + const byte *srcPtr = (const byte *)src; + byte *dstPtr = (byte *)dst; + for (uint32 y = 0; y < dstHeight; ++y) { + for (uint32 x = 0; x < dstWidth; ++x) { + *dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth]; + dstPtr++; + } + } + } else if (bytesPerPixel == 2) { + const uint16 *srcPtr = (const uint16 *)src; + uint16 *dstPtr = (uint16 *)dst; + for (uint32 y = 0; y < dstHeight; ++y) { + for (uint32 x = 0; x < dstWidth; ++x) { + *dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth]; + dstPtr++; + } + } } +} + +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) { + + if (src.format != dst.format) + return; + + Common::Rect srcRect = _srcRect; + if (srcRect.isEmpty()) + srcRect = Common::Rect(src.w, src.h); + srcRect.clip(src.w, src.h); + Common::Rect dstRect = Common::Rect(-_x + srcRect.left , -_y + srcRect.top, -_x + srcRect.left + dst.w, -_y + srcRect.top + dst.h); + srcRect.clip(dstRect); + + if (srcRect.isEmpty() || !srcRect.isValidRect()) + return; + + // Copy srcRect from src surface to dst surface + const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + + int xx = _x; + int yy = _y; + + if (xx < 0) + xx = 0; + if (yy < 0) + yy = 0; - _alphaDataEntries[idNumber] = entry; + if (_x >= dst.w || _y >= dst.h) + return; + + byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); + + int32 w = srcRect.width(); + int32 h = srcRect.height(); + + for (int32 y = 0; y < h; y++) { + memcpy(dstBuffer, srcBuffer, w * src.format.bytesPerPixel); + srcBuffer += src.pitch; + dstBuffer += dst.pitch; + } } -Common::Rect RenderManager::renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) { - AlphaDataEntry entry; - entry.alphaColor = 0; - entry.destX = destX; - entry.destY = destY; +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) { + + if (src.format != dst.format) + return; + + Common::Rect srcRect = _srcRect; + if (srcRect.isEmpty()) + srcRect = Common::Rect(src.w, src.h); + srcRect.clip(src.w, src.h); + Common::Rect dstRect = Common::Rect(-_x + srcRect.left , -_y + srcRect.top, -_x + srcRect.left + dst.w, -_y + srcRect.top + dst.h); + srcRect.clip(dstRect); + + if (srcRect.isEmpty() || !srcRect.isValidRect()) + return; + + + + uint32 _keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); + + // Copy srcRect from src surface to dst surface + const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + + int xx = _x; + int yy = _y; + + if (xx < 0) + xx = 0; + if (yy < 0) + yy = 0; + + if (_x >= dst.w || _y >= dst.h) + return; + + byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); + + int32 w = srcRect.width(); + int32 h = srcRect.height(); + + for (int32 y = 0; y < h; y++) { + switch (src.format.bytesPerPixel) { + case 1: { + const uint *srcTemp = (const uint *)srcBuffer; + uint *dstTemp = (uint *)dstBuffer; + for (int32 x = 0; x < w; x++) { + if (*srcTemp != _keycolor) + *dstTemp = *srcTemp; + srcTemp++; + dstTemp++; + } + } + break; + + case 2: { + const uint16 *srcTemp = (const uint16 *)srcBuffer; + uint16 *dstTemp = (uint16 *)dstBuffer; + for (int32 x = 0; x < w; x++) { + if (*srcTemp != _keycolor) + *dstTemp = *srcTemp; + srcTemp++; + dstTemp++; + } + } + break; + + case 4: { + const uint32 *srcTemp = (const uint32 *)srcBuffer; + uint32 *dstTemp = (uint32 *)dstBuffer; + for (int32 x = 0; x < w; x++) { + if (*srcTemp != _keycolor) + *dstTemp = *srcTemp; + srcTemp++; + dstTemp++; + } + } + break; + + default: + break; + } + srcBuffer += src.pitch; + dstBuffer += dst.pitch; + } +} - // Draw the text to the working window - entry.data = font->drawTextToSurface(text, textColor, maxWidth, maxHeight, align, wrap); - entry.width = entry.data->w; - entry.height = entry.data->h; +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, dst, x, y); +} - _alphaDataEntries[idNumber] = entry; +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, dst, x, y, colorkey); +} - return Common::Rect(destX, destY, destX + entry.width, destY + entry.height); +void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, _curBkg, x, y); + Common::Rect dirty(src.w, src.h); + dirty.translate(x, y); + if (_bkgDirtyRect.isEmpty()) + _bkgDirtyRect = dirty; + else + _bkgDirtyRect.extend(dirty); } -const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { - // Convert from screen space to working window space - Common::Point newPoint(point - Common::Point(_workingWindow.left, _workingWindow.top)); +void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, _curBkg, x, y, colorkey); + Common::Rect dirty(src.w, src.h); + dirty.translate(x, y); + if (_bkgDirtyRect.isEmpty()) + _bkgDirtyRect = dirty; + else + _bkgDirtyRect.extend(dirty); +} - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint); +void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect) { + if (src.w == _dstRect.width() && src.h == _dstRect.height()) + blitSurfaceToBkg(src, _dstRect.left, _dstRect.top); + else { + Graphics::Surface *tmp = new Graphics::Surface; + tmp->create(_dstRect.width(), _dstRect.height(), src.format); + scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); + blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top); + tmp->free(); + delete tmp; } +} - if (state == RenderTable::PANORAMA) { - newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset); - } else if (state == RenderTable::TILT) { - newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset); +void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey) { + if (src.w == _dstRect.width() && src.h == _dstRect.height()) + blitSurfaceToBkg(src, _dstRect.left, _dstRect.top, colorkey); + else { + Graphics::Surface *tmp = new Graphics::Surface; + tmp->create(_dstRect.width(), _dstRect.height(), src.format); + scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); + blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top, colorkey); + tmp->free(); + delete tmp; } +} + +void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, _menuWnd, x, y); + Common::Rect dirty(src.w, src.h); + dirty.translate(x, y); + if (_menuWndDirtyRect.isEmpty()) + _menuWndDirtyRect = dirty; + else + _menuWndDirtyRect.extend(dirty); +} + +void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, _menuWnd, x, y, colorkey); + Common::Rect dirty(src.w, src.h); + dirty.translate(x, y); + if (_menuWndDirtyRect.isEmpty()) + _menuWndDirtyRect = dirty; + else + _menuWndDirtyRect.extend(dirty); +} + +Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) { + Common::Rect dst = rect; + dst.clip(_bkgWidth, _bkgHeight); + + if (dst.isEmpty() || !dst.isValidRect()) + return NULL; + + Graphics::Surface *srf = new Graphics::Surface; + srf->create(dst.width(), dst.height(), _curBkg.format); + + srf->copyRectToSurface(_curBkg, 0, 0, Common::Rect(dst)); + + return srf; +} + +Graphics::Surface *RenderManager::loadImage(Common::String &file) { + Graphics::Surface *tmp = new Graphics::Surface; + readImageToSurface(file, *tmp); + return tmp; +} - if (newPoint.x < 0) - newPoint.x += _backgroundWidth; - else if (newPoint.x >= _backgroundWidth) - newPoint.x -= _backgroundWidth; - if (newPoint.y < 0) - newPoint.y += _backgroundHeight; - else if (newPoint.y >= _backgroundHeight) - newPoint.y -= _backgroundHeight; +Graphics::Surface *RenderManager::loadImage(const char *file) { + Common::String str = Common::String(file); + return loadImage(str); +} - return newPoint; +Graphics::Surface *RenderManager::loadImage(Common::String &file, bool transposed) { + Graphics::Surface *tmp = new Graphics::Surface; + readImageToSurface(file, *tmp, transposed); + return tmp; } -const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) { - Common::Point newPoint(point); +Graphics::Surface *RenderManager::loadImage(const char *file, bool transposed) { + Common::String str = Common::String(file); + return loadImage(str, transposed); +} +void RenderManager::prepareBkg() { + _bkgDirtyRect.clip(_bkgWidth, _bkgHeight); RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA) { - newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset); + Common::Rect viewPort(_wrkWidth, _wrkHeight); + viewPort.translate(-(_screenCenterX - _bkgOff), 0); + Common::Rect drawRect = _bkgDirtyRect; + drawRect.clip(viewPort); + + if (!drawRect.isEmpty()) + blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX - _bkgOff + drawRect.left, drawRect.top); + + _wrkWndDirtyRect = _bkgDirtyRect; + _wrkWndDirtyRect.translate(_screenCenterX - _bkgOff, 0); + + if (_bkgOff < _screenCenterX) { + viewPort.moveTo(-(_screenCenterX - (_bkgOff + _bkgWidth)), 0); + drawRect = _bkgDirtyRect; + drawRect.clip(viewPort); + + if (!drawRect.isEmpty()) + blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX - (_bkgOff + _bkgWidth) + drawRect.left, drawRect.top); + + Common::Rect tmp = _bkgDirtyRect; + tmp.translate(_screenCenterX - (_bkgOff + _bkgWidth), 0); + if (!tmp.isEmpty()) + _wrkWndDirtyRect.extend(tmp); + + } else if (_bkgWidth - _bkgOff < _screenCenterX) { + viewPort.moveTo(-(_screenCenterX + _bkgWidth - _bkgOff), 0); + drawRect = _bkgDirtyRect; + drawRect.clip(viewPort); + + if (!drawRect.isEmpty()) + blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX + _bkgWidth - _bkgOff + drawRect.left, drawRect.top); + + Common::Rect tmp = _bkgDirtyRect; + tmp.translate(_screenCenterX + _bkgWidth - _bkgOff, 0); + if (!tmp.isEmpty()) + _wrkWndDirtyRect.extend(tmp); + + } } else if (state == RenderTable::TILT) { - newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset); + Common::Rect viewPort(_wrkWidth, _wrkHeight); + viewPort.translate(0, -(_screenCenterY - _bkgOff)); + Common::Rect drawRect = _bkgDirtyRect; + drawRect.clip(viewPort); + if (!drawRect.isEmpty()) + blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, drawRect.left, _screenCenterY - _bkgOff + drawRect.top); + + _wrkWndDirtyRect = _bkgDirtyRect; + _wrkWndDirtyRect.translate(0, _screenCenterY - _bkgOff); + + } else { + if (!_bkgDirtyRect.isEmpty()) + blitSurfaceToSurface(_curBkg, _bkgDirtyRect, _wrkWnd, _bkgDirtyRect.left, _bkgDirtyRect.top); + _wrkWndDirtyRect = _bkgDirtyRect; } - return newPoint; + _bkgDirtyRect = Common::Rect(); + + _wrkWndDirtyRect.clip(_wrkWidth, _wrkHeight); +} + +void RenderManager::clearMenuSurface() { + _menuWndDirtyRect = Common::Rect(0, 0, _menuWnd.w, _menuWnd.h); + _menuWnd.fillRect(_menuWndDirtyRect, 0); } -bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) { - if (!_workingWindow.contains(rect)) { - return false; +void RenderManager::clearMenuSurface(const Common::Rect &r) { + if (_menuWndDirtyRect.isEmpty()) + _menuWndDirtyRect = r; + else + _menuWndDirtyRect.extend(r); + _menuWnd.fillRect(r, 0); +} + +void RenderManager::renderMenuToScreen() { + if (!_menuWndDirtyRect.isEmpty()) { + _menuWndDirtyRect.clip(Common::Rect(_menuWnd.w, _menuWnd.h)); + if (!_menuWndDirtyRect.isEmpty()) + _system->copyRectToScreen(_menuWnd.getBasePtr(_menuWndDirtyRect.left, _menuWndDirtyRect.top), _menuWnd.pitch, + _menuWndDirtyRect.left + _menuWndRect.left, + _menuWndDirtyRect.top + _menuWndRect.top, + _menuWndDirtyRect.width(), + _menuWndDirtyRect.height()); + _menuWndDirtyRect = Common::Rect(); } +} + +uint16 RenderManager::createSubArea(const Common::Rect &area) { + _subid++; + + oneSub sub; + sub.redraw = false; + sub.timer = -1; + sub.todelete = false; + sub.r = area; - // We can't clip against the actual working window rect because it's in screen space - // But rect is in working window space - rect.clip(_workingWidth, _workingHeight); - return true; + _subsList[_subid] = sub; + + return _subid; } -RenderTable *RenderManager::getRenderTable() { - return &_renderTable; +uint16 RenderManager::createSubArea() { + _subid++; + + oneSub sub; + sub.redraw = false; + sub.timer = -1; + sub.todelete = false; + sub.r = Common::Rect(_subWndRect.left, _subWndRect.top, _subWndRect.right, _subWndRect.bottom); + sub.r.translate(-_workingWindow.left, -_workingWindow.top); + + _subsList[_subid] = sub; + + return _subid; } -void RenderManager::setBackgroundImage(const Common::String &fileName) { - readImageToSurface(fileName, _currentBackground); +void RenderManager::deleteSubArea(uint16 id) { + if (_subsList.contains(id)) + _subsList[id].todelete = true; +} - moveBackground(0); +void RenderManager::deleteSubArea(uint16 id, int16 delay) { + if (_subsList.contains(id)) + _subsList[id].timer = delay; } -void RenderManager::setBackgroundPosition(int offset) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::TILT) { - _backgroundOffset.x = 0; - _backgroundOffset.y = offset; - } else if (state == RenderTable::PANORAMA) { - _backgroundOffset.x = offset; - _backgroundOffset.y = 0; - } else { - _backgroundOffset.x = 0; - _backgroundOffset.y = 0; +void RenderManager::updateSubArea(uint16 id, const Common::String &txt) { + if (_subsList.contains(id)) { + oneSub *sub = &_subsList[id]; + sub->txt = txt; + sub->redraw = true; } } -void RenderManager::setBackgroundVelocity(int velocity) { - // setBackgroundVelocity(0) will be called quite often, so make sure - // _backgroundInverseVelocity isn't already 0 to prevent an extraneous assignment - if (velocity == 0) { - if (_backgroundInverseVelocity != 0) { - _backgroundInverseVelocity = 0; +void RenderManager::processSubs(uint16 deltatime) { + bool redraw = false; + for (subMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { + if (it->_value.timer != -1) { + it->_value.timer -= deltatime; + if (it->_value.timer <= 0) + it->_value.todelete = true; + } + if (it->_value.todelete) { + _subsList.erase(it); + redraw = true; + } else if (it->_value.redraw) { + redraw = true; } - } else { - _backgroundInverseVelocity = 1000 / velocity; } -} -void RenderManager::moveBackground(int offset) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::TILT) { - _backgroundOffset += Common::Point(0, offset); + if (redraw) { + _subWnd.fillRect(Common::Rect(_subWnd.w, _subWnd.h), 0); + + for (subMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { + oneSub *sub = &it->_value; + if (sub->txt.size()) { + Graphics::Surface *rndr = new Graphics::Surface(); + rndr->create(sub->r.width(), sub->r.height(), _pixelFormat); + _engine->getTextRenderer()->drawTxtInOneLine(sub->txt, *rndr); + blitSurfaceToSurface(*rndr, _subWnd, sub->r.left - _subWndRect.left + _workingWindow.left, sub->r.top - _subWndRect.top + _workingWindow.top); + rndr->free(); + delete rndr; + } + sub->redraw = false; + } - _backgroundOffset.y = CLIP(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY); + _system->copyRectToScreen(_subWnd.getPixels(), _subWnd.pitch, + _subWndRect.left, + _subWndRect.top, + _subWnd.w, + _subWnd.h); + } +} - renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true); - } else if (state == RenderTable::PANORAMA) { - _backgroundOffset += Common::Point(offset, 0); +Common::Point RenderManager::getBkgSize() { + return Common::Point(_bkgWidth, _bkgHeight); +} - if (_backgroundOffset.x <= -_backgroundWidth) - _backgroundOffset.x += _backgroundWidth; - else if (_backgroundOffset.x >= _backgroundWidth) - _backgroundOffset.x -= _backgroundWidth; +void RenderManager::addEffect(Effect *_effect) { + _effects.push_back(_effect); +} - renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true); - } else { - renderImageToScreen(_currentBackground, 0, 0); +void RenderManager::deleteEffect(uint32 ID) { + for (effectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { + if ((*it)->getKey() == ID) { + delete *it; + it = _effects.erase(it); + } } } -uint32 RenderManager::getCurrentBackgroundOffset() { +Common::Rect RenderManager::bkgRectToScreen(const Common::Rect &src) { + Common::Rect tmp = src; RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA) { - return _backgroundOffset.x; + if (_bkgOff < _screenCenterX) { + Common::Rect rScreen(_screenCenterX + _bkgOff, _wrkHeight); + Common::Rect lScreen(_wrkWidth - rScreen.width(), _wrkHeight); + lScreen.translate(_bkgWidth - lScreen.width(), 0); + lScreen.clip(src); + rScreen.clip(src); + if (rScreen.width() < lScreen.width()) { + tmp.translate(_screenCenterX - _bkgOff - _bkgWidth, 0); + } else { + tmp.translate(_screenCenterX - _bkgOff, 0); + } + } else if (_bkgWidth - _bkgOff < _screenCenterX) { + Common::Rect rScreen(_screenCenterX - (_bkgWidth - _bkgOff), _wrkHeight); + Common::Rect lScreen(_wrkWidth - rScreen.width(), _wrkHeight); + lScreen.translate(_bkgWidth - lScreen.width(), 0); + lScreen.clip(src); + rScreen.clip(src); + if (lScreen.width() < rScreen.width()) { + tmp.translate(_screenCenterX + (_bkgWidth - _bkgOff), 0); + } else { + tmp.translate(_screenCenterX - _bkgOff, 0); + } + } else { + tmp.translate(_screenCenterX - _bkgOff, 0); + } } else if (state == RenderTable::TILT) { - return _backgroundOffset.y; - } else { - return 0; + tmp.translate(0, (_screenCenterY - _bkgOff)); } + + return tmp; } -Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { - Graphics::Surface *tranposedSurface = new Graphics::Surface(); - tranposedSurface->create(surface->h, surface->w, surface->format); +EffectMap *RenderManager::makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *_minComp, int8 *_maxComp) { + Common::Rect bkgRect(_bkgWidth, _bkgHeight); + if (!bkgRect.contains(xy)) + return NULL; + + if (!bkgRect.intersects(rect)) + return NULL; + + uint16 color = *(uint16 *)_curBkg.getBasePtr(xy.x, xy.y); + uint8 stC1, stC2, stC3; + _curBkg.format.colorToRGB(color, stC1, stC2, stC3); + EffectMap *newMap = new EffectMap; + + EffectMapUnit unit; + unit.count = 0; + unit.inEffect = false; + + int16 w = rect.width(); + int16 h = rect.height(); + + bool first = true; + + uint8 minComp = MIN(MIN(stC1, stC2), stC3); + uint8 maxComp = MAX(MAX(stC1, stC2), stC3); + + uint8 depth8 = depth << 3; + + for (int16 j = 0; j < h; j++) { + uint16 *pix = (uint16 *)_curBkg.getBasePtr(rect.left, rect.top + j); + for (int16 i = 0; i < w; i++) { + uint16 curClr = pix[i]; + uint8 cC1, cC2, cC3; + _curBkg.format.colorToRGB(curClr, cC1, cC2, cC3); + + bool use = false; + + if (curClr == color) + use = true; + else if (curClr > color) { + if ((cC1 - stC1 < depth8) && + (cC2 - stC2 < depth8) && + (cC3 - stC3 < depth8)) + use = true; + } else { /* if (curClr < color) */ + if ((stC1 - cC1 < depth8) && + (stC2 - cC2 < depth8) && + (stC3 - cC3 < depth8)) + use = true; + } - const uint16 *source = (const uint16 *)surface->getPixels(); - uint16 *dest = (uint16 *)tranposedSurface->getPixels(); + if (first) { + unit.inEffect = use; + first = false; + } - for (uint32 y = 0; y < tranposedSurface->h; ++y) { - uint32 columnIndex = y * tranposedSurface->w; + if (use) { + uint8 cMinComp = MIN(MIN(cC1, cC2), cC3); + uint8 cMaxComp = MAX(MAX(cC1, cC2), cC3); + if (cMinComp < minComp) + minComp = cMinComp; + if (cMaxComp > maxComp) + maxComp = cMaxComp; + } - for (uint32 x = 0; x < tranposedSurface->w; ++x) { - dest[columnIndex + x] = source[x * surface->w + y]; + if (unit.inEffect == use) + unit.count++; + else { + newMap->push_back(unit); + unit.count = 1; + unit.inEffect = use; + } } } + newMap->push_back(unit); - return tranposedSurface; + if (_minComp) { + if (minComp - depth8 < 0) + *_minComp = -(minComp >> 3); + else + *_minComp = -depth; + } + if (_maxComp) { + if ((int16)maxComp + (int16)depth8 > 255) + *_maxComp = (255 - maxComp) >> 3; + else + *_maxComp = depth; + } + + return newMap; +} + +EffectMap *RenderManager::makeEffectMap(const Graphics::Surface &surf, uint16 transp) { + EffectMapUnit unit; + unit.count = 0; + unit.inEffect = false; + + int16 w = surf.w; + int16 h = surf.h; + + EffectMap *newMap = new EffectMap; + + bool first = true; + + for (int16 j = 0; j < h; j++) { + const uint16 *pix = (const uint16 *)surf.getBasePtr(0, j); + for (int16 i = 0; i < w; i++) { + bool use = false; + if (pix[i] != transp) + use = true; + + if (first) { + unit.inEffect = use; + first = false; + } + + if (unit.inEffect == use) + unit.count++; + else { + newMap->push_back(unit); + unit.count = 1; + unit.inEffect = use; + } + } + } + newMap->push_back(unit); + + return newMap; +} + +void RenderManager::markDirty() { + _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); +} + +void RenderManager::bkgFill(uint8 r, uint8 g, uint8 b) { + _curBkg.fillRect(Common::Rect(_curBkg.w, _curBkg.h), _curBkg.format.RGBToColor(r, g, b)); + markDirty(); } } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h index 9feff4c030c7..7723c3d0f322 100644 --- a/engines/zvision/graphics/render_manager.h +++ b/engines/zvision/graphics/render_manager.h @@ -8,12 +8,12 @@ * 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. @@ -31,6 +31,8 @@ #include "graphics/surface.h" +#include "effect.h" + class OSystem; @@ -47,43 +49,54 @@ namespace ZVision { class RenderManager { public: - RenderManager(OSystem *system, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); + RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); ~RenderManager(); private: - struct AlphaDataEntry { - Graphics::Surface *data; - uint16 alphaColor; - uint16 destX; - uint16 destY; - uint16 width; - uint16 height; + struct oneSub { + Common::Rect r; + Common::String txt; + int16 timer; + bool todelete; + bool redraw; }; - typedef Common::HashMap AlphaEntryMap; + typedef Common::HashMap subMap; + typedef Common::List effectsList; private: + ZVision *_engine; OSystem *_system; const Graphics::PixelFormat _pixelFormat; - // A buffer the exact same size as the workingWindow - // This buffer stores everything un-warped, then does a warp at the end of the frame - Graphics::Surface _workingWindowBuffer; - // A buffer representing the entire screen. Any graphical updates are first done with this buffer - // before actually being blitted to the screen - Graphics::Surface _backBuffer; - // A list of Alpha Entries that need to be blitted to the backbuffer - AlphaEntryMap _alphaDataEntries; + // A buffer for blitting background image to working window + Graphics::Surface _wrkWnd; + + Common::Rect _wrkWndDirtyRect; + + // A buffer for mutate image by tilt or panorama renderers + Graphics::Surface _outWnd; + + Common::Rect _bkgDirtyRect; + + // A buffer for subtitles + Graphics::Surface _subWnd; + + Common::Rect _subWndDirtyRect; + + // A buffer for menu drawing + Graphics::Surface _menuWnd; + + Common::Rect _menuWndDirtyRect; + + // A buffer used for apply graphics effects + Graphics::Surface _effectWnd; - // A rectangle representing the portion of the working window where the pixels have been changed since last frame - Common::Rect _workingWindowDirtyRect; - // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame - Common::Rect _backBufferDirtyRect; /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */ - const int _workingWidth; + const int _wrkWidth; /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */ - const int _workingHeight; + const int _wrkHeight; /** Center of the screen in the x direction */ const int _screenCenterX; /** Center of the screen in the y direction */ @@ -95,33 +108,36 @@ class RenderManager { * edges of this Rectangle */ const Common::Rect _workingWindow; + + // Recatangle for subtitles area + Common::Rect _subWndRect; + + // Recatangle for menu area + Common::Rect _menuWndRect; + /** Used to warp the background image */ RenderTable _renderTable; - Graphics::Surface _currentBackground; + // A buffer for background image + Graphics::Surface _curBkg; /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */ - Common::Point _backgroundOffset; + int16 _bkgOff; /** The width of the current background image */ - uint16 _backgroundWidth; + uint16 _bkgWidth; /** The height of the current background image */ - uint16 _backgroundHeight; + uint16 _bkgHeight; - /** - * The "velocity" at which the background image is panning. We actually store the inverse of velocity (ms/pixel instead of pixels/ms) - * because it allows you to accumulate whole pixels 'steps' instead of rounding pixels every frame - */ - int _backgroundInverseVelocity; - /** Holds any 'leftover' milliseconds between frames */ - uint _accumulatedVelocityMilliseconds; + // Internal subtitles counter + uint16 _subid; + + // Subtitle list + subMap _subsList; + + // Visual effects list + effectsList _effects; public: void initialize(); - /** - * Rotates the background image in accordance to the current _backgroundInverseVelocity - * - * @param deltaTimeInMillis The amount of time that has passed since the last frame - */ - void update(uint deltaTimeInMillis); /** * Renders the current state of the backbuffer to the screen @@ -129,89 +145,34 @@ class RenderManager { void renderBackbufferToScreen(); /** - * Renders all AlphaEntries to the backbuffer - */ - void processAlphaEntries(); - /** - * Clears the AlphaEntry list - */ - void clearAlphaEntries() { _alphaDataEntries.clear(); } - /** - * Removes a specific AlphaEntry from the list - * - * @param idNumber The id number identifing the AlphaEntry - */ - void removeAlphaEntry(uint32 idNumber) { _alphaDataEntries.erase(idNumber); } - - /** - * Copies a sub-rectangle of a buffer to the working window - * - * @param buffer The pixel data to copy to the working window - * @param destX The X destination in the working window where the subRect of data should be put - * @param destY The Y destination in the working window where the subRect of data should be put - * @param imageWidth The width of the source image - * @param width The width of the sub rectangle - * @param height The height of the sub rectangle - */ - void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height); - /** - * Copies a sub-rectangle of a buffer to the working window with binary alpha support. - * - * @param buffer The pixel data to copy to the working window - * @param destX The X destination in the working window where the subRect of data should be put - * @param destY The Y destination in the working window where the subRect of data should be put - * @param imageWidth The width of the source image - * @param width The width of the sub rectangle - * @param height The height of the sub rectangle - * @param alphaColor The color to interpret as meaning 'transparent' - * @param idNumber A unique identifier for the data being copied over. - */ - void copyRectToWorkingWindow(const uint16 *buffer, int32 destX, int32 destY, int32 imageWidth, int32 width, int32 height, int16 alphaColor, uint32 idNumber); - - /** - * Renders the supplied text to the working window + * Blits the image or a portion of the image to the background. * - * @param idNumber A unique identifier for the text - * @param text The text to be rendered - * @param font The font to use to render the text - * @param destX The X destination in the working window where the text should be rendered - * @param destY The Y destination in the working window where the text should be rendered - * @param textColor The color to render the text with (in RBG 565) - * @param maxWidth The max width the text should take up. - * @param maxHeight The max height the text should take up. - * @param align The alignment of the text within the bounds of maxWidth - * @param wrap If true, any words extending past maxWidth will wrap to a new line. If false, ellipses will be rendered to show that the text didn't fit - * @return A rectangle representing where the text was drawn in the working window + * @param fileName Name of the image file + * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! + * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! */ - Common::Rect renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight = -1, Graphics::TextAlign align = Graphics::kTextAlignLeft, bool wrap = true); + void renderImageToBackground(const Common::String &fileName, int16 destinationX, int16 destinationY); /** - * Fills the entire workingWindow with the specified color. Internally, the color - * will be converted to RGB 565 and then blitted. + * Blits the image or a portion of the image to the background. * - * @param color The color to fill the working window with. (In RGB 555) + * @param fileName Name of the image file + * @param destX X position where the image should be put. Coords are in working window space, not screen space! + * @param destY Y position where the image should be put. Coords are in working window space, not screen space! + * @param colorkey Transparent color */ - void clearWorkingWindowTo555Color(uint16 color); + void renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 colorkey); /** - * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. - * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! + * Blits the image or a portion of the image to the background. * - * @param fileName Name of the image file - * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! - * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! + * @param fileName Name of the image file + * @param destX X position where the image should be put. Coords are in working window space, not screen space! + * @param destY Y position where the image should be put. Coords are in working window space, not screen space! + * @param keyX X position of transparent color + * @param keyY Y position of transparent color */ - void renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap = false); - - /** - * Blits the image or a portion of the image to the backbuffer. Actual screen updates won't happen until the end of the frame. - * The image will be clipped to fit inside the working window. Coords are in working window space, not screen space! - * - * @param stream Surface to read the image data from - * @param destinationX X position where the image should be put. Coords are in working window space, not screen space! - * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space! - */ - void renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap = false); + void renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, int16 keyX, int16 keyY); /** * Sets the current background image to be used by the RenderManager and immediately @@ -233,14 +194,6 @@ class RenderManager { */ void setBackgroundPosition(int offset); - /** - * Set the background scroll velocity. Negative velocities correspond to left / up scrolling and - * positive velocities correspond to right / down scrolling - * - * @param velocity Velocity - */ - void setBackgroundVelocity(int velocity); - /** * Converts a point in screen coordinate space to image coordinate space * @@ -248,27 +201,12 @@ class RenderManager { * @return Point in image coordinate space */ const Common::Point screenSpaceToImageSpace(const Common::Point &point); - /** - * Converts a point in image coordinate space to ***PRE-WARP*** - * working window coordinate space - * - * @param point Point in image coordinate space - * @return Point in PRE-WARP working window coordinate space - */ - const Common::Point imageSpaceToWorkingWindowSpace(const Common::Point &point); - - /** - * Clip a rectangle to the working window. If it returns false, the original rect - * is not inside the working window. - * - * @param rect The rectangle to clip against the working window - * @return Is rect at least partially inside the working window (true) or completely outside (false) - */ - bool clipRectToWorkingWindow(Common::Rect &rect); + // Return pointer of RenderTable object RenderTable *getRenderTable(); + + // Return current background offset uint32 getCurrentBackgroundOffset(); - const Graphics::Surface *getBackBuffer() { return &_backBuffer; } /** * Creates a copy of surface and transposes the data. @@ -281,21 +219,65 @@ class RenderManager { */ static Graphics::Surface *tranposeSurface(const Graphics::Surface *surface); -private: - /** - * Renders a subRectangle of an image to the backbuffer. The destinationRect and SubRect - * will be clipped to image bound and to working window bounds - * - * @param buffer Pointer to (0, 0) of the image data - * @param imageWidth The width of the original image (not of the subRectangle) - * @param imageHeight The width of the original image (not of the subRectangle) - * @param horizontalPitch The horizontal pitch of the original image - * @param destinationX The x coordinate (in working window space) of where to put the final image - * @param destinationY The y coordinate (in working window space) of where to put the final image - * @param subRectangle A rectangle representing the part of the image that should be rendered - * @param wrap Should the image wrap (tile) if it doesn't completely fill the screen? - */ - void renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap); + // Scale buffer (nearest) + void scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight); + + // Blitting surface-to-surface methods + void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int x, int y); + void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey); + void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y); + void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey); + + // Blitting surface-to-background methods + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y); + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey); + + // Blitting surface-to-background methods with scale + void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect); + void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey); + + // Blitting surface-to-menu methods + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y); + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey); + + // Subtitles methods + + // Create subtitle area and return ID + uint16 createSubArea(const Common::Rect &area); + uint16 createSubArea(); + + // Delete subtitle by ID + void deleteSubArea(uint16 id); + void deleteSubArea(uint16 id, int16 delay); + + // Update subtitle area + void updateSubArea(uint16 id, const Common::String &txt); + + // Processing subtitles + void processSubs(uint16 deltatime); + + + // Return background size + Common::Point getBkgSize(); + + // Return portion of background as new surface + Graphics::Surface *getBkgRect(Common::Rect &rect); + + // Load image into new surface + Graphics::Surface *loadImage(const char *file); + Graphics::Surface *loadImage(Common::String &file); + Graphics::Surface *loadImage(const char *file, bool transposed); + Graphics::Surface *loadImage(Common::String &file, bool transposed); + + // Clear whole/area of menu surface + void clearMenuSurface(); + void clearMenuSurface(const Common::Rect &r); + + // Copy menu buffer to screen + void renderMenuToScreen(); + + // Copy needed portion of background surface to workingWindow surface + void prepareBkg(); /** * Reads an image file pixel data into a Surface buffer. In the process @@ -310,17 +292,43 @@ class RenderManager { void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination); /** - * Move the background image by an offset. If we are currently in Panorama mode, - * the offset will correspond to a horizontal motion. If we are currently in Tilt mode, - * the offset will correspond to a vertical motion. This function should not be called - * if we are in Flat mode. - * - * The RenderManager will take care of wrapping the image. - * Ex: If the image has width 1400px, it is legal to offset 1500px. + * Reads an image file pixel data into a Surface buffer. In the process + * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * is transposed, it will un-transpose the pixel data. The function will + * call destination::create() if the dimensions of destination do not match + * up with the dimensions of the image. * - * @param offset The amount to move the background + * @param fileName The name of a .tga file + * @param destination A reference to the Surface to store the pixel data in + * @param transposed Transpose flag */ - void moveBackground(int offset); + void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed); + + // Add visual effect to effects list + void addEffect(Effect *_effect); + + // Delete effect(s) by ID (ID equal to slot of action:region that create this effect) + void deleteEffect(uint32 ID); + + // Create "mask" for effects - (color +/- depth) will be selected as not transparent. Like color selection + // xy - base color + // depth - +/- of base color + // rect - rectangle where select pixels + // minD - if not NULL will recieve real bottom border of depth + // maxD - if not NULL will recieve real top border of depth + EffectMap *makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *minD, int8 *maxD); + + // Create "mask" for effects by simple transparent color + EffectMap *makeEffectMap(const Graphics::Surface &surf, uint16 transp); + + // Return background rectangle in screen coordinates + Common::Rect bkgRectToScreen(const Common::Rect &src); + + // Mark whole background surface as dirty + void markDirty(); + + // Fille background surface by color + void bkgFill(uint8 r, uint8 g, uint8 b); }; } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp index 49b934dc37b6..629cbde3cf14 100644 --- a/engines/zvision/graphics/render_table.cpp +++ b/engines/zvision/graphics/render_table.cpp @@ -32,9 +32,9 @@ namespace ZVision { RenderTable::RenderTable(uint numColumns, uint numRows) - : _numRows(numRows), - _numColumns(numColumns), - _renderState(FLAT) { + : _numRows(numRows), + _numColumns(numColumns), + _renderState(FLAT) { assert(numRows != 0 && numColumns != 0); _internalBuffer = new Common::Point[numRows * numColumns]; @@ -52,10 +52,11 @@ void RenderTable::setRenderState(RenderState newState) { _panoramaOptions.fieldOfView = 27.0f; _panoramaOptions.linearScale = 0.55f; _panoramaOptions.reverse = false; + _panoramaOptions.zeroPoint = 0; break; case TILT: _tiltOptions.fieldOfView = 27.0f; - _tiltOptions.linearScale = 0.55f; + _tiltOptions.linearScale = 0.65f; _tiltOptions.reverse = false; break; case FLAT: @@ -97,12 +98,12 @@ uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) { uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) | (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) | - (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); + (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); return returnColor; } -void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect) { +void RenderTable::mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect) { uint32 destOffset = 0; for (int16 y = subRect.top; y < subRect.bottom; ++y) { @@ -123,6 +124,28 @@ void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 d } } +void RenderTable::mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf) { + uint32 destOffset = 0; + + uint16 *sourceBuffer = (uint16 *)srcBuf->getPixels(); + uint16 *destBuffer = (uint16 *)dstBuf->getPixels(); + + for (int16 y = 0; y < srcBuf->h; ++y) { + uint32 sourceOffset = y * _numColumns; + + for (int16 x = 0; x < srcBuf->w; ++x) { + uint32 index = sourceOffset + x; + + // RenderTable only stores offsets from the original coordinates + uint32 sourceYIndex = y + _internalBuffer[index].y; + uint32 sourceXIndex = x + _internalBuffer[index].x; + + destBuffer[destOffset] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex]; + destOffset++; + } + } +} + void RenderTable::generateRenderTable() { switch (_renderState) { case ZVision::RenderTable::PANORAMA: @@ -177,6 +200,7 @@ void RenderTable::generateTiltLookupTable() { float fovInRadians = (_tiltOptions.fieldOfView * M_PI / 180.0f); float cylinderRadius = halfWidth / tan(fovInRadians); + _tiltOptions.gap = cylinderRadius * atan2(halfHeight / cylinderRadius, 1.0) * _tiltOptions.linearScale; for (uint y = 0; y < _numRows; ++y) { @@ -221,6 +245,18 @@ void RenderTable::setPanoramaReverse(bool reverse) { _panoramaOptions.reverse = reverse; } +bool RenderTable::getPanoramaReverse() { + return _panoramaOptions.reverse; +} + +void RenderTable::setPanoramaZeroPoint(uint16 point) { + _panoramaOptions.zeroPoint = point; +} + +uint16 RenderTable::getPanoramaZeroPoint() { + return _panoramaOptions.zeroPoint; +} + void RenderTable::setTiltFoV(float fov) { assert(fov > 0.0f); @@ -237,4 +273,26 @@ void RenderTable::setTiltReverse(bool reverse) { _tiltOptions.reverse = reverse; } +float RenderTable::getTiltGap() { + return _tiltOptions.gap; +} + +float RenderTable::getAngle() { + if (_renderState == TILT) + return _tiltOptions.fieldOfView; + else if (_renderState == PANORAMA) + return _panoramaOptions.fieldOfView; + else + return 1.0; +} + +float RenderTable::getLinscale() { + if (_renderState == TILT) + return _tiltOptions.linearScale; + else if (_renderState == PANORAMA) + return _panoramaOptions.linearScale; + else + return 1.0; +} + } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.h b/engines/zvision/graphics/render_table.h index f066187ad143..f061b3b0d628 100644 --- a/engines/zvision/graphics/render_table.h +++ b/engines/zvision/graphics/render_table.h @@ -24,6 +24,7 @@ #define ZVISION_RENDER_TABLE_H #include "common/rect.h" +#include "graphics/surface.h" namespace ZVision { @@ -49,6 +50,7 @@ class RenderTable { float fieldOfView; float linearScale; bool reverse; + uint16 zeroPoint; } _panoramaOptions; // TODO: See if tilt and panorama need to have separate options @@ -56,25 +58,36 @@ class RenderTable { float fieldOfView; float linearScale; bool reverse; + float gap; } _tiltOptions; public: - RenderState getRenderState() { return _renderState; } + RenderState getRenderState() { + return _renderState; + } void setRenderState(RenderState newState); const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point); - void mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect); + void mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect); + void mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf); void generateRenderTable(); void setPanoramaFoV(float fov); void setPanoramaScale(float scale); void setPanoramaReverse(bool reverse); + void setPanoramaZeroPoint(uint16 point); + uint16 getPanoramaZeroPoint(); + bool getPanoramaReverse(); void setTiltFoV(float fov); void setTiltScale(float scale); void setTiltReverse(bool reverse); + float getTiltGap(); + float getAngle(); + float getLinscale(); + private: void generatePanoramaLookupTable(); void generateTiltLookupTable(); diff --git a/engines/zvision/module.mk b/engines/zvision/module.mk index 2e298f24c6ce..f7927650d9f4 100644 --- a/engines/zvision/module.mk +++ b/engines/zvision/module.mk @@ -1,32 +1,56 @@ MODULE := engines/zvision MODULE_OBJS := \ + animation/meta_animation.o \ animation/rlf_animation.o \ archives/zfs_archive.o \ core/console.o \ core/events.o \ + core/menu.o \ + core/midi.o \ core/save_manager.o \ - cursors/cursor.o \ + core/search_manager.o \ cursors/cursor_manager.o \ + cursors/cursor.o \ detection.o \ fonts/truetype_font.o \ + graphics/effects/fog.o \ + graphics/effects/light.o \ + graphics/effects/wave.o \ graphics/render_manager.o \ graphics/render_table.o \ scripting/actions.o \ scripting/control.o \ - scripting/controls/animation_control.o \ + scripting/controls/fist_control.o \ + scripting/controls/hotmov_control.o \ scripting/controls/input_control.o \ scripting/controls/lever_control.o \ + scripting/controls/paint_control.o \ scripting/controls/push_toggle_control.o \ - scripting/controls/timer_node.o \ + scripting/controls/safe_control.o \ + scripting/controls/save_control.o \ + scripting/controls/slot_control.o \ + scripting/controls/titler_control.o \ + scripting/inventory.o \ scripting/scr_file_handling.o \ scripting/script_manager.o \ + scripting/sidefx.o \ + scripting/sidefx/animation_node.o \ + scripting/sidefx/distort_node.o \ + scripting/sidefx/music_node.o \ + scripting/sidefx/region_node.o \ + scripting/sidefx/syncsound_node.o \ + scripting/sidefx/timer_node.o \ + scripting/sidefx/ttytext_node.o \ sound/zork_raw.o \ - strings/string_manager.o \ + subtitles/subtitles.o \ + text/string_manager.o \ + text/text.o \ utility/clock.o \ utility/lzss_read_stream.o \ utility/single_value_container.o \ utility/utility.o \ + utility/win_keys.o \ video/video.o \ video/zork_avi_decoder.o \ zvision.o diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 219f418b13e1..8ff6bd0496e0 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -8,12 +8,12 @@ * 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. @@ -29,8 +29,21 @@ #include "zvision/graphics/render_manager.h" #include "zvision/sound/zork_raw.h" #include "zvision/video/zork_avi_decoder.h" -#include "zvision/scripting/controls/timer_node.h" -#include "zvision/scripting/controls/animation_control.h" +#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/sidefx/music_node.h" +#include "zvision/scripting/sidefx/syncsound_node.h" +#include "zvision/scripting/sidefx/animation_node.h" +#include "zvision/scripting/sidefx/distort_node.h" +#include "zvision/scripting/sidefx/ttytext_node.h" +#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/scripting/controls/titler_control.h" +#include "zvision/graphics/render_table.h" +#include "zvision/graphics/effect.h" +#include "zvision/graphics/effects/fog.h" +#include "zvision/graphics/effects/light.h" +#include "zvision/graphics/effects/wave.h" +#include "zvision/core/save_manager.h" +#include "zvision/cursors/cursor_manager.h" #include "common/file.h" @@ -43,12 +56,13 @@ namespace ZVision { // ActionAdd ////////////////////////////////////////////////////////////////////////////// -ActionAdd::ActionAdd(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u,%u)", &_key, &_value); +ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%u,%d", &_key, &_value); } -bool ActionAdd::execute(ZVision *engine) { - engine->getScriptManager()->addToStateValue(_key, _value); +bool ActionAdd::execute() { + _engine->getScriptManager()->setStateValue(_key, _engine->getScriptManager()->getStateValue(_key) + _value); return true; } @@ -57,12 +71,21 @@ bool ActionAdd::execute(ZVision *engine) { // ActionAssign ////////////////////////////////////////////////////////////////////////////// -ActionAssign::ActionAssign(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %u)", &_key, &_value); +ActionAssign::ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char buf[64]; + memset(buf, 0, 64); + sscanf(line.c_str(), "%u, %s", &_key, buf); + _value = new ValueSlot(_engine->getScriptManager(), buf); } -bool ActionAssign::execute(ZVision *engine) { - engine->getScriptManager()->setStateValue(_key, _value); +ActionAssign::~ActionAssign() { + if (_value) + delete _value; +} + +bool ActionAssign::execute() { + _engine->getScriptManager()->setStateValue(_key, _value->getValue()); return true; } @@ -71,12 +94,17 @@ bool ActionAssign::execute(ZVision *engine) { // ActionAttenuate ////////////////////////////////////////////////////////////////////////////// -ActionAttenuate::ActionAttenuate(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %d)", &_key, &_attenuation); +ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%u, %d", &_key, &_attenuation); } -bool ActionAttenuate::execute(ZVision *engine) { - // TODO: Implement +bool ActionAttenuate::execute() { + SideFX *fx = _engine->getScriptManager()->getSideFX(_key); + if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + MusicNode *mus = (MusicNode *)fx; + mus->setVolume(255 - (abs(_attenuation) >> 7)); + } return true; } @@ -85,13 +113,14 @@ bool ActionAttenuate::execute(ZVision *engine) { // ActionChangeLocation ////////////////////////////////////////////////////////////////////////////// -ActionChangeLocation::ActionChangeLocation(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%c,%c,%c%c,%u)", &_world, &_room, &_node, &_view, &_offset); +ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%c, %c, %c%c, %u", &_world, &_room, &_node, &_view, &_offset); } -bool ActionChangeLocation::execute(ZVision *engine) { +bool ActionChangeLocation::execute() { // We can't directly call ScriptManager::ChangeLocationIntern() because doing so clears all the Puzzles, and thus would corrupt the current puzzle checking - engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); + _engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); // Tell the puzzle system to stop checking any more puzzles return false; } @@ -101,133 +130,455 @@ bool ActionChangeLocation::execute(ZVision *engine) { // ActionCrossfade ////////////////////////////////////////////////////////////////////////////// -ActionCrossfade::ActionCrossfade(const Common::String &line) { +ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { sscanf(line.c_str(), - "%*[^(](%u %u %u %u %u %u %u)", - &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); + "%u %u %d %d %d %d %d", + &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); +} + +bool ActionCrossfade::execute() { + if (_keyOne) { + SideFX *fx = _engine->getScriptManager()->getSideFX(_keyOne); + if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + MusicNode *mus = (MusicNode *)fx; + if (_oneStartVolume >= 0) + mus->setVolume((_oneStartVolume * 255) / 100); + + mus->setFade(_timeInMillis, (_oneEndVolume * 255) / 100); + } + } + + if (_keyTwo) { + SideFX *fx = _engine->getScriptManager()->getSideFX(_keyTwo); + if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + MusicNode *mus = (MusicNode *)fx; + if (_twoStartVolume >= 0) + mus->setVolume((_twoStartVolume * 255) / 100); + + mus->setFade(_timeInMillis, (_twoEndVolume * 255) / 100); + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionCursor +////////////////////////////////////////////////////////////////////////////// + +ActionCursor::ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + Common::String up = line; + up.toUppercase(); + _action = 0; + + if (up[0] == 'B') + _action = 2; + else if (up[0] == 'I') + _action = 3; + else if (up[0] == 'U') + _action = 0; + else if (up[0] == 'H') + _action = 1; } -bool ActionCrossfade::execute(ZVision *engine) { - // TODO: Implement +bool ActionCursor::execute() { + switch (_action) { + case 1: + _engine->getCursorManager()->showMouse(false); + break; + default: + _engine->getCursorManager()->showMouse(true); + break; + } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionDelayRender +////////////////////////////////////////////////////////////////////////////// + +ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%u", &_framesToDelay); +} + +bool ActionDelayRender::execute() { + _engine->setRenderDelay(_framesToDelay); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionDisableControl ////////////////////////////////////////////////////////////////////////////// -ActionDisableControl::ActionDisableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); +ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%u", &_key); } -bool ActionDisableControl::execute(ZVision *engine) { - debug("Disabling control %u", _key); +bool ActionDisableControl::execute() { + _engine->getScriptManager()->setStateFlag(_key, Puzzle::DISABLED); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionDisableVenus +////////////////////////////////////////////////////////////////////////////// + +ActionDisableVenus::ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%d", &_key); +} + +bool ActionDisableVenus::execute() { + _engine->getScriptManager()->setStateValue(_key, 0); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionDisplayMessage +////////////////////////////////////////////////////////////////////////////// - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); +ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%hd %hd", &_control, &_msgid); +} +bool ActionDisplayMessage::execute() { + Control *ctrl = _engine->getScriptManager()->getControl(_control); + if (ctrl && ctrl->getType() == Control::CONTROL_TITLER) { + TitlerControl *titler = (TitlerControl *)ctrl; + titler->setString(_msgid); + } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionDissolve +////////////////////////////////////////////////////////////////////////////// + +ActionDissolve::ActionDissolve(ZVision *engine) : + ResultAction(engine, 0) { +} + +bool ActionDissolve::execute() { + // Cause black screen flick + // _engine->getRenderManager()->bkgFill(0, 0, 0); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionDistort +////////////////////////////////////////////////////////////////////////////// + +ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%hd %hd %f %f %f %f", &_distSlot, &_speed, &_startAngle, &_endAngle, &_startLineScale, &_endLineScale); +} + +ActionDistort::~ActionDistort() { + _engine->getScriptManager()->killSideFx(_distSlot); +} + +bool ActionDistort::execute() { + if (_engine->getScriptManager()->getSideFX(_distSlot)) + return true; + + _engine->getScriptManager()->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionEnableControl ////////////////////////////////////////////////////////////////////////////// -ActionEnableControl::ActionEnableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); +ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%u", &_key); +} + +bool ActionEnableControl::execute() { + _engine->getScriptManager()->unsetStateFlag(_key, Puzzle::DISABLED); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionFlushMouseEvents +////////////////////////////////////////////////////////////////////////////// + +ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotkey) : + ResultAction(engine, slotkey) { } -bool ActionEnableControl::execute(ZVision *engine) { - debug("Enabling control %u", _key); +bool ActionFlushMouseEvents::execute() { + _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONUP); + _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONDOWN); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionInventory +////////////////////////////////////////////////////////////////////////////// + +ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char buf[25]; + sscanf(line.c_str(), "%25s %d", buf, &_key); + + if (strcmp(buf, "add") == 0) { + _type = 0; + } else if (strcmp(buf, "addi") == 0) { + _type = 1; + } else if (strcmp(buf, "drop") == 0) { + _type = 2; + } else if (strcmp(buf, "dropi") == 0) { + _type = 3; + } else if (strcmp(buf, "cycle") == 0) { + _type = 4; + } - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~ScriptManager::DISABLED); +} +bool ActionInventory::execute() { + switch (_type) { + case 0: // add + _engine->getScriptManager()->inventoryAdd(_key); + break; + case 1: // addi + _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(_key)); + break; + case 2: // drop + if (_key >= 0) + _engine->getScriptManager()->inventoryDrop(_key); + else + _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(StateKey_InventoryItem)); + break; + case 3: // dropi + _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(_key)); + break; + case 4: // cycle + _engine->getScriptManager()->inventoryCycle(); + break; + default: + break; + } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionKill +////////////////////////////////////////////////////////////////////////////// + +ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + _type = 0; + char keytype[25]; + sscanf(line.c_str(), "%25s", keytype); + if (keytype[0] == '"') { + if (!scumm_stricmp(keytype, "\"ANIM\"")) + _type = SideFX::SIDEFX_ANIM; + else if (!scumm_stricmp(keytype, "\"AUDIO\"")) + _type = SideFX::SIDEFX_AUDIO; + else if (!scumm_stricmp(keytype, "\"DISTORT\"")) + _type = SideFX::SIDEFX_DISTORT; + else if (!scumm_stricmp(keytype, "\"PANTRACK\"")) + _type = SideFX::SIDEFX_PANTRACK; + else if (!scumm_stricmp(keytype, "\"REGION\"")) + _type = SideFX::SIDEFX_REGION; + else if (!scumm_stricmp(keytype, "\"TIMER\"")) + _type = SideFX::SIDEFX_TIMER; + else if (!scumm_stricmp(keytype, "\"TTYTEXT\"")) + _type = SideFX::SIDEFX_TTYTXT; + else if (!scumm_stricmp(keytype, "\"ALL\"")) + _type = SideFX::SIDEFX_ALL; + } else + _key = atoi(keytype); +} + +bool ActionKill::execute() { + if (_type) + _engine->getScriptManager()->killSideFxType((SideFX::SideFXType)_type); + else + _engine->getScriptManager()->killSideFx(_key); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionMenuBarEnable +////////////////////////////////////////////////////////////////////////////// + +ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%hu", &_menus); +} + +bool ActionMenuBarEnable::execute() { + _engine->menuBarEnable(_menus); + return true; +} + ////////////////////////////////////////////////////////////////////////////// // ActionMusic ////////////////////////////////////////////////////////////////////////////// -ActionMusic::ActionMusic(const Common::String &line) : _volume(255) { +ActionMusic::ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global) : + ResultAction(engine, slotkey), + _volume(255), + _universe(global) { uint type; - char fileNameBuffer[26]; + char fileNameBuffer[25]; uint loop; uint volume = 255; - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u %25s %u %u)", &_key, &type, fileNameBuffer, &loop, &volume); + sscanf(line.c_str(), "%u %25s %u %u", &type, fileNameBuffer, &loop, &volume); // type 4 are midi sound effect files if (type == 4) { - _soundType = Audio::Mixer::kSFXSoundType; - _fileName = Common::String::format("midi/%s/%u.wav", fileNameBuffer, loop); - _loop = false; + _midi = true; + int note; + int prog; + sscanf(line.c_str(), "%u %d %d %u", &type, &prog, ¬e, &volume); + _volume = volume; + _note = note; + _prog = prog; } else { - // TODO: See what the other types are so we can specify the correct Mixer::SoundType. In the meantime use kPlainSoundType - _soundType = Audio::Mixer::kPlainSoundType; + _midi = false; _fileName = Common::String(fileNameBuffer); _loop = loop == 1 ? true : false; - } - // Volume is optional. If it doesn't appear, assume full volume - if (volume != 255) { - // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] - _volume = volume * 255 / 100; + // Volume is optional. If it doesn't appear, assume full volume + if (volume != 255) { + // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] + _volume = volume * 255 / 100; + } } + + } -bool ActionMusic::execute(ZVision *engine) { - Audio::RewindableAudioStream *audioStream; +ActionMusic::~ActionMusic() { + if (!_universe) + _engine->getScriptManager()->killSideFx(_slotKey); +} - if (_fileName.contains(".wav")) { - Common::File *file = new Common::File(); - if (file->open(_fileName)) { - audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); - } else { - warning("Unable to open %s", _fileName.c_str()); - return false; - } - } else { - audioStream = makeRawZorkStream(_fileName, engine); - } +bool ActionMusic::execute() { + if (_engine->getScriptManager()->getSideFX(_slotKey)) + return true; - if (_loop) { - Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); + if (_midi) { + _engine->getScriptManager()->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, _volume)); } else { - engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume); + if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; + + _engine->getScriptManager()->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, _volume)); } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionPanTrack +////////////////////////////////////////////////////////////////////////////// + +ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey), + _pos(0), + _musicSlot(0) { + + sscanf(line.c_str(), "%u %d", &_musicSlot, &_pos); +} + +ActionPanTrack::~ActionPanTrack() { + _engine->getScriptManager()->killSideFx(_slotKey); +} + +bool ActionPanTrack::execute() { + if (_engine->getScriptManager()->getSideFX(_slotKey)) + return true; + + _engine->getScriptManager()->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionPreferences +////////////////////////////////////////////////////////////////////////////// + +ActionPreferences::ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + if (line.compareToIgnoreCase("save") == 0) + _save = true; + else + _save = false; +} + +bool ActionPreferences::execute() { + if (_save) + _engine->saveSettings(); + else + _engine->loadSettings(); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) { - char fileName[26]; +ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%25s %*u %*u %u %u)", &_key, fileName, &_mask, &_framerate); + sscanf(line.c_str(), "%25s %*u %*u %d %d", fileName, &_mask, &_framerate); + + if (_mask > 0) { + byte r, g, b; + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); + _mask = _engine->_pixelFormat.RGBToColor(r, g, b); + } _fileName = Common::String(fileName); } -bool ActionPreloadAnimation::execute(ZVision *engine) { - // TODO: We ignore the mask and framerate atm. Mask refers to a key color used for binary alpha. We assume the framerate is the default framerate embedded in the videos +ActionPreloadAnimation::~ActionPreloadAnimation() { + _engine->getScriptManager()->deleteSideFx(_slotKey); +} + +bool ActionPreloadAnimation::execute() { + AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + + if (!nod) { + nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate, false); + _engine->getScriptManager()->addSideFX(nod); + } else + nod->stop(); + _engine->getScriptManager()->setStateValue(_slotKey, 2); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionUnloadAnimation +////////////////////////////////////////////////////////////////////////////// + +ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + + sscanf(line.c_str(), "%u", &_key); +} - // TODO: Check if the Control already exists +bool ActionUnloadAnimation::execute() { + AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_key); - // Create the control, but disable it until PlayPreload is called - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->addControl(new AnimationControl(engine, _key, _fileName)); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); + if (nod && nod->getType() == SideFX::SIDEFX_ANIM) + _engine->getScriptManager()->deleteSideFx(_key); return true; } @@ -237,19 +588,40 @@ bool ActionPreloadAnimation::execute(ZVision *engine) { // ActionPlayAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) { - char fileName[26]; +ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; // The two %*u are always 0 and dont seem to have a use sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%25s %u %u %u %u %u %u %u %*u %*u %u %u)", - &_key, fileName, &_x, &_y, &_width, &_height, &_start, &_end, &_loopCount, &_mask, &_framerate); + "%25s %u %u %u %u %u %u %d %*u %*u %d %d", + fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate); + + if (_mask > 0) { + byte r, g, b; + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); + _mask = _engine->_pixelFormat.RGBToColor(r, g, b); + } _fileName = Common::String(fileName); } -bool ActionPlayAnimation::execute(ZVision *engine) { - // TODO: Implement +ActionPlayAnimation::~ActionPlayAnimation() { + _engine->getScriptManager()->deleteSideFx(_slotKey); +} + +bool ActionPlayAnimation::execute() { + AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + + if (!nod) { + nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate); + _engine->getScriptManager()->addSideFX(nod); + } else + nod->stop(); + + if (nod) + nod->addPlayNode(_slotKey, _x, _y, _x2, _y2, _start, _end, _loopCount); + return true; } @@ -258,24 +630,18 @@ bool ActionPlayAnimation::execute(ZVision *engine) { // ActionPlayPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(const Common::String &line) { +ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%u %u %u %u %u %u %u %u)", - &_animationKey, &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); + "%u %u %u %u %u %u %u %u", + &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); } -bool ActionPlayPreloadAnimation::execute(ZVision *engine) { - // Find the control - AnimationControl *control = (AnimationControl *)engine->getScriptManager()->getControl(_controlKey); - - // Set the needed values within the control - control->setAnimationKey(_animationKey); - control->setLoopCount(_loopCount); - control->setXPos(_x1); - control->setYPost(_y1); +bool ActionPlayPreloadAnimation::execute() { + AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_controlKey); - // Enable the control. ScriptManager will take care of the rest - control->enable(); + if (nod) + nod->addPlayNode(_slotKey, _x1, _y1, _x2, _y2, _startFrame, _endFrame, _loopCount); return true; } @@ -285,24 +651,138 @@ bool ActionPlayPreloadAnimation::execute(ZVision *engine) { // ActionQuit ////////////////////////////////////////////////////////////////////////////// -bool ActionQuit::execute(ZVision *engine) { - engine->quitGame(); +bool ActionQuit::execute() { + _engine->quitGame(); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionRegion +////////////////////////////////////////////////////////////////////////////// + +ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + + char art[64]; + char custom[64]; + + int32 x1, x2, y1, y2; + + sscanf(line.c_str(), "%s %d %d %d %d %hu %hu %hu %hu %s", art, &x1, &y1, &x2, &y2, &_delay, &_type, &_unk1, &_unk2, custom); + _art = Common::String(art); + _custom = Common::String(custom); + _rect = Common::Rect(x1, y1, x2 + 1, y2 + 1); +} + +ActionRegion::~ActionRegion() { + _engine->getScriptManager()->killSideFx(_slotKey); +} + +bool ActionRegion::execute() { + if (_engine->getScriptManager()->getSideFX(_slotKey)) + return true; + + Effect *effct = NULL; + switch (_type) { + case 0: { + uint16 centerX, centerY, frames; + double amplitude, waveln, speed; + sscanf(_custom.c_str(), "%hu,%hu,%hu,%lf,%lf,%lf,", ¢erX, ¢erY, &frames, &litude, &waveln, &speed); + effct = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); + } + break; + case 1: { + uint16 aX, aY, aD; + if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) + sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aY, &aX, &aD); + else + sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aX, &aY, &aD); + int8 minD; + int8 maxD; + EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD); + effct = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); + } + break; + case 9: { + int16 dum1; + int32 dum2; + char buf[64]; + sscanf(_custom.c_str(), "%hd,%d,%s", &dum1, &dum2, buf); + Graphics::Surface tempMask; + _engine->getRenderManager()->readImageToSurface(_art, tempMask); + if (_rect.width() != tempMask.w) + _rect.setWidth(tempMask.w); + if (_rect.height() != tempMask.h) + _rect.setHeight(tempMask.h); + + EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0); + effct = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); + } + break; + default: + break; + } + + if (effct) { + _engine->getScriptManager()->addSideFX(new RegionNode(_engine, _slotKey, effct, _delay)); + _engine->getRenderManager()->addEffect(effct); + } + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionRandom ////////////////////////////////////////////////////////////////////////////// -ActionRandom::ActionRandom(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u, %u)", &_key, &_max); +ActionRandom::ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char maxBuffer[64]; + memset(maxBuffer, 0, 64); + sscanf(line.c_str(), "%s", maxBuffer); + _max = new ValueSlot(_engine->getScriptManager(), maxBuffer); +} + +ActionRandom::~ActionRandom() { + if (_max) + delete _max; +} + +bool ActionRandom::execute() { + uint randNumber = _engine->getRandomSource()->getRandomNumber(_max->getValue()); + _engine->getScriptManager()->setStateValue(_slotKey, randNumber); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionRestoreGame +////////////////////////////////////////////////////////////////////////////// + +ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char buf[128]; + sscanf(line.c_str(), "%s", buf); + _fileName = Common::String(buf); +} + +bool ActionRestoreGame::execute() { + _engine->getSaveManager()->loadGame(_fileName); + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionRotateTo +////////////////////////////////////////////////////////////////////////////// + +ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%d, %d", &_toPos, &_time); } -bool ActionRandom::execute(ZVision *engine) { - uint randNumber = engine->getRandomSource()->getRandomNumber(_max); - engine->getScriptManager()->setStateValue(_key, randNumber); +bool ActionRotateTo::execute() { + _engine->rotateTo(_toPos, _time); + return true; } @@ -311,27 +791,44 @@ bool ActionRandom::execute(ZVision *engine) { // ActionSetPartialScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { - char fileName[26]; - uint color; +ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; + int color; - sscanf(line.c_str(), "%*[^(](%u %u %25s %*u %u)", &_x, &_y, fileName, &color); + sscanf(line.c_str(), "%u %u %25s %*u %d", &_x, &_y, fileName, &color); _fileName = Common::String(fileName); - if (color > 0xFFFF) { + if (color >= 0) { + byte r, g, b; + Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); + _backgroundColor = _engine->_pixelFormat.RGBToColor(r, g, b); + } else { + _backgroundColor = color; + } + + if (color > 65535) { warning("Background color for ActionSetPartialScreen is bigger than a uint16"); } - _backgroundColor = color; } -bool ActionSetPartialScreen::execute(ZVision *engine) { - RenderManager *renderManager = engine->getRenderManager(); +bool ActionSetPartialScreen::execute() { + RenderManager *renderManager = _engine->getRenderManager(); - if (_backgroundColor > 0) { - renderManager->clearWorkingWindowTo555Color(_backgroundColor); + if (_engine->getGameId() == GID_NEMESIS) { + if (_backgroundColor) + renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0); + else + renderManager->renderImageToBackground(_fileName, _x, _y); + } else { + if (_backgroundColor >= 0) + renderManager->renderImageToBackground(_fileName, _x, _y, _backgroundColor); + else if (_backgroundColor == -2) + renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0); + else + renderManager->renderImageToBackground(_fileName, _x, _y); } - renderManager->renderImageToScreen(_fileName, _x, _y); return true; } @@ -341,16 +838,49 @@ bool ActionSetPartialScreen::execute(ZVision *engine) { // ActionSetScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetScreen::ActionSetScreen(const Common::String &line) { - char fileName[26]; - sscanf(line.c_str(), "%*[^(](%25[^)])", fileName); +ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; + sscanf(line.c_str(), "%25s", fileName); _fileName = Common::String(fileName); } -bool ActionSetScreen::execute(ZVision *engine) { - engine->getRenderManager()->setBackgroundImage(_fileName); +bool ActionSetScreen::execute() { + _engine->getRenderManager()->setBackgroundImage(_fileName); + + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// ActionSetVenus +////////////////////////////////////////////////////////////////////////////// + +ActionSetVenus::ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + sscanf(line.c_str(), "%d", &_key); +} + +bool ActionSetVenus::execute() { + if (_engine->getScriptManager()->getStateValue(_key)) + _engine->getScriptManager()->setStateValue(StateKey_Venus, _key); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionStop +////////////////////////////////////////////////////////////////////////////// + +ActionStop::ActionStop(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + sscanf(line.c_str(), "%u", &_key); +} +bool ActionStop::execute() { + _engine->getScriptManager()->stopSideFx(_key); return true; } @@ -359,42 +889,127 @@ bool ActionSetScreen::execute(ZVision *engine) { // ActionStreamVideo ////////////////////////////////////////////////////////////////////////////// -ActionStreamVideo::ActionStreamVideo(const Common::String &line) { - char fileName[26]; - uint skippable; +ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; + uint skipline; //skipline - render video with skip every second line, not skippable. - sscanf(line.c_str(), "%*[^(](%25s %u %u %u %u %u %u)", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skippable); + sscanf(line.c_str(), "%25s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); _fileName = Common::String(fileName); - _skippable = (skippable == 0) ? false : true; + _skippable = true; } -bool ActionStreamVideo::execute(ZVision *engine) { +bool ActionStreamVideo::execute() { ZorkAVIDecoder decoder; - if (!decoder.loadFile(_fileName)) { - return true; - } + Common::File *_file = _engine->getSearchManager()->openFile(_fileName); + + if (_file) { + if (!decoder.loadStream(_file)) { + return true; + } + + _engine->getCursorManager()->showMouse(false); + + Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + + Common::String subname = _fileName; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + + Subtitle *sub = NULL; + + if (_engine->getSearchManager()->hasFile(subname)) + sub = new Subtitle(_engine, subname); - Common::Rect destRect; - if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) { - destRect = Common::Rect(_x1, _y1, _x2, _y2); + _engine->playVideo(decoder, destRect, _skippable, sub); + + _engine->getCursorManager()->showMouse(true); + + if (sub) + delete sub; } - engine->playVideo(decoder, destRect, _skippable); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionSyncSound +////////////////////////////////////////////////////////////////////////////// + +ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; + int notUsed; + + sscanf(line.c_str(), "%d %d %25s", &_syncto, ¬Used, fileName); + + _fileName = Common::String(fileName); +} + +bool ActionSyncSound::execute() { + SideFX *fx = _engine->getScriptManager()->getSideFX(_syncto); + if (!fx) + return true; + + if (!(fx->getType() & SideFX::SIDEFX_ANIM)) + return true; + + AnimationNode *animnode = (AnimationNode *)fx; + if (animnode->getFrameDelay() > 200) // Hack for fix incorrect framedelay in some animpreload + animnode->setNewFrameDelay(66); // ~15fps + + _engine->getScriptManager()->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionTimer ////////////////////////////////////////////////////////////////////////////// -ActionTimer::ActionTimer(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u)", &_key, &_time); +ActionTimer::ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char timeBuffer[64]; + memset(timeBuffer, 0, 64); + sscanf(line.c_str(), "%s", timeBuffer); + _time = new ValueSlot(_engine->getScriptManager(), timeBuffer); +} + +ActionTimer::~ActionTimer() { + if (_time) + delete _time; + _engine->getScriptManager()->killSideFx(_slotKey); +} + +bool ActionTimer::execute() { + if (_engine->getScriptManager()->getSideFX(_slotKey)) + return true; + _engine->getScriptManager()->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); + return true; } -bool ActionTimer::execute(ZVision *engine) { - engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time)); +////////////////////////////////////////////////////////////////////////////// +// ActionTtyText +////////////////////////////////////////////////////////////////////////////// + +ActionTtyText::ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char filename[64]; + int32 x1, y1, x2, y2; + sscanf(line.c_str(), "%d %d %d %d %s %u", &x1, &y1, &x2, &y2, filename, &_delay); + _r = Common::Rect(x1, y1, x2, y2); + _filename = Common::String(filename); +} + +ActionTtyText::~ActionTtyText() { + _engine->getScriptManager()->killSideFx(_slotKey); +} + +bool ActionTtyText::execute() { + if (_engine->getScriptManager()->getSideFX(_slotKey)) + return true; + _engine->getScriptManager()->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); return true; } diff --git a/engines/zvision/scripting/actions.h b/engines/zvision/scripting/actions.h index 01457d21cc59..03a249e580d7 100644 --- a/engines/zvision/scripting/actions.h +++ b/engines/zvision/scripting/actions.h @@ -24,6 +24,7 @@ #define ZVISION_ACTIONS_H #include "common/str.h" +#include "common/rect.h" #include "audio/mixer.h" @@ -32,6 +33,7 @@ namespace ZVision { // Forward declaration of ZVision. This file is included before ZVision is declared class ZVision; +class ValueSlot; /** * The base class that represents any action that a Puzzle can take. @@ -39,6 +41,7 @@ class ZVision; */ class ResultAction { public: + ResultAction(ZVision *engine, int32 slotkey) : _engine(engine), _slotKey(slotkey) {} virtual ~ResultAction() {} /** * This is called by the script system whenever a Puzzle's criteria are found to be true. @@ -48,75 +51,47 @@ class ResultAction { * @param engine A pointer to the base engine so the ResultAction can access all the necessary methods * @return Should the script system continue to test any remaining puzzles (true) or immediately break and go on to the next frame (false) */ - virtual bool execute(ZVision *engine) = 0; -}; - - -// The different types of actions -// DEBUG, -// DISABLE_CONTROL, -// DISABLE_VENUS, -// DISPLAY_MESSAGE, -// DISSOLVE, -// DISTORT, -// ENABLE_CONTROL, -// FLUSH_MOUSE_EVENTS, -// INVENTORY, -// KILL, -// MENU_BAR_ENABLE, -// MUSIC, -// PAN_TRACK, -// PLAY_PRELOAD, -// PREFERENCES, -// QUIT, -// RANDOM, -// REGION, -// RESTORE_GAME, -// ROTATE_TO, -// SAVE_GAME, -// SET_PARTIAL_SCREEN, -// SET_SCREEN, -// SET_VENUS, -// STOP, -// STREAM_VIDEO, -// SYNC_SOUND, -// TTY_TEXT, -// UNIVERSE_MUSIC, + virtual bool execute() = 0; +protected: + ZVision *_engine; + int32 _slotKey; +}; class ActionAdd : public ResultAction { public: - ActionAdd(const Common::String &line); - bool execute(ZVision *engine); + ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _key; - uint _value; + int _value; }; class ActionAssign : public ResultAction { public: - ActionAssign(const Common::String &line); - bool execute(ZVision *engine); + ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionAssign(); + bool execute(); private: uint32 _key; - uint _value; + ValueSlot *_value; }; class ActionAttenuate : public ResultAction { public: - ActionAttenuate(const Common::String &line); - bool execute(ZVision *engine); + ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _key; - int _attenuation; + int32 _attenuation; }; class ActionChangeLocation : public ResultAction { public: - ActionChangeLocation(const Common::String &line); - bool execute(ZVision *engine); + ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: char _world; @@ -128,41 +103,49 @@ class ActionChangeLocation : public ResultAction { class ActionCrossfade : public ResultAction { public: - ActionCrossfade(const Common::String &line); - bool execute(ZVision *engine); + ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _keyOne; uint32 _keyTwo; - uint _oneStartVolume; - uint _twoStartVolume; - uint _oneEndVolume; - uint _twoEndVolume; - uint _timeInMillis; + int32 _oneStartVolume; + int32 _twoStartVolume; + int32 _oneEndVolume; + int32 _twoEndVolume; + int32 _timeInMillis; +}; + +class ActionCursor : public ResultAction { +public: + ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + uint8 _action; }; class ActionDebug : public ResultAction { public: - ActionDebug(const Common::String &line); - bool execute(ZVision *engine); + ActionDebug(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: }; class ActionDelayRender : public ResultAction { public: - ActionDelayRender(const Common::String &line); - bool execute(ZVision *engine); + ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: - // TODO: Check if this should actually be frames or if it should be milliseconds/seconds - uint32 framesToDelay; + uint32 _framesToDelay; }; class ActionDisableControl : public ResultAction { public: - ActionDisableControl(const Common::String &line); - bool execute(ZVision *engine); + ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _key; @@ -170,79 +153,138 @@ class ActionDisableControl : public ResultAction { class ActionDisableVenus : public ResultAction { public: - ActionDisableVenus(const Common::String &line); - bool execute(ZVision *engine); + ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: + int32 _key; }; class ActionDisplayMessage : public ResultAction { public: - ActionDisplayMessage(const Common::String &line); - bool execute(ZVision *engine); + ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: + int16 _control; + int16 _msgid; }; class ActionDissolve : public ResultAction { public: - ActionDissolve(); - bool execute(ZVision *engine); + ActionDissolve(ZVision *engine); + bool execute(); }; class ActionDistort : public ResultAction { public: - ActionDistort(const Common::String &line); - bool execute(ZVision *engine); + ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionDistort(); + bool execute(); private: + int16 _distSlot; + int16 _speed; + float _startAngle; + float _endAngle; + float _startLineScale; + float _endLineScale; }; class ActionEnableControl : public ResultAction { public: - ActionEnableControl(const Common::String &line); - bool execute(ZVision *engine); + ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + uint32 _key; +}; + +class ActionFlushMouseEvents : public ResultAction { +public: + ActionFlushMouseEvents(ZVision *engine, int32 slotkey); + bool execute(); +}; + +class ActionInventory : public ResultAction { +public: + ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); +private: + uint8 _type; + int32 _key; +}; + +class ActionKill : public ResultAction { +public: + ActionKill(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _key; + uint32 _type; +}; + +class ActionMenuBarEnable : public ResultAction { +public: + ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); +private: + uint16 _menus; }; class ActionMusic : public ResultAction { public: - ActionMusic(const Common::String &line); - bool execute(ZVision *engine); + ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global); + ~ActionMusic(); + bool execute(); private: uint32 _key; - Audio::Mixer::SoundType _soundType; Common::String _fileName; bool _loop; byte _volume; + bool _universe; + bool _midi; + int8 _note; + int8 _prog; +}; + +class ActionPanTrack : public ResultAction { +public: + ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionPanTrack(); + bool execute(); + +private: + int32 _pos; + uint32 _musicSlot; }; class ActionPlayAnimation : public ResultAction { public: - ActionPlayAnimation(const Common::String &line); - bool execute(ZVision *engine); + ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionPlayAnimation(); + bool execute(); private: uint32 _key; Common::String _fileName; uint32 _x; uint32 _y; - uint32 _width; - uint32 _height; + uint32 _x2; + uint32 _y2; uint32 _start; uint32 _end; - uint _mask; - uint _framerate; - uint _loopCount; + int32 _mask; + int32 _framerate; + int32 _loopCount; }; class ActionPlayPreloadAnimation : public ResultAction { public: - ActionPlayPreloadAnimation(const Common::String &line); - bool execute(ZVision *engine); + ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint32 _animationKey; @@ -258,64 +300,130 @@ class ActionPlayPreloadAnimation : public ResultAction { class ActionPreloadAnimation : public ResultAction { public: - ActionPreloadAnimation(const Common::String &line); - bool execute(ZVision *engine); + ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionPreloadAnimation(); + bool execute(); private: uint32 _key; Common::String _fileName; - uint _mask; - uint _framerate; + int32 _mask; + int32 _framerate; +}; + +class ActionPreferences : public ResultAction { +public: + ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + bool _save; }; class ActionQuit : public ResultAction { public: - ActionQuit() {} - bool execute(ZVision *engine); + ActionQuit(ZVision *engine, int32 slotkey) : ResultAction(engine, slotkey) {} + bool execute(); +}; + +class ActionRegion : public ResultAction { +public: + ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionRegion(); + bool execute(); + +private: + Common::String _art; + Common::String _custom; + Common::Rect _rect; + uint16 _delay; + uint16 _type; + uint16 _unk1; + uint16 _unk2; }; // TODO: See if this exists in ZGI. It doesn't in ZNem class ActionUnloadAnimation : public ResultAction { public: - ActionUnloadAnimation(const Common::String &line); - bool execute(ZVision *engine); + ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); +private: + uint32 _key; }; class ActionRandom : public ResultAction { public: - ActionRandom(const Common::String &line); - bool execute(ZVision *engine); + ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionRandom(); + bool execute(); private: uint32 _key; - uint _max; + ValueSlot *_max; +}; + +class ActionRestoreGame : public ResultAction { +public: + ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + Common::String _fileName; +}; + +class ActionRotateTo : public ResultAction { +public: + ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + int32 _toPos; + int32 _time; }; class ActionSetPartialScreen : public ResultAction { public: - ActionSetPartialScreen(const Common::String &line); - bool execute(ZVision *engine); + ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: uint _x; uint _y; Common::String _fileName; - uint16 _backgroundColor; + int32 _backgroundColor; }; class ActionSetScreen : public ResultAction { public: - ActionSetScreen(const Common::String &line); - bool execute(ZVision *engine); + ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: Common::String _fileName; }; +class ActionSetVenus : public ResultAction { +public: + ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + int32 _key; +}; + +class ActionStop : public ResultAction { +public: + ActionStop(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); + +private: + uint32 _key; +}; + class ActionStreamVideo : public ResultAction { public: - ActionStreamVideo(const Common::String &line); - bool execute(ZVision *engine); + ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); private: enum { @@ -331,16 +439,37 @@ class ActionStreamVideo : public ResultAction { bool _skippable; }; -class ActionTimer : public ResultAction { +class ActionSyncSound : public ResultAction { public: - ActionTimer(const Common::String &line); - bool execute(ZVision *engine); + ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line); + bool execute(); +private: + int _syncto; + Common::String _fileName; +}; + +class ActionTimer : public ResultAction { +public: + ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionTimer(); + bool execute(); private: uint32 _key; - uint _time; + ValueSlot *_time; }; +class ActionTtyText : public ResultAction { +public: + ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionTtyText(); + bool execute(); + +private: + Common::String _filename; + uint32 _delay; + Common::Rect _r; +}; } // End of namespace ZVision #endif diff --git a/engines/zvision/scripting/control.cpp b/engines/zvision/scripting/control.cpp index 2343c83c569b..e69d57f75c86 100644 --- a/engines/zvision/scripting/control.cpp +++ b/engines/zvision/scripting/control.cpp @@ -23,6 +23,7 @@ #include "common/scummsys.h" #include "zvision/scripting/control.h" +#include "zvision/scripting/script_manager.h" #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" @@ -33,24 +34,6 @@ namespace ZVision { -void Control::enable() { - if (!_enabled) { - _enabled = true; - return; - } - - debug("Control %u is already enabled", _key); -} - -void Control::disable() { - if (_enabled) { - _enabled = false; - return; - } - - debug("Control %u is already disabled", _key); -} - void Control::parseFlatControl(ZVision *engine) { engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); } @@ -79,7 +62,9 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream & renderTable->setPanoramaReverse(true); } } else if (line.matchString("zeropoint*", true)) { - // TODO: Implement + uint point; + sscanf(line.c_str(), "zeropoint(%u)", &point); + renderTable->setPanoramaZeroPoint(point); } line = stream.readLine(); @@ -121,4 +106,34 @@ void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stre renderTable->generateRenderTable(); } +void Control::getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) { + const char *chrs = inputStr.c_str(); + uint lbr; + + for (lbr = 0; lbr < inputStr.size(); lbr++) + if (chrs[lbr] == '(') + break; + + if (lbr >= inputStr.size()) + return; + + uint rbr; + + for (rbr = lbr + 1; rbr < inputStr.size(); rbr++) + if (chrs[rbr] == ')') + break; + + if (rbr >= inputStr.size()) + return; + + parameter = Common::String(chrs, chrs + lbr); + values = Common::String(chrs + lbr + 1, chrs + rbr); +} + +void Control::setVenus() { + if (_venusId >= 0) + if (_engine->getScriptManager()->getStateValue(_venusId) > 0) + _engine->getScriptManager()->setStateValue(StateKey_Venus, _venusId); +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h index ffeacb273d32..5814c9e41977 100644 --- a/engines/zvision/scripting/control.h +++ b/engines/zvision/scripting/control.h @@ -8,12 +8,12 @@ * 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. @@ -24,6 +24,7 @@ #define ZVISION_CONTROL_H #include "common/keyboard.h" +#include "common/str.h" namespace Common { @@ -38,14 +39,32 @@ class ZVision; class Control { public: - Control() : _engine(0), _key(0), _enabled(false) {} - Control(ZVision *engine, uint32 key) : _engine(engine), _key(key), _enabled(false) {} + + enum ControlType { + CONTROL_UNKNOW, + CONTROL_INPUT, + CONTROL_PUSHTGL, + CONTROL_SLOT, + CONTROL_LEVER, + CONTROL_SAVE, + CONTROL_SAFE, + CONTROL_FIST, + CONTROL_TITLER, + CONTROL_HOTMOV, + CONTROL_PAINT + }; + + Control(ZVision *engine, uint32 key, ControlType type) : _engine(engine), _key(key), _type(type), _venusId(-1) {} virtual ~Control() {} - uint32 getKey() { return _key; } + uint32 getKey() { + return _key; + } + + ControlType getType() { + return _type; + } - virtual void enable(); - virtual void disable(); virtual void focus() {} virtual void unfocus() {} /** @@ -54,14 +73,18 @@ class Control { * @param screenSpacePos The position of the mouse in screen space * @param backgroundImageSpacePos The position of the mouse in background image space */ - virtual void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} + virtual bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + return false; + } /** * Called when LeftMouse is lifted. Default is NOP. * * @param screenSpacePos The position of the mouse in screen space * @param backgroundImageSpacePos The position of the mouse in background image space */ - virtual void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {} + virtual bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + return false; + } /** * Called on every MouseMove. Default is NOP. * @@ -69,78 +92,52 @@ class Control { * @param backgroundImageSpacePos The position of the mouse in background image space * @return Was the cursor changed? */ - virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { return false; } + virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + return false; + } /** * Called when a key is pressed. Default is NOP. * * @param keycode The key that was pressed */ - virtual void onKeyDown(Common::KeyState keyState) {} + virtual bool onKeyDown(Common::KeyState keyState) { + return false; + } /** * Called when a key is released. Default is NOP. * * @param keycode The key that was pressed */ - virtual void onKeyUp(Common::KeyState keyState) {} + virtual bool onKeyUp(Common::KeyState keyState) { + return false; + } /** * Processes the node given the deltaTime since last frame. Default is NOP. * * @param deltaTimeInMillis The number of milliseconds that have passed since last frame * @return If true, the node can be deleted after process() finishes */ - virtual bool process(uint32 deltaTimeInMillis) { return false; } - /** - * Serialize a Control for save game use. This should only be used if a Control needs - * to save values that would be different from initialization. AKA a TimerNode needs to - * store the amount of time left on the timer. Any Controls overriding this *MUST* write - * their key as the first data outputted. The default implementation is NOP. - * - * NOTE: If this method is overridden, you MUST also override deserialize() - * and needsSerialization() - * - * @param stream Stream to write any needed data to - */ - virtual void serialize(Common::WriteStream *stream) {} - /** - * De-serialize data from a save game stream. This should only be implemented if the - * Control also implements serialize(). The calling method assumes the size of the - * data read from the stream exactly equals that written in serialize(). The default - * implementation is NOP. - * - * NOTE: If this method is overridden, you MUST also override serialize() - * and needsSerialization() - * - * @param stream Save game file stream - */ - virtual void deserialize(Common::SeekableReadStream *stream) {} - /** - * If a Control overrides serialize() and deserialize(), this should return true - * - * @return Does the Control need save game serialization? - */ - virtual inline bool needsSerialization() { return false; } + virtual bool process(uint32 deltaTimeInMillis) { + return false; + } + + void setVenus(); protected: - ZVision * _engine; + ZVision *_engine; uint32 _key; - bool _enabled; + int32 _venusId; + void getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values); // Static member functions public: static void parseFlatControl(ZVision *engine); static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream); static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream); +private: + ControlType _type; }; -// TODO: Implement InputControl -// TODO: Implement SaveControl -// TODO: Implement SlotControl -// TODO: Implement SafeControl -// TODO: Implement FistControl -// TODO: Implement HotMovieControl -// TODO: Implement PaintControl -// TODO: Implement TilterControl - } // End of namespace ZVision #endif diff --git a/engines/zvision/scripting/controls/animation_control.cpp b/engines/zvision/scripting/controls/animation_control.cpp deleted file mode 100644 index e351e81d2526..000000000000 --- a/engines/zvision/scripting/controls/animation_control.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* 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 "zvision/scripting/controls/animation_control.h" - -#include "zvision/zvision.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/scripting/script_manager.h" -#include "zvision/animation/rlf_animation.h" -#include "zvision/video/zork_avi_decoder.h" - -#include "video/video_decoder.h" - -#include "graphics/surface.h" - - -namespace ZVision { - -AnimationControl::AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName) - : Control(engine, controlKey), - _fileType(RLF), - _loopCount(1), - _currentLoop(0), - _accumulatedTime(0), - _cachedFrame(0), - _cachedFrameNeedsDeletion(false) { - if (fileName.hasSuffix(".rlf")) { - _fileType = RLF; - _animation.rlf = new RlfAnimation(fileName, false); - } else if (fileName.hasSuffix(".avi")) { - _fileType = AVI; - _animation.avi = new ZorkAVIDecoder(); - _animation.avi->loadFile(fileName); - } else { - warning("Unrecognized animation file type: %s", fileName.c_str()); - } - - _cachedFrame = new Graphics::Surface(); -} - -AnimationControl::~AnimationControl() { - if (_fileType == RLF) { - delete _animation.rlf; - } else if (_fileType == AVI) { - delete _animation.avi; - } - - _cachedFrame->free(); - delete _cachedFrame; -} - -bool AnimationControl::process(uint32 deltaTimeInMillis) { - if (!_enabled) { - return false; - } - - bool finished = false; - - if (_fileType == RLF) { - _accumulatedTime += deltaTimeInMillis; - - uint32 frameTime = _animation.rlf->frameTime(); - if (_accumulatedTime >= frameTime) { - while (_accumulatedTime >= frameTime) { - _accumulatedTime -= frameTime; - - // Make sure the frame is inside the working window - // If it's not, then just return - - RenderManager *renderManager = _engine->getRenderManager(); - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _animation.rlf->width(), workingWindowPoint.y + _animation.rlf->height()); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - const Graphics::Surface *frame = _animation.rlf->getNextFrame(); - - // Animation frames for PANORAMAs are transposed, so un-transpose them - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - if (state == RenderTable::PANORAMA) { - Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); - - renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame = tranposedFrame; - _cachedFrameNeedsDeletion = true; - } else { - // Cleanup - tranposedFrame->free(); - delete tranposedFrame; - } - } else { - renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, _animation.rlf->width(), subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame->copyFrom(*frame); - } - } - - // Check if we should continue looping - if (_animation.rlf->endOfAnimation()) { - _animation.rlf->seekToFrame(-1); - if (_loopCount > 0) { - _currentLoop++; - if (_currentLoop >= _loopCount) { - finished = true; - } - } - } - } - } else { - // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement - RenderManager *renderManager = _engine->getRenderManager(); - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); - } - } - } else if (_fileType == AVI) { - if (!_animation.avi->isPlaying()) { - _animation.avi->start(); - } - - if (_animation.avi->needsUpdate()) { - const Graphics::Surface *frame = _animation.avi->decodeNextFrame(); - - if (frame) { - // Make sure the frame is inside the working window - // If it's not, then just return - - RenderManager *renderManager = _engine->getRenderManager(); - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + frame->w, workingWindowPoint.y + frame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - // Animation frames for PANORAMAs are transposed, so un-transpose them - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - if (state == RenderTable::PANORAMA) { - Graphics::Surface *tranposedFrame = RenderManager::tranposeSurface(frame); - - renderManager->copyRectToWorkingWindow((uint16 *)tranposedFrame->getBasePtr(tranposedFrame->w - subRect.width(), tranposedFrame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame = tranposedFrame; - _cachedFrameNeedsDeletion = true; - } else { - // Cleanup - tranposedFrame->free(); - delete tranposedFrame; - } - } else { - renderManager->copyRectToWorkingWindow((const uint16 *)frame->getBasePtr(frame->w - subRect.width(), frame->h - subRect.height()), subRect.left, subRect.top, frame->w, subRect.width(), subRect.height()); - - // If the background can move, we need to cache the last frame so it can be rendered during background movement - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (_cachedFrameNeedsDeletion) { - _cachedFrame->free(); - delete _cachedFrame; - _cachedFrameNeedsDeletion = false; - } - _cachedFrame->copyFrom(*frame); - } - } - } else { - // If the background can move, we have to keep rendering animation frames, otherwise the animation flickers during background movement - RenderManager *renderManager = _engine->getRenderManager(); - RenderTable::RenderState state = renderManager->getRenderTable()->getRenderState(); - - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - Common::Point workingWindowPoint = renderManager->imageSpaceToWorkingWindowSpace(Common::Point(_x, _y)); - Common::Rect subRect(workingWindowPoint.x, workingWindowPoint.y, workingWindowPoint.x + _cachedFrame->w, workingWindowPoint.y + _cachedFrame->h); - - // If the clip returns false, it means the animation is outside the working window - if (!renderManager->clipRectToWorkingWindow(subRect)) { - return false; - } - - renderManager->copyRectToWorkingWindow((uint16 *)_cachedFrame->getBasePtr(_cachedFrame->w - subRect.width(), _cachedFrame->h - subRect.height()), subRect.left, subRect.top, _cachedFrame->w, subRect.width(), subRect.height()); - } - } - } - - // Check if we should continue looping - if (_animation.avi->endOfVideo()) { - _animation.avi->rewind(); - if (_loopCount > 0) { - _currentLoop++; - if (_currentLoop >= _loopCount) { - _animation.avi->stop(); - finished = true; - } - } - } - } - - // If we're done, set _animation key = 2 (Why 2? I don't know. It's just the value that they used) - // Then disable the control. DON'T delete it. It can be re-used - if (finished) { - _engine->getScriptManager()->setStateValue(_animationKey, 2); - disable(); - _currentLoop = 0; - } - - return false; -} - -} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/fist_control.cpp b/engines/zvision/scripting/controls/fist_control.cpp new file mode 100644 index 000000000000..9bb03cba5181 --- /dev/null +++ b/engines/zvision/scripting/controls/fist_control.cpp @@ -0,0 +1,320 @@ +/* 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 "zvision/scripting/controls/fist_control.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/cursors/cursor_manager.h" +#include "zvision/animation/meta_animation.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" +#include "common/file.h" +#include "common/system.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_FIST) { + _cursor = CursorIndex_Idle; + _animation = NULL; + _soundKey = 0; + _fiststatus = 0; + _order = 0; + _fistnum = 0; + + _frameCur = -1; + _frameEnd = -1; + _frameTime = 0; + _lastRenderedFrame = -1; + _animationId = 0; + + clearFistArray(_fistsUp); + clearFistArray(_fistsDwn); + + _numEntries = 0; + _entries.clear(); + + _anmRect = Common::Rect(); + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("sound_key", true)) { + _soundKey = atoi(values.c_str()); + } else if (param.matchString("cursor", true)) { + _cursor = _engine->getCursorManager()->getCursorId(values); + } else if (param.matchString("descfile", true)) { + readDescFile(values); + } else if (param.matchString("animation_id", true)) { + _animationId = atoi(values.c_str()); + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } +} + +FistControl::~FistControl() { + if (_animation) + delete _animation; + + clearFistArray(_fistsUp); + clearFistArray(_fistsDwn); + _entries.clear(); +} + +void FistControl::renderFrame(uint frameNumber) { + if ((int32)frameNumber == _lastRenderedFrame) + return; + + _lastRenderedFrame = frameNumber; + + const Graphics::Surface *frameData; + + if (_animation) { + frameData = _animation->getFrameData(frameNumber); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _anmRect); + } +} + +bool FistControl::process(uint32 deltaTimeInMillis) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_frameCur >= 0 && _frameEnd >= 0) + if (_frameCur <= _frameEnd) { + _frameTime -= deltaTimeInMillis; + + if (_frameTime <= 0) { + _frameTime = _animation->frameTime(); + + renderFrame(_frameCur); + + _frameCur++; + + if (_frameCur > _frameEnd) + _engine->getScriptManager()->setStateValue(_animationId, 2); + } + } + + return false; +} + +bool FistControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (mouseIn(screenSpacePos, backgroundImageSpacePos) >= 0) { + _engine->getCursorManager()->changeCursor(_cursor); + return true; + } + + return false; +} + +bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + int fistNumber = mouseIn(screenSpacePos, backgroundImageSpacePos); + + if (fistNumber >= 0) { + setVenus(); + + uint32 oldStatus = _fiststatus; + _fiststatus ^= (1 << fistNumber); + + for (int i = 0; i < _numEntries; i++) + if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) { + _frameCur = _entries[i]._anmStrt; + _frameEnd = _entries[i]._anmEnd; + _frameTime = 0; + + _engine->getScriptManager()->setStateValue(_animationId, 1); + _engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound); + break; + } + + _engine->getScriptManager()->setStateValue(_key, _fiststatus); + } + + return false; +} + +void FistControl::readDescFile(const Common::String &fileName) { + Common::File file; + if (!_engine->getSearchManager()->openFile(file, fileName)) { + warning("Desc file %s could could be opened", fileName.c_str()); + return; + } + + Common::String line; + Common::String param; + Common::String values; + + while (!file.eos()) { + line = file.readLine(); + getFistParams(line, param, values); + + if (param.matchString("animation_id", true)) { + // Not used + } else if (param.matchString("animation", true)) { + _animation = new MetaAnimation(values, _engine); + } else if (param.matchString("anim_rect", true)) { + int left, top, right, bottom; + sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom); + _anmRect = Common::Rect(left, top, right, bottom); + } else if (param.matchString("num_fingers", true)) { + sscanf(values.c_str(), "%d", &_fistnum); + _fistsUp.resize(_fistnum); + _fistsDwn.resize(_fistnum); + } else if (param.matchString("entries", true)) { + sscanf(values.c_str(), "%d", &_numEntries); + _entries.resize(_numEntries); + } else if (param.matchString("eval_order_ascending", true)) { + sscanf(values.c_str(), "%d", &_order); + } else if (param.matchString("up_hs_num_*", true)) { + int fist, num; + num = atoi(values.c_str()); + + sscanf(param.c_str(), "up_hs_num_%d", &fist); + _fistsUp[fist].resize(num); + } else if (param.matchString("up_hs_*", true)) { + int16 fist, box, x1, y1, x2, y2; + sscanf(param.c_str(), "up_hs_%hd_%hd", &fist, &box); + sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2); + (_fistsUp[fist])[box] = Common::Rect(x1, y1, x2, y2); + } else if (param.matchString("down_hs_num_*", true)) { + int fist, num; + num = atoi(values.c_str()); + + sscanf(param.c_str(), "down_hs_num_%d", &fist); + _fistsDwn[fist].resize(num); + } else if (param.matchString("down_hs_*", true)) { + int16 fist, box, x1, y1, x2, y2; + sscanf(param.c_str(), "down_hs_%hd_%hd", &fist, &box); + sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2); + (_fistsDwn[fist])[box] = Common::Rect(x1, y1, x2, y2); + } else { + int entry, start, end, sound; + char bitsStart[33]; + char bitsEnd[33]; + entry = atoi(param.c_str()); + if (sscanf(values.c_str(), "%s %s %d %d (%d)", bitsStart, bitsEnd, &start, &end, &sound) == 5) { + _entries[entry]._bitsStrt = readBits(bitsStart); + _entries[entry]._bitsEnd = readBits(bitsEnd); + _entries[entry]._anmStrt = start; + _entries[entry]._anmEnd = end; + _entries[entry]._sound = sound; + } + } + } + file.close(); +} + +void FistControl::clearFistArray(Common::Array< Common::Array > &arr) { + for (uint i = 0; i < arr.size(); i++) + arr[i].clear(); + + arr.clear(); +} + +uint32 FistControl::readBits(const char *str) { + uint32 bfield = 0; + int len = strlen(str); + for (int i = 0; i < len; i++) + if (str[i] != '0') + bfield |= (1 << i); + return bfield; +} + +int FistControl::mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_order) { + for (int i = 0; i < _fistnum; i++) { + if (((_fiststatus >> i) & 1) == 1) { + for (uint j = 0; j < _fistsDwn[i].size(); j++) + if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos)) + return i; + } else { + for (uint j = 0; j < _fistsUp[i].size(); j++) + if ((_fistsUp[i])[j].contains(backgroundImageSpacePos)) + return i; + } + } + } else { + for (int i = _fistnum - 1; i >= 0; i--) { + if (((_fiststatus >> i) & 1) == 1) { + for (uint j = 0; j < _fistsDwn[i].size(); j++) + if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos)) + return i; + } else { + for (uint j = 0; j < _fistsUp[i].size(); j++) + if ((_fistsUp[i])[j].contains(backgroundImageSpacePos)) + return i; + } + } + } + return -1; +} + +void FistControl::getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) { + const char *chrs = inputStr.c_str(); + uint lbr; + + for (lbr = 0; lbr < inputStr.size(); lbr++) + if (chrs[lbr] == ':') + break; + + if (lbr >= inputStr.size()) + return; + + uint rbr; + + for (rbr = lbr + 1; rbr < inputStr.size(); rbr++) + if (chrs[rbr] == '~') + break; + + if (rbr >= inputStr.size()) + return; + + parameter = Common::String(chrs, chrs + lbr); + values = Common::String(chrs + lbr + 1, chrs + rbr); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/fist_control.h b/engines/zvision/scripting/controls/fist_control.h new file mode 100644 index 000000000000..33c3c7b579a9 --- /dev/null +++ b/engines/zvision/scripting/controls/fist_control.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. + * + */ + +#ifndef ZVISION_FIST_CONTROL_H +#define ZVISION_FIST_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "common/array.h" +#include "common/rect.h" + + +namespace ZVision { + +class MetaAnimation; + +class FistControl : public Control { +public: + FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~FistControl(); + +private: + uint32 _fiststatus; + int _fistnum; + int16 _cursor; + int _order; + + Common::Array< Common::Array > _fistsUp; + Common::Array< Common::Array > _fistsDwn; + + int32 _numEntries; + + struct entries { + uint32 _bitsStrt; + uint32 _bitsEnd; + int32 _anmStrt; + int32 _anmEnd; + int32 _sound; + }; + + Common::Array _entries; + + MetaAnimation *_animation; + Common::Rect _anmRect; + int32 _soundKey; + int32 _frameCur; + int32 _frameEnd; + int32 _frameTime; + int32 _lastRenderedFrame; + int32 _animationId; + +public: + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool process(uint32 deltaTimeInMillis); + +private: + void renderFrame(uint frameNumber); + void readDescFile(const Common::String &fileName); + void clearFistArray(Common::Array< Common::Array > &arr); + uint32 readBits(const char *str); + int mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + void getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/hotmov_control.cpp b/engines/zvision/scripting/controls/hotmov_control.cpp new file mode 100644 index 000000000000..fd7afb92d1e5 --- /dev/null +++ b/engines/zvision/scripting/controls/hotmov_control.cpp @@ -0,0 +1,201 @@ +/* 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 "zvision/scripting/controls/hotmov_control.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/cursors/cursor_manager.h" +#include "zvision/animation/meta_animation.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" +#include "common/file.h" +#include "common/system.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_HOTMOV) { + _animation = NULL; + _cycle = 0; + _curFrame = -1; + _lastRenderedFrame = -1; + _frames.clear(); + _frameTime = 0; + _cyclesCount = 0; + _framesCount = 0; + + _engine->getScriptManager()->setStateValue(_key, 0); + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("hs_frame_list", true)) { + readHsFile(values); + } else if (param.matchString("rectangle", true)) { + int x; + int y; + int width; + int height; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height); + + _rectangle = Common::Rect(x, y, width, height); + } else if (param.matchString("num_frames", true)) { + _framesCount = atoi(values.c_str()); + } else if (param.matchString("num_cycles", true)) { + _cyclesCount = atoi(values.c_str()); + } else if (param.matchString("animation", true)) { + char filename[64]; + sscanf(values.c_str(), "%s", filename); + values = Common::String(filename); + _animation = new MetaAnimation(values, _engine); + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } +} + +HotMovControl::~HotMovControl() { + if (_animation) + delete _animation; + + _frames.clear(); +} + +void HotMovControl::renderFrame(uint frameNumber) { + if ((int)frameNumber == _lastRenderedFrame) + return; + + _lastRenderedFrame = frameNumber; + + const Graphics::Surface *frameData; + + if (_animation) { + frameData = _animation->getFrameData(frameNumber); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); + } +} + +bool HotMovControl::process(uint32 deltaTimeInMillis) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_cycle < _cyclesCount) { + _frameTime -= deltaTimeInMillis; + + if (_frameTime <= 0) { + _curFrame++; + if (_curFrame >= _framesCount) { + _curFrame = 0; + _cycle++; + } + if (_cycle != _cyclesCount) + renderFrame(_curFrame); + else + _engine->getScriptManager()->setStateValue(_key, 2); + + _frameTime = _animation->frameTime(); + } + } + + return false; +} + +bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_cycle < _cyclesCount) { + if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(CursorIndex_Active); + return true; + } + } + + return false; +} + +bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_cycle < _cyclesCount) { + if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + setVenus(); + _engine->getScriptManager()->setStateValue(_key, 1); + return true; + } + } + + return false; +} + +void HotMovControl::readHsFile(const Common::String &fileName) { + if (_framesCount == 0) + return; + + Common::File file; + if (!_engine->getSearchManager()->openFile(file, fileName)) { + warning("HS file %s could could be opened", fileName.c_str()); + return; + } + + Common::String line; + + _frames.resize(_framesCount); + + while (!file.eos()) { + line = file.readLine(); + + int frame; + int x; + int y; + int width; + int height; + + sscanf(line.c_str(), "%d:%d %d %d %d~", &frame, &x, &y, &width, &height); + + if (frame >= 0 && frame < _framesCount) + _frames[frame] = Common::Rect(x, y, width, height); + } + file.close(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/hotmov_control.h b/engines/zvision/scripting/controls/hotmov_control.h new file mode 100644 index 000000000000..9e01b40bab59 --- /dev/null +++ b/engines/zvision/scripting/controls/hotmov_control.h @@ -0,0 +1,63 @@ +/* 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 ZVISION_HOTMOV_CONTROL_H +#define ZVISION_HOTMOV_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "common/array.h" +#include "common/rect.h" + + +namespace ZVision { + +class MetaAnimation; + +class HotMovControl : public Control { +public: + HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~HotMovControl(); + +private: + int32 _framesCount; + int32 _frameTime; + int32 _curFrame; + int32 _lastRenderedFrame; + int32 _cycle; + int32 _cyclesCount; + MetaAnimation *_animation; + Common::Rect _rectangle; + Common::Array _frames; +public: + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool process(uint32 deltaTimeInMillis); + +private: + void renderFrame(uint frameNumber); + void readHsFile(const Common::String &fileName); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp index a35548d02ea3..8af436bb4035 100644 --- a/engines/zvision/scripting/controls/input_control.cpp +++ b/engines/zvision/scripting/controls/input_control.cpp @@ -8,12 +8,12 @@ * 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. @@ -23,10 +23,11 @@ #include "common/scummsys.h" #include "zvision/scripting/controls/input_control.h" +#include "zvision/cursors/cursor_manager.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" -#include "zvision/strings/string_manager.h" +#include "zvision/text/string_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/utility/utility.h" @@ -38,105 +39,218 @@ namespace ZVision { InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key), - _nextTabstop(0), - _focused(false), - _textChanged(false), - _cursorOffset(0) { + : Control(engine, key, CONTROL_INPUT), + _nextTabstop(0), + _focused(false), + _textChanged(false), + _cursorOffset(0), + _enterPressed(false), + _readOnly(false), + _txtWidth(0), + _animation(NULL) { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*rectangle*", true)) { + if (param.matchString("rectangle", true)) { int x1; int y1; int x2; int y2; - sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); + sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2); _textRectangle = Common::Rect(x1, y1, x2, y2); - } else if (line.matchString("*aux_hotspot*", true)) { + } else if (param.matchString("aux_hotspot", true)) { int x1; int y1; int x2; int y2; - sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2); + sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2); _headerRectangle = Common::Rect(x1, y1, x2, y2); - } else if (line.matchString("*string_init*", true)) { + } else if (param.matchString("string_init", true)) { uint fontFormatNumber; - sscanf(line.c_str(), "%*[^(](%u)", &fontFormatNumber); + sscanf(values.c_str(), "%u", &fontFormatNumber); - _textStyle = _engine->getStringManager()->getTextStyle(fontFormatNumber); - } else if (line.matchString("*next_tabstop*", true)) { - sscanf(line.c_str(), "%*[^(](%u)", &_nextTabstop); - } else if (line.matchString("*cursor_animation*", true)) { - char fileName[26]; + _stringInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + } else if (param.matchString("chooser_init_string", true)) { + uint fontFormatNumber; - sscanf(line.c_str(), "%*[^(](%25s %*u)", fileName); + sscanf(values.c_str(), "%u", &fontFormatNumber); - _cursorAnimationFileName = Common::String(fileName); - } else if (line.matchString("*cursor_dimensions*", true)) { + _stringChooserInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + } else if (param.matchString("next_tabstop", true)) { + sscanf(values.c_str(), "%u", &_nextTabstop); + } else if (param.matchString("cursor_dimensions", true)) { // Ignore, use the dimensions in the animation file - } else if (line.matchString("*cursor_animation_frames*", true)) { + } else if (param.matchString("cursor_animation_frames", true)) { // Ignore, use the frame count in the animation file - } else if (line.matchString("*focus*", true)) { + } else if (param.matchString("cursor_animation", true)) { + char fileName[25]; + + sscanf(values.c_str(), "%25s %*u", fileName); + + _animation = new MetaAnimation(fileName, _engine); + _frame = -1; + _frameDelay = 0; + } else if (param.matchString("focus", true)) { _focused = true; + _engine->getScriptManager()->setFocusControlKey(_key); + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); } } -void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - _engine->getScriptManager()->focusControl(_key); +bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_textRectangle.contains(backgroundImageSpacePos)) { + if (!_readOnly) { + // Save + _engine->getScriptManager()->focusControl(_key); + setVenus(); + } else { + // Restore + if (_currentInputText.size()) { + setVenus(); + _enterPressed = true; + } + } + } + return false; } -void InputControl::onKeyDown(Common::KeyState keyState) { +bool InputControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_textRectangle.contains(backgroundImageSpacePos)) { + if (!_readOnly) { + // Save + _engine->getCursorManager()->changeCursor(CursorIndex_Active); + return true; + } else { + // Restore + if (_currentInputText.size()) { + _engine->getCursorManager()->changeCursor(CursorIndex_Active); + _engine->getScriptManager()->focusControl(_key); + return true; + } + } + } + return false; +} + +bool InputControl::onKeyDown(Common::KeyState keyState) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + if (!_focused) { - return; + return false; } if (keyState.keycode == Common::KEYCODE_BACKSPACE) { - _currentInputText.deleteLastChar(); + if (!_readOnly) { + _currentInputText.deleteLastChar(); + _textChanged = true; + } + } else if (keyState.keycode == Common::KEYCODE_RETURN) { + _enterPressed = true; } else if (keyState.keycode == Common::KEYCODE_TAB) { - _focused = false; + unfocus(); // Focus the next input control _engine->getScriptManager()->focusControl(_nextTabstop); + // Don't process this event for other controls + return true; } else { - // Otherwise, append the new character to the end of the current text - - uint16 asciiValue = keyState.ascii; - // We only care about text values - if (asciiValue >= 32 && asciiValue <= 126) { - _currentInputText += (char)asciiValue; - _textChanged = true; + if (!_readOnly) { + // Otherwise, append the new character to the end of the current text + uint16 asciiValue = keyState.ascii; + // We only care about text values + if (asciiValue >= 32 && asciiValue <= 126) { + _currentInputText += (char)asciiValue; + _textChanged = true; + } } } + return false; } bool InputControl::process(uint32 deltaTimeInMillis) { - if (!_focused) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - } // First see if we need to render the text if (_textChanged) { // Blit the text using the RenderManager - Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width()); - _cursorOffset = destRect.left - _textRectangle.left; + Graphics::Surface txt; + txt.create(_textRectangle.width(), _textRectangle.height(), _engine->_pixelFormat); + + if (!_readOnly || !_focused) + _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringInit, txt); + else + _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringChooserInit, txt); + + _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top); + + txt.free(); } - // Render the next frame of the animation - // TODO: Implement animation handling + if (_animation && !_readOnly && _focused) { + bool needDraw = true;// = _textChanged; + _frameDelay -= deltaTimeInMillis; + if (_frameDelay <= 0) { + _frame = (_frame + 1) % _animation->frameCount(); + _frameDelay = _animation->frameTime(); + needDraw = true; + } + if (needDraw) { + const Graphics::Surface *srf = _animation->getFrameData(_frame); + uint32 xx = _textRectangle.left + _txtWidth; + if (xx >= _textRectangle.left + (_textRectangle.width() - _animation->width())) + xx = _textRectangle.left + _textRectangle.width() - _animation->width(); + _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top); + } + } + + _textChanged = false; + return false; +} + +bool InputControl::enterPress() { + if (_enterPressed) { + _enterPressed = false; + return true; + } return false; } +void InputControl::setText(const Common::String &_str) { + _currentInputText = _str; + _textChanged = true; +} + +const Common::String InputControl::getText() { + return _currentInputText; +} + +void InputControl::setReadOnly(bool readonly) { + _readOnly = readonly; +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h index 32432438bb5e..9a829d30f6b2 100644 --- a/engines/zvision/scripting/controls/input_control.h +++ b/engines/zvision/scripting/controls/input_control.h @@ -24,7 +24,9 @@ #define ZVISION_INPUT_CONTROL_H #include "zvision/scripting/control.h" -#include "zvision/strings/string_manager.h" +#include "zvision/animation/meta_animation.h" +#include "zvision/text/text.h" +#include "zvision/text/string_manager.h" #include "common/rect.h" @@ -38,21 +40,39 @@ class InputControl : public Control { private: Common::Rect _textRectangle; Common::Rect _headerRectangle; - StringManager::TextStyle _textStyle; + cTxtStyle _stringInit; + cTxtStyle _stringChooserInit; uint32 _nextTabstop; - Common::String _cursorAnimationFileName; bool _focused; Common::String _currentInputText; bool _textChanged; uint _cursorOffset; + bool _enterPressed; + bool _readOnly; + + int16 _txtWidth; + MetaAnimation *_animation; + int32 _frameDelay; + int16 _frame; public: - void focus() { _focused = true; } - void unfocus() { _focused = false; } - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - void onKeyDown(Common::KeyState keyState); + void focus() { + _focused = true; + _textChanged = true; + } + void unfocus() { + _focused = false; + _textChanged = true; + } + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onKeyDown(Common::KeyState keyState); bool process(uint32 deltaTimeInMillis); + void setText(const Common::String &_str); + const Common::String getText(); + bool enterPress(); + void setReadOnly(bool); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index c029a2a7a145..1f176ef9d098 100644 --- a/engines/zvision/scripting/controls/lever_control.cpp +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -8,12 +8,12 @@ * 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. @@ -28,8 +28,7 @@ #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/cursors/cursor_manager.h" -#include "zvision/animation/rlf_animation.h" -#include "zvision/video/zork_avi_decoder.h" +#include "zvision/animation/meta_animation.h" #include "zvision/utility/utility.h" #include "common/stream.h" @@ -43,114 +42,113 @@ namespace ZVision { LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key), - _frameInfo(0), - _frameCount(0), - _startFrame(0), - _currentFrame(0), - _lastRenderedFrame(0), - _mouseIsCaptured(false), - _isReturning(false), - _accumulatedTime(0), - _returnRoutesCurrentFrame(0) { + : Control(engine, key, CONTROL_LEVER), + _frameInfo(0), + _frameCount(0), + _startFrame(0), + _currentFrame(0), + _lastRenderedFrame(0), + _mouseIsCaptured(false), + _isReturning(false), + _accumulatedTime(0), + _returnRoutesCurrentFrame(0) { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*descfile*", true)) { + if (param.matchString("descfile", true)) { char levFileName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName); + sscanf(values.c_str(), "%25s", levFileName); parseLevFile(levFileName); - } else if (line.matchString("*cursor*", true)) { + } else if (param.matchString("cursor", true)) { char cursorName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName); + sscanf(values.c_str(), "%25s", cursorName); - _cursorName = Common::String(cursorName); + _cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName)); } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); } renderFrame(_currentFrame); } LeverControl::~LeverControl() { - if (_fileType == AVI) { - delete _animation.avi; - } else if (_fileType == RLF) { - delete _animation.rlf; - } + if (_animation) + delete _animation; delete[] _frameInfo; } void LeverControl::parseLevFile(const Common::String &fileName) { Common::File file; - if (!file.open(fileName)) { + if (!_engine->getSearchManager()->openFile(file, fileName)) { warning("LEV file %s could could be opened", fileName.c_str()); return; } - Common::String line = file.readLine(); + Common::String line; + Common::String param; + Common::String values; while (!file.eos()) { - if (line.matchString("*animation_id*", true)) { + line = file.readLine(); + getLevParams(line, param, values); + + if (param.matchString("animation_id", true)) { // Not used - } else if (line.matchString("*filename*", true)) { - char fileNameBuffer[25]; - sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); - - Common::String animationFileName(fileNameBuffer); - - if (animationFileName.hasSuffix(".avi")) { - _animation.avi = new ZorkAVIDecoder(); - _animation.avi->loadFile(animationFileName); - _fileType = AVI; - } else if (animationFileName.hasSuffix(".rlf")) { - _animation.rlf = new RlfAnimation(animationFileName, false); - _fileType = RLF; - } - } else if (line.matchString("*skipcolor*", true)) { + } else if (param.matchString("filename", true)) { + _animation = new MetaAnimation(values, _engine); + } else if (param.matchString("skipcolor", true)) { // Not used - } else if (line.matchString("*anim_coords*", true)) { + } else if (param.matchString("anim_coords", true)) { int left, top, right, bottom; - sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom); + sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom); _animationCoords.left = left; _animationCoords.top = top; _animationCoords.right = right; _animationCoords.bottom = bottom; - } else if (line.matchString("*mirrored*", true)) { + } else if (param.matchString("mirrored", true)) { uint mirrored; - sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); + sscanf(values.c_str(), "%u", &mirrored); _mirrored = mirrored == 0 ? false : true; - } else if (line.matchString("*frames*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); + } else if (param.matchString("frames", true)) { + sscanf(values.c_str(), "%u", &_frameCount); _frameInfo = new FrameInfo[_frameCount]; - } else if (line.matchString("*elsewhere*", true)) { + } else if (param.matchString("elsewhere", true)) { // Not used - } else if (line.matchString("*out_of_control*", true)) { + } else if (param.matchString("out_of_control", true)) { // Not used - } else if (line.matchString("*start_pos*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); + } else if (param.matchString("start_pos", true)) { + sscanf(values.c_str(), "%u", &_startFrame); _currentFrame = _startFrame; - } else if (line.matchString("*hotspot_deltas*", true)) { + } else if (param.matchString("hotspot_deltas", true)) { uint x; uint y; - sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); + sscanf(values.c_str(), "%u %u", &x, &y); _hotspotDelta.x = x; _hotspotDelta.y = y; + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); } else { uint frameNumber; uint x, y; + line.toLowercase(); + if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { _frameInfo[frameNumber].hotspot.left = x; _frameInfo[frameNumber].hotspot.top = y; @@ -158,13 +156,13 @@ void LeverControl::parseLevFile(const Common::String &fileName) { _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; } - Common::StringTokenizer tokenizer(line, " ^=()"); + Common::StringTokenizer tokenizer(line, " ^=()~"); tokenizer.nextToken(); tokenizer.nextToken(); Common::String token = tokenizer.nextToken(); while (!tokenizer.empty()) { - if (token == "D") { + if (token == "d") { token = tokenizer.nextToken(); uint angle; @@ -172,7 +170,7 @@ void LeverControl::parseLevFile(const Common::String &fileName) { sscanf(token.c_str(), "%u,%u", &toFrame, &angle); _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); - } else if (token.hasPrefix("P")) { + } else if (token.hasPrefix("p")) { // Format: P( to ) tokenizer.nextToken(); tokenizer.nextToken(); @@ -186,25 +184,25 @@ void LeverControl::parseLevFile(const Common::String &fileName) { } } - line = file.readLine(); + // Don't read lines in this place because last will not be parsed. } } -void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; - } +bool LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + setVenus(); _mouseIsCaptured = true; _lastMousePos = backgroundImageSpacePos; } + return false; } -void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; - } +bool LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; if (_mouseIsCaptured) { _mouseIsCaptured = false; @@ -214,12 +212,12 @@ void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common:: _returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin(); _returnRoutesCurrentFrame = _currentFrame; } + return false; } bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - } bool cursorWasChanged = false; @@ -240,7 +238,7 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common } } } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_cursorName); + _engine->getCursorManager()->changeCursor(_cursor); cursorWasChanged = true; } @@ -248,9 +246,8 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common } bool LeverControl::process(uint32 deltaTimeInMillis) { - if (!_enabled) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - } if (_isReturning) { _accumulatedTime += deltaTimeInMillis; @@ -301,7 +298,7 @@ int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Comm // Calculate the angle using arctan // Then convert to degrees. (180 / 3.14159 = 57.2958) - int angle = int(atan((float)yDist / (float)xDist) * 57); + int angle = int(atan((float)yDist / (float)abs(xDist)) * 57); // Calculate what quadrant pointTwo is in uint quadrant = ((yDist > 0 ? 1 : 0) << 1) | (xDist < 0 ? 1 : 0); @@ -350,16 +347,16 @@ int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Comm // Convert the local angles to unit circle angles switch (quadrant) { case 0: - angle = 180 + angle; + angle = -angle; break; case 1: - // Do nothing + angle = angle + 180; break; case 2: - angle = 180 + angle; + angle = 360 - angle; break; case 3: - angle = 360 + angle; + angle = 180 + angle; break; } @@ -377,26 +374,35 @@ void LeverControl::renderFrame(uint frameNumber) { _lastRenderedFrame = frameNumber; } - const uint16 *frameData = 0; - int x = _animationCoords.left; - int y = _animationCoords.top; - int width = 0; - int height = 0; - - if (_fileType == RLF) { - // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can - frameData = (const uint16 *)_animation.rlf->getFrameData(frameNumber)->getPixels(); - width = _animation.rlf->width(); // Use the animation width instead of _animationCoords.width() - height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height() - } else if (_fileType == AVI) { - _animation.avi->seekToFrame(frameNumber); - const Graphics::Surface *surface = _animation.avi->decodeNextFrame(); - frameData = (const uint16 *)surface->getPixels(); - width = surface->w; - height = surface->h; - } + const Graphics::Surface *frameData; + + frameData = _animation->getFrameData(frameNumber); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _animationCoords); +} + +void LeverControl::getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) { + const char *chrs = inputStr.c_str(); + uint lbr; + + for (lbr = 0; lbr < inputStr.size(); lbr++) + if (chrs[lbr] == ':') + break; + + if (lbr >= inputStr.size()) + return; + + uint rbr; + + for (rbr = lbr + 1; rbr < inputStr.size(); rbr++) + if (chrs[rbr] == '~') + break; + + if (rbr >= inputStr.size()) + return; - _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height); + parameter = Common::String(chrs, chrs + lbr); + values = Common::String(chrs + lbr + 1, chrs + rbr); } } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h index 49e4fd3806c9..8de6d1e5c98f 100644 --- a/engines/zvision/scripting/controls/lever_control.h +++ b/engines/zvision/scripting/controls/lever_control.h @@ -32,7 +32,7 @@ namespace ZVision { class ZorkAVIDecoder; -class RlfAnimation; +class MetaAnimation; class LeverControl : public Control { public: @@ -40,10 +40,6 @@ class LeverControl : public Control { ~LeverControl(); private: - enum FileType { - RLF = 1, - AVI = 2 - }; struct Direction { Direction(uint a, uint t) : angle(a), toFrame(t) {} @@ -64,13 +60,9 @@ class LeverControl : public Control { }; private: - union { - RlfAnimation *rlf; - ZorkAVIDecoder *avi; - } _animation; - FileType _fileType; + MetaAnimation *_animation; - Common::String _cursorName; + int _cursor; Common::Rect _animationCoords; bool _mirrored; uint _frameCount; @@ -88,8 +80,8 @@ class LeverControl : public Control { uint32 _accumulatedTime; public: - void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool process(uint32 deltaTimeInMillis); @@ -120,6 +112,7 @@ class LeverControl : public Control { */ static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo); void renderFrame(uint frameNumber); + void getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/paint_control.cpp b/engines/zvision/scripting/controls/paint_control.cpp new file mode 100644 index 000000000000..cb3c17e0fd7b --- /dev/null +++ b/engines/zvision/scripting/controls/paint_control.cpp @@ -0,0 +1,216 @@ +/* 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 "zvision/scripting/controls/paint_control.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/cursors/cursor_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/utility/utility.h" + +namespace ZVision { + +PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_PAINT) { + + _cursor = CursorIndex_Active; + _paint = NULL; + _bkg = NULL; + _brush = NULL; + _colorKey = 0; + _mouseDown = false; + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("rectangle", true)) { + int x; + int y; + int width; + int height; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height); + + _rectangle = Common::Rect(x, y, width + x, height + y); + } else if (param.matchString("cursor", true)) { + _cursor = _engine->getCursorManager()->getCursorId(values); + } else if (param.matchString("brush_file", true)) { + _brush = _engine->getRenderManager()->loadImage(values, false); + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); + } else if (param.matchString("paint_file", true)) { + _paint = _engine->getRenderManager()->loadImage(values, false); + } else if (param.matchString("eligible_objects", true)) { + char buf[256]; + memset(buf, 0, 256); + strcpy(buf, values.c_str()); + + char *curpos = buf; + char *strend = buf + strlen(buf); + while (true) { + char *st = curpos; + + if (st >= strend) + break; + + while (*curpos != ' ' && curpos < strend) + curpos++; + + *curpos = 0; + curpos++; + + int obj = atoi(st); + + _eligibleObjects.push_back(obj); + } + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } + + if (_paint) { + _colorKey = _paint->format.RGBToColor(255, 0, 255); + _bkg = new Graphics::Surface; + _bkg->create(_rectangle.width(), _rectangle.height(), _paint->format); + _bkg->fillRect(Common::Rect(_rectangle.width(), _rectangle.height()), _colorKey); + + Graphics::Surface *tmp = new Graphics::Surface; + tmp->create(_rectangle.width(), _rectangle.height(), _paint->format); + _engine->getRenderManager()->blitSurfaceToSurface(*_paint, _rectangle, *tmp, 0, 0); + _paint->free(); + delete _paint; + _paint = tmp; + } + + +} + +PaintControl::~PaintControl() { + // Clear the state value back to 0 + //_engine->getScriptManager()->setStateValue(_key, 0); + if (_paint) + delete _paint; + if (_brush) + delete _brush; + if (_bkg) + delete _bkg; +} + +bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + _mouseDown = false; + + return false; +} + +bool PaintControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_rectangle.contains(backgroundImageSpacePos)) { + int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + + if (eligeblity(mouseItem)) { + setVenus(); + _mouseDown = true; + } + } + + return false; +} + +bool PaintControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_rectangle.contains(backgroundImageSpacePos)) { + int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + + if (eligeblity(mouseItem)) { + _engine->getCursorManager()->changeCursor(_cursor); + + if (_mouseDown) { + Common::Rect bkgRect = paint(backgroundImageSpacePos); + if (!bkgRect.isEmpty()) { + Common::Rect imgRect = bkgRect; + imgRect.translate(-_rectangle.left, -_rectangle.top); + + Graphics::Surface imgUpdate = _bkg->getSubArea(imgRect); + + _engine->getRenderManager()->blitSurfaceToBkg(imgUpdate, bkgRect.left, bkgRect.top, _colorKey); + } + } + return true; + } + } + + return false; +} + +bool PaintControl::eligeblity(int itemId) { + for (Common::List::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++) + if (*it == itemId) + return true; + return false; +} + +Common::Rect PaintControl::paint(const Common::Point &point) { + Common::Rect paintRect = Common::Rect(_brush->w, _brush->h); + paintRect.moveTo(point); + paintRect.clip(_rectangle); + + if (!paintRect.isEmpty()) { + Common::Rect brushRect = paintRect; + brushRect.translate(-point.x, -point.y); + + Common::Rect bkgRect = paintRect; + bkgRect.translate(-_rectangle.left, -_rectangle.top); + + for (int yy = 0; yy < brushRect.height(); yy++) { + uint16 *mask = (uint16 *)_brush->getBasePtr(brushRect.left, brushRect.top + yy); + uint16 *from = (uint16 *)_paint->getBasePtr(bkgRect.left, bkgRect.top + yy); + uint16 *to = (uint16 *)_bkg->getBasePtr(bkgRect.left, bkgRect.top + yy); + for (int xx = 0; xx < brushRect.width(); xx++) { + if (*mask != 0) + *(to + xx) = *(from + xx); + + mask++; + } + } + + } + return paintRect; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/paint_control.h b/engines/zvision/scripting/controls/paint_control.h new file mode 100644 index 000000000000..aac4755fcd64 --- /dev/null +++ b/engines/zvision/scripting/controls/paint_control.h @@ -0,0 +1,92 @@ +/* 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 ZVISION_PAINT_CONTROL_H +#define ZVISION_PAINT_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "graphics/surface.h" + +#include "common/rect.h" +#include "common/list.h" + + +namespace ZVision { + +class PaintControl : public Control { +public: + PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~PaintControl(); + + /** + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + + /** + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor. + * + * @param engine The base engine + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + * @return Was the cursor changed? + */ + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + + bool process(uint32 deltaTimeInMillis) { + return false; + }; + +private: + /** + * The area that will trigger the event + * This is in image space coordinates, NOT screen space + */ + + uint32 _colorKey; + + Graphics::Surface *_paint; + Graphics::Surface *_bkg; + Graphics::Surface *_brush; + + Common::List _eligibleObjects; + + int _cursor; + Common::Rect _rectangle; + + bool _mouseDown; + + bool eligeblity(int itemId); + Common::Rect paint(const Common::Point &point); + +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp index a96c95b377d5..ea4e947abe37 100644 --- a/engines/zvision/scripting/controls/push_toggle_control.cpp +++ b/engines/zvision/scripting/controls/push_toggle_control.cpp @@ -35,64 +35,114 @@ namespace ZVision { PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key) { + : Control(engine, key, CONTROL_PUSHTGL), + _countTo(2), + _event(Common::EVENT_LBUTTONUP) { + + _hotspots.clear(); + // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*_hotspot*", true)) { + if (param.matchString("*_hotspot", true)) { uint x; uint y; uint width; uint height; - sscanf(line.c_str(), "%*[^(](%u,%u,%u,%u)", &x, &y, &width, &height); - - _hotspot = Common::Rect(x, y, x + width, y + height); - } else if (line.matchString("cursor*", true)) { - char nameBuffer[25]; - - sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer); - - _hoverCursor = Common::String(nameBuffer); + sscanf(values.c_str(), "%u,%u,%u,%u", &x, &y, &width, &height); + + _hotspots.push_back(Common::Rect(x, y, x + width + 1, y + height + 1)); + } else if (param.matchString("cursor", true)) { + _cursor = _engine->getCursorManager()->getCursorId(values); + } else if (param.matchString("animation", true)) { + // Not used + } else if (param.matchString("sound", true)) { + // Not used + } else if (param.matchString("count_to", true)) { + sscanf(values.c_str(), "%u", &_countTo); + } else if (param.matchString("mouse_event", true)) { + if (values.equalsIgnoreCase("up")) { + _event = Common::EVENT_LBUTTONUP; + } else if (values.equalsIgnoreCase("down")) { + _event = Common::EVENT_LBUTTONDOWN; + } else if (values.equalsIgnoreCase("double")) { + // Not used + } + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); } - if (_hotspot.isEmpty() || _hoverCursor.empty()) { - warning("Push_toggle cursor %u was parsed incorrectly", key); + if (_hotspots.size() == 0) { + warning("Push_toggle %u was parsed incorrectly", key); } } PushToggleControl::~PushToggleControl() { - // Clear the state value back to 0 - _engine->getScriptManager()->setStateValue(_key, 0); + _hotspots.clear(); } -void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { - return; +bool PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_event != Common::EVENT_LBUTTONUP) + return false; + + if (contain(backgroundImageSpacePos)) { + setVenus(); + int32 val = _engine->getScriptManager()->getStateValue(_key); + val = (val + 1) % _countTo; + _engine->getScriptManager()->setStateValue(_key, val); + return true; } + return false; +} + +bool PushToggleControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; - if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getScriptManager()->setStateValue(_key, 1); + if (_event != Common::EVENT_LBUTTONDOWN) + return false; + + if (contain(backgroundImageSpacePos)) { + setVenus(); + int32 val = _engine->getScriptManager()->getStateValue(_key); + val = (val + 1) % _countTo; + _engine->getScriptManager()->setStateValue(_key, val); + return true; } + return false; } bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_enabled) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - } - if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_hoverCursor); + if (contain(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_cursor); return true; } return false; } +bool PushToggleControl::contain(const Common::Point &point) { + for (uint i = 0; i < _hotspots.size(); i++) + if (_hotspots[i].contains(point)) + return true; + return false; +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/push_toggle_control.h b/engines/zvision/scripting/controls/push_toggle_control.h index 3854fc2005be..6d68b8f16296 100644 --- a/engines/zvision/scripting/controls/push_toggle_control.h +++ b/engines/zvision/scripting/controls/push_toggle_control.h @@ -26,6 +26,8 @@ #include "zvision/scripting/control.h" #include "common/rect.h" +#include "common/events.h" +#include "common/array.h" namespace ZVision { @@ -35,13 +37,20 @@ class PushToggleControl : public Control { PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); ~PushToggleControl(); + /** + * Called when LeftMouse is pushed. Default is NOP. + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); /** * Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1); * * @param screenSpacePos The position of the mouse in screen space * @param backgroundImageSpacePos The position of the mouse in background image space */ - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); /** * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor. * @@ -57,9 +66,15 @@ class PushToggleControl : public Control { * The area that will trigger the event * This is in image space coordinates, NOT screen space */ - Common::Rect _hotspot; + Common::Array _hotspots; /** The cursor to use when hovering over _hotspot */ - Common::String _hoverCursor; + int _cursor; + /** Button maximal values count */ + uint _countTo; + + Common::EventType _event; + + bool contain(const Common::Point &point); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/safe_control.cpp b/engines/zvision/scripting/controls/safe_control.cpp new file mode 100644 index 000000000000..de1ece5b197d --- /dev/null +++ b/engines/zvision/scripting/controls/safe_control.cpp @@ -0,0 +1,205 @@ +/* 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 "zvision/scripting/controls/safe_control.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/cursors/cursor_manager.h" +#include "zvision/animation/meta_animation.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" +#include "common/file.h" +#include "common/tokenizer.h" +#include "common/system.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_SAFE) { + _statesCount = 0; + _curState = 0; + _animation = NULL; + _innerRaduis = 0; + _innerRadiusSqr = 0; + _outerRadius = 0; + _outerRadiusSqr = 0; + _zeroPointer = 0; + _startPointer = 0; + _curFrame = -1; + _targetFrame = 0; + _frameTime = 0; + _lastRenderedFrame = -1; + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("animation", true)) { + _animation = new MetaAnimation(values, _engine); + } else if (param.matchString("rectangle", true)) { + int x; + int y; + int width; + int height; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height); + + _rectangle = Common::Rect(x, y, width, height); + } else if (param.matchString("num_states", true)) { + _statesCount = atoi(values.c_str()); + } else if (param.matchString("center", true)) { + int x; + int y; + + sscanf(values.c_str(), "%d %d", &x, &y); + _center = Common::Point(x, y); + } else if (param.matchString("dial_inner_radius", true)) { + _innerRaduis = atoi(values.c_str()); + _innerRadiusSqr = _innerRaduis * _innerRaduis; + } else if (param.matchString("radius", true)) { + _outerRadius = atoi(values.c_str()); + _outerRadiusSqr = _outerRadius * _outerRadius; + } else if (param.matchString("zero_radians_offset", true)) { + _zeroPointer = atoi(values.c_str()); + } else if (param.matchString("pointer_offset", true)) { + _startPointer = atoi(values.c_str()); + _curState = _startPointer; + } else if (param.matchString("cursor", true)) { + // Not used + } else if (param.matchString("mirrored", true)) { + // Not used + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } + renderFrame(_curState); +} + +SafeControl::~SafeControl() { + if (_animation) + delete _animation; + +} + +void SafeControl::renderFrame(uint frameNumber) { + if (frameNumber == 0) { + _lastRenderedFrame = frameNumber; + } else if ((int16)frameNumber < _lastRenderedFrame) { + _lastRenderedFrame = frameNumber; + frameNumber = (_statesCount * 2) - frameNumber; + } else { + _lastRenderedFrame = frameNumber; + } + + const Graphics::Surface *frameData; + int x = _rectangle.left; + int y = _rectangle.top; + + frameData = _animation->getFrameData(frameNumber); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, x, y); +} + +bool SafeControl::process(uint32 deltaTimeInMillis) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_curFrame != _targetFrame) { + _frameTime -= deltaTimeInMillis; + + if (_frameTime <= 0) { + if (_curFrame < _targetFrame) { + _curFrame++; + renderFrame(_curFrame); + } else if (_curFrame > _targetFrame) { + _curFrame--; + renderFrame(_curFrame); + } + _frameTime = _animation->frameTime(); + } + } + return false; +} + +bool SafeControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_rectangle.contains(backgroundImageSpacePos)) { + int32 mR = backgroundImageSpacePos.sqrDist(_center); + if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) { + _engine->getCursorManager()->changeCursor(CursorIndex_Active); + return true; + } + } + return false; +} + +bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_rectangle.contains(backgroundImageSpacePos)) { + int32 mR = backgroundImageSpacePos.sqrDist(_center); + if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) { + setVenus(); + + Common::Point tmp = backgroundImageSpacePos - _center; + + float dd = atan2(tmp.x, tmp.y) * 57.29578; + + int16 dp_state = 360 / _statesCount; + + int16 m_state = (_statesCount - ((((int16)dd + 540) % 360) / dp_state)) % _statesCount; + + int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount; + + _curFrame = (_curState + _statesCount - _startPointer) % _statesCount; + + _curState = (_statesCount * 2 + tmp2) % _statesCount; + + _targetFrame = (_curState + _statesCount - _startPointer) % _statesCount; + + _engine->getScriptManager()->setStateValue(_key, _curState); + return true; + } + } + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/safe_control.h b/engines/zvision/scripting/controls/safe_control.h new file mode 100644 index 000000000000..e682e3505034 --- /dev/null +++ b/engines/zvision/scripting/controls/safe_control.h @@ -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. + * + */ + +#ifndef ZVISION_SAFE_CONTROL_H +#define ZVISION_SAFE_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "common/list.h" +#include "common/rect.h" + + +namespace ZVision { + +class ZorkAVIDecoder; +class MetaAnimation; + +class SafeControl : public Control { +public: + SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~SafeControl(); + +private: + int16 _statesCount; + int16 _curState; + MetaAnimation *_animation; + Common::Point _center; + Common::Rect _rectangle; + int16 _innerRaduis; + int32 _innerRadiusSqr; + int16 _outerRadius; + int32 _outerRadiusSqr; + int16 _zeroPointer; + int16 _startPointer; + int16 _curFrame; + int16 _targetFrame; + int32 _frameTime; + + int16 _lastRenderedFrame; + +public: + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + bool process(uint32 deltaTimeInMillis); + +private: + void renderFrame(uint frameNumber); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp new file mode 100644 index 000000000000..7e1a65a9cc10 --- /dev/null +++ b/engines/zvision/scripting/controls/save_control.cpp @@ -0,0 +1,123 @@ +/* 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 "zvision/scripting/controls/input_control.h" +#include "zvision/scripting/controls/save_control.h" +#include "zvision/utility/utility.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/text/string_manager.h" + +#include "zvision/core/save_manager.h" + +#include "common/str.h" +#include "common/stream.h" + + +namespace ZVision { + +SaveControl::SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_SAVE), + _saveControl(false) { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("savebox", true)) { + int saveId; + int inputId; + + sscanf(values.c_str(), "%d %d", &saveId, &inputId); + saveElement elmnt; + elmnt.inputKey = inputId; + elmnt.saveId = saveId; + elmnt.exist = false; + _inputs.push_back(elmnt); + } else if (param.matchString("control_type", true)) { + if (values.contains("save")) + _saveControl = true; + else + _saveControl = false; + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } + + for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) { + Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey); + if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) { + InputControl *inp = (InputControl *)ctrl; + inp->setReadOnly(!_saveControl); + Common::SeekableReadStream *save = _engine->getSaveManager()->getSlotFile(iter->saveId); + if (save) { + SaveGameHeader header; + _engine->getSaveManager()->readSaveGameHeader(save, header); + delete save; + inp->setText(header.saveName); + iter->exist = true; + } + } + } +} + +bool SaveControl::process(uint32 deltaTimeInMillis) { + for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) { + Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey); + if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) { + InputControl *inp = (InputControl *)ctrl; + if (inp->enterPress()) { + if (_saveControl) { + if (inp->getText().size() > 0) { + bool toSave = true; + if (iter->exist) + if (!_engine->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) + toSave = false; + + if (toSave) { + _engine->getSaveManager()->saveGameBuffered(iter->saveId, inp->getText()); + _engine->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); + _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation()); + } + } else { + _engine->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); + } + } else { + _engine->getSaveManager()->loadGame(iter->saveId); + return true; + } + break; + } + } + } + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/save_control.h b/engines/zvision/scripting/controls/save_control.h new file mode 100644 index 000000000000..fefb0e0ce2ed --- /dev/null +++ b/engines/zvision/scripting/controls/save_control.h @@ -0,0 +1,56 @@ +/* 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 ZVISION_SAVE_CONTROL_H +#define ZVISION_SAVE_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "common/list.h" + + +namespace ZVision { + +class SaveControl : public Control { +public: + SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + +private: + struct saveElement { + int saveId; + int inputKey; + bool exist; + }; + typedef Common::List saveElmntList; + saveElmntList _inputs; + + bool _saveControl; + +public: + + bool process(uint32 deltaTimeInMillis); + +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/slot_control.cpp b/engines/zvision/scripting/controls/slot_control.cpp new file mode 100644 index 000000000000..074d1905b423 --- /dev/null +++ b/engines/zvision/scripting/controls/slot_control.cpp @@ -0,0 +1,219 @@ +/* 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 "zvision/scripting/controls/slot_control.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/cursors/cursor_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" + + +namespace ZVision { + +SlotControl::SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_SLOT) { + + _renderedItem = 0; + _bkg = NULL; + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("hotspot", true)) { + int x; + int y; + int width; + int height; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height); + + _hotspot = Common::Rect(x, y, width, height); + } else if (param.matchString("rectangle", true)) { + int x; + int y; + int width; + int height; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height); + + _rectangle = Common::Rect(x, y, width, height); + } else if (param.matchString("cursor", true)) { + _cursor = _engine->getCursorManager()->getCursorId(values); + } else if (param.matchString("distance_id", true)) { + sscanf(values.c_str(), "%c", &_distanceId); + } else if (param.matchString("venus_id", true)) { + _venusId = atoi(values.c_str()); + } else if (param.matchString("eligible_objects", true)) { + char buf[256]; + memset(buf, 0, 256); + strcpy(buf, values.c_str()); + + char *curpos = buf; + char *strend = buf + strlen(buf); + while (true) { + char *st = curpos; + + if (st >= strend) + break; + + while (*curpos != ' ' && curpos < strend) + curpos++; + + *curpos = 0; + curpos++; + + int obj = atoi(st); + + _eligibleObjects.push_back(obj); + } + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } + + if (_hotspot.isEmpty() || _rectangle.isEmpty()) { + warning("Slot %u was parsed incorrectly", key); + } +} + +SlotControl::~SlotControl() { + // Clear the state value back to 0 + //_engine->getScriptManager()->setStateValue(_key, 0); + + if (_bkg) + delete _bkg; +} + +bool SlotControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_hotspot.contains(backgroundImageSpacePos)) { + setVenus(); + + int item = _engine->getScriptManager()->getStateValue(_key); + int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + if (item != 0) { + if (mouseItem != 0) { + if (eligeblity(mouseItem)) { + _engine->getScriptManager()->inventoryDrop(mouseItem); + _engine->getScriptManager()->inventoryAdd(item); + _engine->getScriptManager()->setStateValue(_key, mouseItem); + } + } else { + _engine->getScriptManager()->inventoryAdd(item); + _engine->getScriptManager()->setStateValue(_key, 0); + } + } else if (mouseItem == 0) { + if (eligeblity(0)) { + _engine->getScriptManager()->inventoryDrop(0); + _engine->getScriptManager()->setStateValue(_key, 0); + } + } else if (eligeblity(mouseItem)) { + _engine->getScriptManager()->setStateValue(_key, mouseItem); + _engine->getScriptManager()->inventoryDrop(mouseItem); + } + } + return false; +} + +bool SlotControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_hotspot.contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_cursor); + return true; + } + + return false; +} + +bool SlotControl::process(uint32 deltaTimeInMillis) { + if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) + return false; + + if (_engine->canRender()) { + int curItem = _engine->getScriptManager()->getStateValue(_key); + if (curItem != _renderedItem) { + if (_renderedItem != 0 && curItem == 0) { + _engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top); + _renderedItem = curItem; + } else { + if (_renderedItem == 0) { + if (_bkg) + delete _bkg; + + _bkg = _engine->getRenderManager()->getBkgRect(_rectangle); + } else { + _engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top); + } + + char buf[16]; + if (_engine->getGameId() == GID_NEMESIS) + sprintf(buf, "%d%cobj.tga", curItem, _distanceId); + else + sprintf(buf, "g0z%cu%2.2x1.tga", _distanceId, curItem); + + Graphics::Surface *srf = _engine->getRenderManager()->loadImage(buf); + + int16 drawx = _rectangle.left; + int16 drawy = _rectangle.top; + + if (_rectangle.width() > srf->w) + drawx = _rectangle.left + (_rectangle.width() - srf->w) / 2; + + if (_rectangle.height() > srf->h) + drawy = _rectangle.top + (_rectangle.height() - srf->h) / 2; + + _engine->getRenderManager()->blitSurfaceToBkg(*srf, drawx, drawy, 0); + + delete srf; + + _renderedItem = curItem; + } + } + } + return false; +} + +bool SlotControl::eligeblity(int itemId) { + for (Common::List::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++) + if (*it == itemId) + return true; + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/slot_control.h b/engines/zvision/scripting/controls/slot_control.h new file mode 100644 index 000000000000..86fd261f255b --- /dev/null +++ b/engines/zvision/scripting/controls/slot_control.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. + * + */ + +#ifndef ZVISION_SLOT_CONTROL_H +#define ZVISION_SLOT_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "graphics/surface.h" + +#include "common/rect.h" +#include "common/list.h" + + +namespace ZVision { + +class SlotControl : public Control { +public: + SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~SlotControl(); + + /** + * Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1); + * + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + */ + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + /** + * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor. + * + * @param engine The base engine + * @param screenSpacePos The position of the mouse in screen space + * @param backgroundImageSpacePos The position of the mouse in background image space + * @return Was the cursor changed? + */ + bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + + bool process(uint32 deltaTimeInMillis); + +private: + /** + * The area that will trigger the event + * This is in image space coordinates, NOT screen space + */ + Common::Rect _rectangle; + Common::Rect _hotspot; + + int _cursor; + char _distanceId; + + int _renderedItem; + + Common::List _eligibleObjects; + + bool eligeblity(int itemId); + + Graphics::Surface *_bkg; + + /** The cursor to use when hovering over _hotspot */ + Common::String _hoverCursor; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/titler_control.cpp b/engines/zvision/scripting/controls/titler_control.cpp new file mode 100644 index 000000000000..b803501033c1 --- /dev/null +++ b/engines/zvision/scripting/controls/titler_control.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 "common/scummsys.h" + +#include "zvision/scripting/controls/titler_control.h" + +#include "zvision/zvision.h" +#include "zvision/text/text.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" + + +namespace ZVision { + +TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key, CONTROL_TITLER) { + + _surface = NULL; + _curString = -1; + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + Common::String param; + Common::String values; + getParams(line, param, values); + + while (!stream.eos() && !line.contains('}')) { + if (param.matchString("string_resource_file", true)) { + readStringsFile(values); + } else if (param.matchString("rectangle", true)) { + int x; + int y; + int x2; + int y2; + + sscanf(values.c_str(), "%d %d %d %d", &x, &y, &x2, &y2); + + _rectangle = Common::Rect(x, y, x2, y2); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + getParams(line, param, values); + } + + if (!_rectangle.isEmpty()) { + _surface = new Graphics::Surface; + _surface->create(_rectangle.width(), _rectangle.height(), _engine->_pixelFormat); + _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); + } +} + +TitlerControl::~TitlerControl() { + if (_surface) + delete _surface; +} + +void TitlerControl::setString(int strLine) { + if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) { + _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); + _engine->getTextRenderer()->drawTxtInOneLine(_strings[strLine], *_surface); + _engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top); + _curString = strLine; + } +} + +void TitlerControl::readStringsFile(const Common::String &fileName) { + Common::File file; + if (!_engine->getSearchManager()->openFile(file, fileName)) { + warning("String_resource_file %s could could be opened", fileName.c_str()); + return; + } + + _strings.clear(); + + while (!file.eos()) { + + Common::String line = readWideLine(file); + _strings.push_back(line); + } + file.close(); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/titler_control.h b/engines/zvision/scripting/controls/titler_control.h new file mode 100644 index 000000000000..ee230afa979b --- /dev/null +++ b/engines/zvision/scripting/controls/titler_control.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 ZVISION_TITLER_CONTROL_H +#define ZVISION_TITLER_CONTROL_H + +#include "zvision/scripting/control.h" + +#include "graphics/surface.h" + +#include "common/rect.h" +#include "common/array.h" + + +namespace ZVision { + +class TitlerControl : public Control { +public: + TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~TitlerControl(); + + void setString(int strLine); + +private: + + Common::Array< Common::String > _strings; + Common::Rect _rectangle; + int16 _curString; + Graphics::Surface *_surface; + + void readStringsFile(const Common::String &fileName); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/inventory.cpp b/engines/zvision/scripting/inventory.cpp new file mode 100644 index 000000000000..98d063395bee --- /dev/null +++ b/engines/zvision/scripting/inventory.cpp @@ -0,0 +1,123 @@ +/* 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 "zvision/scripting/script_manager.h" + + +namespace ZVision { + +int8 ScriptManager::inventoryGetCount() { + return getStateValue(StateKey_Inv_Cnt_Slot); +} + +void ScriptManager::inventorySetCount(int8 cnt) { + setStateValue(StateKey_Inv_Cnt_Slot, cnt); +} + +int16 ScriptManager::inventoryGetItem(int8 id) { + if (id < 49 && id >= 0) + return getStateValue(StateKey_Inv_1_Slot + id); + return -1; +} + +void ScriptManager::inventorySetItem(int8 id, int16 item) { + if (id < 49 && id >= 0) + setStateValue(StateKey_Inv_1_Slot + id, item); +} + +void ScriptManager::inventoryAdd(int16 item) { + int8 cnt = inventoryGetCount(); + + if (cnt < 49) { + bool notExist = true; + + if (cnt == 0) { + inventorySetItem(0, 0); + inventorySetCount(1); // we needed empty item for cycle code + cnt = 1; + } + + for (int8 cur = 0; cur < cnt; cur++) + if (inventoryGetItem(cur) == item) { + notExist = false; + break; + } + + if (notExist) { + for (int8 i = cnt; i > 0; i--) + inventorySetItem(i, inventoryGetItem(i - 1)); + + inventorySetItem(0, item); + + setStateValue(StateKey_InventoryItem, item); + + inventorySetCount(cnt + 1); + } + } +} + +void ScriptManager::inventoryDrop(int16 item) { + int8 itemCount = inventoryGetCount(); + + // if items in inventory > 0 + if (itemCount != 0) { + int8 index = 0; + + // finding needed item + while (index < itemCount) { + if (inventoryGetItem(index) == item) + break; + + index++; + } + + // if item in the inventory + if (itemCount != index) { + // shift all items left with rewrite founded item + for (int8 v = index; v < itemCount - 1 ; v++) + inventorySetItem(v, inventoryGetItem(v + 1)); + + // del last item + inventorySetItem(itemCount - 1, 0); + inventorySetCount(inventoryGetCount() - 1); + + setStateValue(StateKey_InventoryItem, inventoryGetItem(0)); + } + } +} +void ScriptManager::inventoryCycle() { + int8 itemCount = inventoryGetCount(); + int8 curItem = inventoryGetItem(0); + if (itemCount > 1) { + for (int8 i = 0; i < itemCount - 1; i++) + inventorySetItem(i, inventoryGetItem(i + 1)); + + inventorySetItem(itemCount - 1, curItem); + + setStateValue(StateKey_InventoryItem, inventoryGetItem(0)); + + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/puzzle.h b/engines/zvision/scripting/puzzle.h index ee9ce521c90d..ab38c5fc5d1d 100644 --- a/engines/zvision/scripting/puzzle.h +++ b/engines/zvision/scripting/puzzle.h @@ -8,12 +8,12 @@ * 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. @@ -63,10 +63,17 @@ struct Puzzle { bool argumentIsAKey; }; + enum StateFlags { + ONCE_PER_INST = 0x01, + DISABLED = 0x02, + DO_ME_NOW = 0x04 + }; + uint32 key; Common::List > criteriaList; // This has to be list of pointers because ResultAction is abstract Common::List resultActions; + bool addedBySetState; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index 416bac00f345..697de58ed896 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -8,12 +8,12 @@ * 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. @@ -22,6 +22,7 @@ #include "common/scummsys.h" +#include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" #include "zvision/utility/utility.h" @@ -29,6 +30,14 @@ #include "zvision/scripting/actions.h" #include "zvision/scripting/controls/push_toggle_control.h" #include "zvision/scripting/controls/lever_control.h" +#include "zvision/scripting/controls/slot_control.h" +#include "zvision/scripting/controls/save_control.h" +#include "zvision/scripting/controls/input_control.h" +#include "zvision/scripting/controls/safe_control.h" +#include "zvision/scripting/controls/hotmov_control.h" +#include "zvision/scripting/controls/fist_control.h" +#include "zvision/scripting/controls/paint_control.h" +#include "zvision/scripting/controls/titler_control.h" #include "common/textconsole.h" #include "common/file.h" @@ -37,14 +46,14 @@ namespace ZVision { -void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) { +void ScriptManager::parseScrFile(const Common::String &fileName, ScriptScope &scope) { Common::File file; - if (!file.open(fileName)) { + if (!_engine->getSearchManager()->openFile(file, fileName)) { warning("Script file not found: %s", fileName.c_str()); return; } - while(!file.eos()) { + while (!file.eos()) { Common::String line = file.readLine(); if (file.err()) { warning("Error parsing scr file: %s", fileName.c_str()); @@ -57,18 +66,19 @@ void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) if (line.matchString("puzzle:*", true)) { Puzzle *puzzle = new Puzzle(); - sscanf(line.c_str(),"puzzle:%u",&(puzzle->key)); - + sscanf(line.c_str(), "puzzle:%u", &(puzzle->key)); + if (getStateFlag(puzzle->key) & Puzzle::ONCE_PER_INST) + setStateValue(puzzle->key, 0); parsePuzzle(puzzle, file); - if (isGlobal) { - _globalPuzzles.push_back(puzzle); - } else { - _activePuzzles.push_back(puzzle); - } + scope.puzzles.push_back(puzzle); + } else if (line.matchString("control:*", true)) { - parseControl(line, file); + Control *ctrl = parseControl(line, file); + if (ctrl) + scope.controls.push_back(ctrl); } } + scope.procCount = 0; } void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) { @@ -81,12 +91,14 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre } else if (line.matchString("results {", true)) { parseResults(stream, puzzle->resultActions); } else if (line.matchString("flags {", true)) { - setStateFlags(puzzle->key, parseFlags(stream)); + setStateFlag(puzzle->key, parseFlags(stream)); } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); } + + puzzle->addedBySetState = 0; } bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List > &criteriaList) const { @@ -148,103 +160,150 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + line.toLowercase(); // TODO: Re-order the if-then statements in order of highest occurrence while (!stream.eos() && !line.contains('}')) { if (line.empty()) { line = stream.readLine(); trimCommentsAndWhiteSpace(&line); - + line.toLowercase(); continue; } - // Parse for the action type - if (line.matchString("*:add*", true)) { - actionList.push_back(new ActionAdd(line)); - } else if (line.matchString("*:animplay*", true)) { - actionList.push_back(new ActionPlayAnimation(line)); - } else if (line.matchString("*:animpreload*", true)) { - actionList.push_back(new ActionPreloadAnimation(line)); - } else if (line.matchString("*:animunload*", true)) { - //actionList.push_back(new ActionUnloadAnimation(line)); - } else if (line.matchString("*:attenuate*", true)) { - // TODO: Implement ActionAttenuate - } else if (line.matchString("*:assign*", true)) { - actionList.push_back(new ActionAssign(line)); - } else if (line.matchString("*:change_location*", true)) { - actionList.push_back(new ActionChangeLocation(line)); - } else if (line.matchString("*:crossfade*", true)) { - // TODO: Implement ActionCrossfade - } else if (line.matchString("*:debug*", true)) { - // TODO: Implement ActionDebug - } else if (line.matchString("*:delay_render*", true)) { - // TODO: Implement ActionDelayRender - } else if (line.matchString("*:disable_control*", true)) { - actionList.push_back(new ActionDisableControl(line)); - } else if (line.matchString("*:disable_venus*", true)) { - // TODO: Implement ActionDisableVenus - } else if (line.matchString("*:display_message*", true)) { - // TODO: Implement ActionDisplayMessage - } else if (line.matchString("*:dissolve*", true)) { - // TODO: Implement ActionDissolve - } else if (line.matchString("*:distort*", true)) { - // TODO: Implement ActionDistort - } else if (line.matchString("*:enable_control*", true)) { - actionList.push_back(new ActionEnableControl(line)); - } else if (line.matchString("*:flush_mouse_events*", true)) { - // TODO: Implement ActionFlushMouseEvents - } else if (line.matchString("*:inventory*", true)) { - // TODO: Implement ActionInventory - } else if (line.matchString("*:kill*", true)) { - // TODO: Implement ActionKill - } else if (line.matchString("*:menu_bar_enable*", true)) { - // TODO: Implement ActionMenuBarEnable - } else if (line.matchString("*:music*", true)) { - actionList.push_back(new ActionMusic(line)); - } else if (line.matchString("*:pan_track*", true)) { - // TODO: Implement ActionPanTrack - } else if (line.matchString("*:playpreload*", true)) { - actionList.push_back(new ActionPlayPreloadAnimation(line)); - } else if (line.matchString("*:preferences*", true)) { - // TODO: Implement ActionPreferences - } else if (line.matchString("*:quit*", true)) { - actionList.push_back(new ActionQuit()); - } else if (line.matchString("*:random*", true)) { - actionList.push_back(new ActionRandom(line)); - } else if (line.matchString("*:region*", true)) { - // TODO: Implement ActionRegion - } else if (line.matchString("*:restore_game*", true)) { - // TODO: Implement ActionRestoreGame - } else if (line.matchString("*:rotate_to*", true)) { - // TODO: Implement ActionRotateTo - } else if (line.matchString("*:save_game*", true)) { - // TODO: Implement ActionSaveGame - } else if (line.matchString("*:set_partial_screen*", true)) { - actionList.push_back(new ActionSetPartialScreen(line)); - } else if (line.matchString("*:set_screen*", true)) { - actionList.push_back(new ActionSetScreen(line)); - } else if (line.matchString("*:set_venus*", true)) { - // TODO: Implement ActionSetVenus - } else if (line.matchString("*:stop*", true)) { - // TODO: Implement ActionStop - } else if (line.matchString("*:streamvideo*", true)) { - actionList.push_back(new ActionStreamVideo(line)); - } else if (line.matchString("*:syncsound*", true)) { - // TODO: Implement ActionSyncSound - } else if (line.matchString("*:timer*", true)) { - actionList.push_back(new ActionTimer(line)); - } else if (line.matchString("*:ttytext*", true)) { - // TODO: Implement ActionTTYText - } else if (line.matchString("*:universe_music*", true)) { - // TODO: Implement ActionUniverseMusic - } else if (line.matchString("*:copy_file*", true)) { - // Not used. Purposely left empty - } else { - warning("Unhandled result action type: %s", line.c_str()); + const char *chrs = line.c_str(); + uint pos; + for (pos = 0; pos < line.size(); pos++) + if (chrs[pos] == ':') + break; + + if (pos < line.size()) { + + uint startpos = pos + 1; + + for (pos = startpos; pos < line.size(); pos++) + if (chrs[pos] == ':' || chrs[pos] == '(') + break; + + if (pos < line.size()) { + int32 slot = 11; + Common::String args = ""; + Common::String act(chrs + startpos, chrs + pos); + + startpos = pos + 1; + + if (chrs[pos] == ':') { + for (pos = startpos; pos < line.size(); pos++) + if (chrs[pos] == '(') + break; + Common::String strSlot(chrs + startpos, chrs + pos); + slot = atoi(strSlot.c_str()); + + startpos = pos + 1; + } + + if (pos < line.size()) { + for (pos = startpos; pos < line.size(); pos++) + if (chrs[pos] == ')') + break; + + args = Common::String(chrs + startpos, chrs + pos); + } + + + + // Parse for the action type + if (act.matchString("add", true)) { + actionList.push_back(new ActionAdd(_engine, slot, args)); + } else if (act.matchString("animplay", true)) { + actionList.push_back(new ActionPlayAnimation(_engine, slot, args)); + } else if (act.matchString("animpreload", true)) { + actionList.push_back(new ActionPreloadAnimation(_engine, slot, args)); + } else if (act.matchString("animunload", true)) { + actionList.push_back(new ActionUnloadAnimation(_engine, slot, args)); + } else if (act.matchString("attenuate", true)) { + actionList.push_back(new ActionAttenuate(_engine, slot, args)); + } else if (act.matchString("assign", true)) { + actionList.push_back(new ActionAssign(_engine, slot, args)); + } else if (act.matchString("change_location", true)) { + actionList.push_back(new ActionChangeLocation(_engine, slot, args)); + } else if (act.matchString("crossfade", true)) { + actionList.push_back(new ActionCrossfade(_engine, slot, args)); + } else if (act.matchString("cursor", true)) { + actionList.push_back(new ActionCursor(_engine, slot, args)); + } else if (act.matchString("debug", true)) { + // Not used. Purposely left empty + } else if (act.matchString("delay_render", true)) { + actionList.push_back(new ActionDelayRender(_engine, slot, args)); + } else if (act.matchString("disable_control", true)) { + actionList.push_back(new ActionDisableControl(_engine, slot, args)); + } else if (act.matchString("disable_venus", true)) { + actionList.push_back(new ActionDisableVenus(_engine, slot, args)); + } else if (act.matchString("display_message", true)) { + actionList.push_back(new ActionDisplayMessage(_engine, slot, args)); + } else if (act.matchString("dissolve", true)) { + actionList.push_back(new ActionDissolve(_engine)); + } else if (act.matchString("distort", true)) { + actionList.push_back(new ActionDistort(_engine, slot, args)); + } else if (act.matchString("enable_control", true)) { + actionList.push_back(new ActionEnableControl(_engine, slot, args)); + } else if (act.matchString("flush_mouse_events", true)) { + actionList.push_back(new ActionFlushMouseEvents(_engine, slot)); + } else if (act.matchString("inventory", true)) { + actionList.push_back(new ActionInventory(_engine, slot, args)); + } else if (act.matchString("kill", true)) { + actionList.push_back(new ActionKill(_engine, slot, args)); + } else if (act.matchString("menu_bar_enable", true)) { + actionList.push_back(new ActionMenuBarEnable(_engine, slot, args)); + } else if (act.matchString("music", true)) { + actionList.push_back(new ActionMusic(_engine, slot, args, false)); + } else if (act.matchString("pan_track", true)) { + actionList.push_back(new ActionPanTrack(_engine, slot, args)); + } else if (act.matchString("playpreload", true)) { + actionList.push_back(new ActionPlayPreloadAnimation(_engine, slot, args)); + } else if (act.matchString("preferences", true)) { + actionList.push_back(new ActionPreferences(_engine, slot, args)); + } else if (act.matchString("quit", true)) { + actionList.push_back(new ActionQuit(_engine, slot)); + } else if (act.matchString("random", true)) { + actionList.push_back(new ActionRandom(_engine, slot, args)); + } else if (act.matchString("region", true)) { + actionList.push_back(new ActionRegion(_engine, slot, args)); + } else if (act.matchString("restore_game", true)) { + actionList.push_back(new ActionRestoreGame(_engine, slot, args)); + } else if (act.matchString("rotate_to", true)) { + actionList.push_back(new ActionRotateTo(_engine, slot, args)); + } else if (act.matchString("save_game", true)) { + // Not used. Purposely left empty + } else if (act.matchString("set_partial_screen", true)) { + actionList.push_back(new ActionSetPartialScreen(_engine, slot, args)); + } else if (act.matchString("set_screen", true)) { + actionList.push_back(new ActionSetScreen(_engine, slot, args)); + } else if (act.matchString("set_venus", true)) { + actionList.push_back(new ActionSetVenus(_engine, slot, args)); + } else if (act.matchString("stop", true)) { + actionList.push_back(new ActionStop(_engine, slot, args)); + } else if (act.matchString("streamvideo", true)) { + actionList.push_back(new ActionStreamVideo(_engine, slot, args)); + } else if (act.matchString("syncsound", true)) { + actionList.push_back(new ActionSyncSound(_engine, slot, args)); + } else if (act.matchString("timer", true)) { + actionList.push_back(new ActionTimer(_engine, slot, args)); + } else if (act.matchString("ttytext", true)) { + actionList.push_back(new ActionTtyText(_engine, slot, args)); + } else if (act.matchString("universe_music", true)) { + actionList.push_back(new ActionMusic(_engine, slot, args, true)); + } else if (act.matchString("copy_file", true)) { + // Not used. Purposely left empty + } else { + warning("Unhandled result action type: %s", line.c_str()); + } + } } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + line.toLowercase(); } return; @@ -259,11 +318,11 @@ uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const { while (!stream.eos() && !line.contains('}')) { if (line.matchString("ONCE_PER_INST", true)) { - flags |= ONCE_PER_INST; + flags |= Puzzle::ONCE_PER_INST; } else if (line.matchString("DO_ME_NOW", true)) { - flags |= DO_ME_NOW; + flags |= Puzzle::DO_ME_NOW; } else if (line.matchString("DISABLED", true)) { - flags |= DISABLED; + flags |= Puzzle::DISABLED; } line = stream.readLine(); @@ -273,7 +332,7 @@ uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const { return flags; } -void ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) { +Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) { uint32 key; char controlTypeBuffer[20]; @@ -282,21 +341,36 @@ void ScriptManager::parseControl(Common::String &line, Common::SeekableReadStrea Common::String controlType(controlTypeBuffer); if (controlType.equalsIgnoreCase("push_toggle")) { - _activeControls.push_back(new PushToggleControl(_engine, key, stream)); - return; + return new PushToggleControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("flat")) { Control::parseFlatControl(_engine); - return; + return NULL; } else if (controlType.equalsIgnoreCase("pana")) { Control::parsePanoramaControl(_engine, stream); - return; + return NULL; } else if (controlType.equalsIgnoreCase("tilt")) { Control::parseTiltControl(_engine, stream); - return; + return NULL; } else if (controlType.equalsIgnoreCase("lever")) { - _activeControls.push_back(new LeverControl(_engine, key, stream)); - return; + return new LeverControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("slot")) { + return new SlotControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("input")) { + return new InputControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("save")) { + return new SaveControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("safe")) { + return new SafeControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("hotmovie")) { + return new HotMovControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("fist")) { + return new FistControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("paint")) { + return new PaintControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("titler")) { + return new TitlerControl(_engine, key, stream); } + return NULL; } } // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index da823080512b..2a54cc431467 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -1,24 +1,24 @@ /* 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. - * - */ +* +* 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" @@ -30,6 +30,7 @@ #include "zvision/core/save_manager.h" #include "zvision/scripting/actions.h" #include "zvision/utility/utility.h" +#include "zvision/scripting/sidefx/timer_node.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -41,157 +42,221 @@ namespace ZVision { ScriptManager::ScriptManager(ZVision *engine) : _engine(engine), - _currentlyFocusedControl(0) { + _currentlyFocusedControl(0), + _activeControls(NULL) { } ScriptManager::~ScriptManager() { - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - delete (*iter); - } - for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { - delete (*iter); - } - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - delete (*iter); - } + cleanScriptScope(universe); + cleanScriptScope(world); + cleanScriptScope(room); + cleanScriptScope(nodeview); + _controlEvents.clear(); } void ScriptManager::initialize() { - parseScrFile("universe.scr", true); + cleanScriptScope(universe); + cleanScriptScope(world); + cleanScriptScope(room); + cleanScriptScope(nodeview); + + _currentLocation.node = 0; + _currentLocation.world = 0; + _currentLocation.room = 0; + _currentLocation.view = 0; + + parseScrFile("universe.scr", universe); changeLocation('g', 'a', 'r', 'y', 0); + + _controlEvents.clear(); } void ScriptManager::update(uint deltaTimeMillis) { + if (_currentLocation.node != _nextLocation.node || + _currentLocation.room != _nextLocation.room || + _currentLocation.view != _nextLocation.view || + _currentLocation.world != _nextLocation.world) + ChangeLocationReal(); + updateNodes(deltaTimeMillis); - checkPuzzleCriteria(); + if (! execScope(nodeview)) + return; + if (! execScope(room)) + return; + if (! execScope(world)) + return; + if (! execScope(universe)) + return; + updateControls(deltaTimeMillis); } -void ScriptManager::createReferenceTable() { - // Iterate through each local Puzzle - for (PuzzleList::iterator activePuzzleIter = _activePuzzles.begin(); activePuzzleIter != _activePuzzles.end(); ++activePuzzleIter) { - Puzzle *puzzlePtr = (*activePuzzleIter); - - // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List >::iterator criteriaIter = (*activePuzzleIter)->criteriaList.begin(); criteriaIter != (*activePuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - _referenceTable[entryIter->key].push_back(puzzlePtr); - - // If the argument is a key, add a reference to it as well - if (entryIter->argumentIsAKey) { - _referenceTable[entryIter->argument].push_back(puzzlePtr); - } - } - } +bool ScriptManager::execScope(ScriptScope &scope) { + // Swap queues + PuzzleList *tmp = scope.execQueue; + scope.execQueue = scope.scopeQueue; + scope.scopeQueue = tmp; + scope.scopeQueue->clear(); + + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) + (*PuzzleIter)->addedBySetState = 0; + + if (scope.procCount < 2 || getStateValue(StateKey_ExecScopeStyle)) { + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + return false; + } else { + for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + return false; } - // Iterate through each global Puzzle - for (PuzzleList::iterator globalPuzzleIter = _globalPuzzles.begin(); globalPuzzleIter != _globalPuzzles.end(); ++globalPuzzleIter) { - Puzzle *puzzlePtr = (*globalPuzzleIter); + if (scope.procCount < 2) { + scope.procCount++; + } + return true; +} - // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List >::iterator criteriaIter = (*globalPuzzleIter)->criteriaList.begin(); criteriaIter != (*globalPuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - _referenceTable[entryIter->key].push_back(puzzlePtr); - - // If the argument is a key, add a reference to it as well - if (entryIter->argumentIsAKey) { - _referenceTable[entryIter->argument].push_back(puzzlePtr); - } - } - } +void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) { + if (_referenceTable.contains(key)) { + Common::Array *arr = &_referenceTable[key]; + for (uint32 i = 0; i < arr->size(); i++) + if ((*arr)[i].puz == ref.puz) + return; } - // Remove duplicate entries - for (PuzzleMap::iterator referenceTableIter = _referenceTable.begin(); referenceTableIter != _referenceTable.end(); ++referenceTableIter) { - removeDuplicateEntries(referenceTableIter->_value); + _referenceTable[key].push_back(ref); +} + +void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) { + // Iterate through each local Puzzle + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) { + Puzzle *puzzlePtr = (*PuzzleIter); + + PuzzleRef ref; + ref.scope = &scope; + ref.puz = puzzlePtr; + + referenceTableAddPuzzle(puzzlePtr->key, ref); + + // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle + for (Common::List >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) + for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) + referenceTableAddPuzzle(entryIter->key, ref); } } void ScriptManager::updateNodes(uint deltaTimeMillis) { // If process() returns true, it means the node can be deleted - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end();) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) { if ((*iter)->process(deltaTimeMillis)) { - delete (*iter); + delete(*iter); // Remove the node - iter = _activeControls.erase(iter); + iter = _activeSideFx.erase(iter); } else { ++iter; } } } -void ScriptManager::checkPuzzleCriteria() { - while (!_puzzlesToCheck.empty()) { - Puzzle *puzzle = _puzzlesToCheck.pop(); - - // Check if the puzzle is already finished - // Also check that the puzzle isn't disabled - if (getStateValue(puzzle->key) == 1 && (getStateFlags(puzzle->key) & DISABLED) == 0) { - continue; +void ScriptManager::updateControls(uint deltaTimeMillis) { + if (!_activeControls) + return; + + // Process only one event + if (!_controlEvents.empty()) { + Common::Event _event = _controlEvents.front(); + Common::Point imageCoord; + switch (_event.type) { + case Common::EVENT_LBUTTONDOWN: + imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse); + onMouseDown(_event.mouse, imageCoord); + break; + case Common::EVENT_LBUTTONUP: + imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse); + onMouseUp(_event.mouse, imageCoord); + break; + case Common::EVENT_KEYDOWN: + onKeyDown(_event.kbd); + break; + case Common::EVENT_KEYUP: + onKeyUp(_event.kbd); + break; + default: + break; } + _controlEvents.pop_front(); + } + + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) + if ((*iter)->process(deltaTimeMillis)) + break; +} - // Check each Criteria - - bool criteriaMet = false; - for (Common::List >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { - criteriaMet = false; - - for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { - // Get the value to compare against - uint argumentValue; - if (entryIter->argumentIsAKey) - argumentValue = getStateValue(entryIter->argument); - else - argumentValue = entryIter->argument; - - // Do the comparison - switch (entryIter->criteriaOperator) { - case Puzzle::EQUAL_TO: - criteriaMet = getStateValue(entryIter->key) == argumentValue; - break; - case Puzzle::NOT_EQUAL_TO: - criteriaMet = getStateValue(entryIter->key) != argumentValue; - break; - case Puzzle::GREATER_THAN: - criteriaMet = getStateValue(entryIter->key) > argumentValue; - break; - case Puzzle::LESS_THAN: - criteriaMet = getStateValue(entryIter->key) < argumentValue; - break; - } - - // If one check returns false, don't keep checking - if (!criteriaMet) { - break; - } +bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { + // Check if the puzzle is already finished + // Also check that the puzzle isn't disabled + if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED) == Puzzle::DISABLED) { + return true; + } + + // Check each Criteria + if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) + return true; + + bool criteriaMet = false; + for (Common::List >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { + criteriaMet = false; + + for (Common::List::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { + // Get the value to compare against + int argumentValue; + if (entryIter->argumentIsAKey) + argumentValue = getStateValue(entryIter->argument); + else + argumentValue = entryIter->argument; + + // Do the comparison + switch (entryIter->criteriaOperator) { + case Puzzle::EQUAL_TO: + criteriaMet = getStateValue(entryIter->key) == argumentValue; + break; + case Puzzle::NOT_EQUAL_TO: + criteriaMet = getStateValue(entryIter->key) != argumentValue; + break; + case Puzzle::GREATER_THAN: + criteriaMet = getStateValue(entryIter->key) > argumentValue; + break; + case Puzzle::LESS_THAN: + criteriaMet = getStateValue(entryIter->key) < argumentValue; + break; } - // If any of the Criteria are *fully* met, then execute the results - if (criteriaMet) { + // If one check returns false, don't keep checking + if (!criteriaMet) { break; } } - // criteriaList can be empty. Aka, the puzzle should be executed immediately - if (puzzle->criteriaList.empty() || criteriaMet) { - debug(1, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key); + // If any of the Criteria are *fully* met, then execute the results + if (criteriaMet) { + break; + } + } - // Set the puzzle as completed - setStateValue(puzzle->key, 1); + // criteriaList can be empty. Aka, the puzzle should be executed immediately + if (puzzle->criteriaList.empty() || criteriaMet) { + debug(1, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key); - bool shouldContinue = true; - for (Common::List::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { - shouldContinue = shouldContinue && (*resultIter)->execute(_engine); - if (!shouldContinue) { - break; - } - } + // Set the puzzle as completed + setStateValue(puzzle->key, 1); - if (!shouldContinue) { - break; - } + for (Common::List::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { + if (!(*resultIter)->execute()) + return false; } } + + return true; } void ScriptManager::cleanStateTable() { @@ -205,60 +270,102 @@ void ScriptManager::cleanStateTable() { } } -uint ScriptManager::getStateValue(uint32 key) { +void ScriptManager::cleanScriptScope(ScriptScope &scope) { + scope.privQueueOne.clear(); + scope.privQueueTwo.clear(); + scope.scopeQueue = &scope.privQueueOne; + scope.execQueue = &scope.privQueueTwo; + for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) + delete(*iter); + + scope.puzzles.clear(); + + for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) + delete(*iter); + + scope.controls.clear(); + + scope.procCount = 0; +} + +int ScriptManager::getStateValue(uint32 key) { if (_globalState.contains(key)) return _globalState[key]; else return 0; } -void ScriptManager::setStateValue(uint32 key, uint value) { - _globalState[key] = value; - +void ScriptManager::queuePuzzles(uint32 key) { if (_referenceTable.contains(key)) { - for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { - _puzzlesToCheck.push((*iter)); - } + Common::Array *arr = &_referenceTable[key]; + for (int32 i = arr->size() - 1; i >= 0; i--) + if (!(*arr)[i].puz->addedBySetState) { + (*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz); + (*arr)[i].puz->addedBySetState = true; + } } } -uint ScriptManager::getStateFlags(uint32 key) { +void ScriptManager::setStateValue(uint32 key, int value) { + if (value == 0) + _globalState.erase(key); + else + _globalState[key] = value; + + queuePuzzles(key); +} + +void ScriptManager::setStateValueSilent(uint32 key, int value) { + if (value == 0) + _globalState.erase(key); + else + _globalState[key] = value; +} + +uint ScriptManager::getStateFlag(uint32 key) { if (_globalStateFlags.contains(key)) return _globalStateFlags[key]; else return 0; } -void ScriptManager::setStateFlags(uint32 key, uint flags) { - _globalStateFlags[key] = flags; +void ScriptManager::setStateFlag(uint32 key, uint value) { + queuePuzzles(key); - if (_referenceTable.contains(key)) { - for (Common::Array::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { - _puzzlesToCheck.push((*iter)); - } - } + _globalStateFlags[key] |= value; } -void ScriptManager::addToStateValue(uint32 key, uint valueToAdd) { - _globalState[key] += valueToAdd; +void ScriptManager::setStateFlagSilent(uint32 key, uint value) { + if (value == 0) + _globalStateFlags.erase(key); + else + _globalStateFlags[key] = value; } -void ScriptManager::addControl(Control *control) { - _activeControls.push_back(control); -} +void ScriptManager::unsetStateFlag(uint32 key, uint value) { + queuePuzzles(key); -Control *ScriptManager::getControl(uint32 key) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->getKey() == key) { - return (*iter); - } + if (_globalStateFlags.contains(key)) { + _globalStateFlags[key] &= ~value; + + if (_globalStateFlags[key] == 0) + _globalStateFlags.erase(key); } +} +Control *ScriptManager::getControl(uint32 key) { + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) + if ((*iter)->getKey() == key) + return *iter; return nullptr; } void ScriptManager::focusControl(uint32 key) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + if (!_activeControls) + return; + if (_currentlyFocusedControl == key) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { uint32 controlKey = (*iter)->getKey(); if (controlKey == key) { @@ -271,167 +378,372 @@ void ScriptManager::focusControl(uint32 key) { _currentlyFocusedControl = key; } +void ScriptManager::setFocusControlKey(uint32 key) { + _currentlyFocusedControl = key; +} + +void ScriptManager::addSideFX(SideFX *fx) { + _activeSideFx.push_back(fx); +} + +SideFX *ScriptManager::getSideFX(uint32 key) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { + if ((*iter)->getKey() == key) { + return (*iter); + } + } + + return nullptr; +} + +void ScriptManager::deleteSideFx(uint32 key) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { + if ((*iter)->getKey() == key) { + delete(*iter); + _activeSideFx.erase(iter); + break; + } + } +} + +void ScriptManager::stopSideFx(uint32 key) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { + if ((*iter)->getKey() == key) { + bool ret = (*iter)->stop(); + if (ret) { + delete(*iter); + _activeSideFx.erase(iter); + } + break; + } + } +} + +void ScriptManager::killSideFx(uint32 key) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { + if ((*iter)->getKey() == key) { + (*iter)->kill(); + delete(*iter); + _activeSideFx.erase(iter); + break; + } + } +} + +void ScriptManager::killSideFxType(SideFX::SideFXType type) { + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) { + if ((*iter)->getType() & type) { + (*iter)->kill(); + delete(*iter); + iter = _activeSideFx.erase(iter); + } else { + ++iter; + } + } +} + void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos); + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { + if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) + return; } } void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos); + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { + if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) + return; } } bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - bool cursorWasChanged = false; - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - cursorWasChanged = cursorWasChanged || (*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos); + if (!_activeControls) + return false; + + for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { + if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) + return true; } - return cursorWasChanged; + return false; } void ScriptManager::onKeyDown(Common::KeyState keyState) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onKeyDown(keyState); + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { + if ((*iter)->onKeyDown(keyState)) + return; } } void ScriptManager::onKeyUp(Common::KeyState keyState) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->onKeyUp(keyState); + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { + if ((*iter)->onKeyUp(keyState)) + return; } } -void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) { - assert(world != 0); - debug(1, "Changing location to: %c %c %c %c %u", world, room, node, view, offset); +void ScriptManager::changeLocation(const Location &_newLocation) { + changeLocation(_newLocation.world, _newLocation.room, _newLocation.node, _newLocation.view, _newLocation.offset); +} - // Auto save - _engine->getSaveManager()->autoSave(); +void ScriptManager::changeLocation(char _world, char _room, char _node, char _view, uint32 offset) { + _nextLocation.world = _world; + _nextLocation.room = _room; + _nextLocation.node = _node; + _nextLocation.view = _view; + _nextLocation.offset = offset; + // If next location 0000 - it's indicate to go to previous location. + if (_nextLocation.world == '0' && _nextLocation.room == '0' && _nextLocation.node == '0' && _nextLocation.view == '0') { + if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { + _nextLocation.world = getStateValue(StateKey_LastWorld); + _nextLocation.room = getStateValue(StateKey_LastRoom); + _nextLocation.node = getStateValue(StateKey_LastNode); + _nextLocation.view = getStateValue(StateKey_LastView); + _nextLocation.offset = getStateValue(StateKey_LastViewPos); + } else { + _nextLocation.world = getStateValue(StateKey_Menu_LastWorld); + _nextLocation.room = getStateValue(StateKey_Menu_LastRoom); + _nextLocation.node = getStateValue(StateKey_Menu_LastNode); + _nextLocation.view = getStateValue(StateKey_Menu_LastView); + _nextLocation.offset = getStateValue(StateKey_Menu_LastViewPos); + } + } +} - // Clear all the containers - _referenceTable.clear(); - _puzzlesToCheck.clear(); - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - delete (*iter); +void ScriptManager::ChangeLocationReal() { + assert(_nextLocation.world != 0); + debug(1, "Changing location to: %c %c %c %c %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset); + + _engine->setRenderDelay(2); + + if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { + if (_nextLocation.world != 'g' || _nextLocation.room != 'j') { + setStateValue(StateKey_LastWorld, getStateValue(StateKey_World)); + setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room)); + setStateValue(StateKey_LastNode, getStateValue(StateKey_Node)); + setStateValue(StateKey_LastView, getStateValue(StateKey_View)); + setStateValue(StateKey_LastViewPos, getStateValue(StateKey_ViewPos)); + } else { + setStateValue(StateKey_Menu_LastWorld, getStateValue(StateKey_World)); + setStateValue(StateKey_Menu_LastRoom, getStateValue(StateKey_Room)); + setStateValue(StateKey_Menu_LastNode, getStateValue(StateKey_Node)); + setStateValue(StateKey_Menu_LastView, getStateValue(StateKey_View)); + setStateValue(StateKey_Menu_LastViewPos, getStateValue(StateKey_ViewPos)); + } } - _activePuzzles.clear(); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - delete (*iter); + + if (_nextLocation.world == 'g' && _nextLocation.room == 'j') { + if (_nextLocation.node == 's' && _nextLocation.view == 'e' && + _currentLocation.world != 'g' && _currentLocation.room != 'j') + _engine->getSaveManager()->prepareSaveBuffer(); + } else { + if (_currentLocation.world == 'g' && _currentLocation.room == 'j') + _engine->getSaveManager()->flushSaveBuffer(); + else { + // Auto save + //_engine->getSaveManager()->autoSave(); + } } - _activeControls.clear(); - // Revert to the idle cursor - _engine->getCursorManager()->revertToIdle(); + setStateValue(StateKey_World, _nextLocation.world); + setStateValue(StateKey_Room, _nextLocation.room); + setStateValue(StateKey_Node, _nextLocation.node); + setStateValue(StateKey_View, _nextLocation.view); + setStateValue(StateKey_ViewPos, _nextLocation.offset); - // Reset the background velocity - _engine->getRenderManager()->setBackgroundVelocity(0); + _referenceTable.clear(); + addPuzzlesToReferenceTable(universe); - // Remove any alphaEntries - _engine->getRenderManager()->clearAlphaEntries(); + _engine->menuBarEnable(0xFFFF); - // Clean the global state table - cleanStateTable(); + if (_nextLocation.world != _currentLocation.world) { + cleanScriptScope(nodeview); + cleanScriptScope(room); + cleanScriptScope(world); - // Parse into puzzles and controls - Common::String fileName = Common::String::format("%c%c%c%c.scr", world, room, node, view); - parseScrFile(fileName); + Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view); + parseScrFile(fileName, nodeview); + addPuzzlesToReferenceTable(nodeview); - // Change the background position - _engine->getRenderManager()->setBackgroundPosition(offset); + fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room); + parseScrFile(fileName, room); + addPuzzlesToReferenceTable(room); - // Enable all the controls - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->enable(); - } + fileName = Common::String::format("%c.scr", _nextLocation.world); + parseScrFile(fileName, world); + addPuzzlesToReferenceTable(world); + } else if (_nextLocation.room != _currentLocation.room) { + cleanScriptScope(nodeview); + cleanScriptScope(room); - // Add all the local puzzles to the queue to be checked - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - // Reset any Puzzles that have the flag ONCE_PER_INST - if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { - setStateValue((*iter)->key, 0); - } + addPuzzlesToReferenceTable(world); - _puzzlesToCheck.push((*iter)); - } + Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view); + parseScrFile(fileName, nodeview); + addPuzzlesToReferenceTable(nodeview); - // Add all the global puzzles to the queue to be checked - for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) { - // Reset any Puzzles that have the flag ONCE_PER_INST - if ((getStateFlags((*iter)->key) & ONCE_PER_INST) == ONCE_PER_INST) { - setStateValue((*iter)->key, 0); - } + fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room); + parseScrFile(fileName, room); + addPuzzlesToReferenceTable(room); + + } else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) { + cleanScriptScope(nodeview); - _puzzlesToCheck.push((*iter)); + addPuzzlesToReferenceTable(room); + addPuzzlesToReferenceTable(world); + + Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view); + parseScrFile(fileName, nodeview); + addPuzzlesToReferenceTable(nodeview); } - // Create the puzzle reference table - createReferenceTable(); + _activeControls = &nodeview.controls; + + // Revert to the idle cursor + _engine->getCursorManager()->changeCursor(CursorIndex_Idle); - // Update _currentLocation - _currentLocation.world = world; - _currentLocation.room = room; - _currentLocation.node = node; - _currentLocation.view = view; - _currentLocation.offset = offset; + // Change the background position + _engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset); + + if (_currentLocation.world == 0 && _currentLocation.room == 0 && _currentLocation.node == 0 && _currentLocation.view == 0) { + _currentLocation = _nextLocation; + execScope(world); + execScope(room); + execScope(nodeview); + } else if (_nextLocation.world != _currentLocation.world) { + _currentLocation = _nextLocation; + execScope(room); + execScope(nodeview); + } else if (_nextLocation.room != _currentLocation.room) { + _currentLocation = _nextLocation; + execScope(room); + execScope(nodeview); + } else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) { + _currentLocation = _nextLocation; + execScope(nodeview); + } + + _engine->checkBorders(); } -void ScriptManager::serializeStateTable(Common::WriteStream *stream) { - // Write the number of state value entries - stream->writeUint32LE(_globalState.size()); +void ScriptManager::serialize(Common::WriteStream *stream) { + stream->writeUint32BE(MKTAG('Z', 'N', 'S', 'G')); + stream->writeUint32LE(4); + stream->writeUint32LE(0); + stream->writeUint32BE(MKTAG('L', 'O', 'C', ' ')); + stream->writeUint32LE(8); + stream->writeByte(getStateValue(StateKey_World)); + stream->writeByte(getStateValue(StateKey_Room)); + stream->writeByte(getStateValue(StateKey_Node)); + stream->writeByte(getStateValue(StateKey_View)); + stream->writeUint32LE(getStateValue(StateKey_ViewPos)); + + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) + (*iter)->serialize(stream); - for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { - // Write out the key/value pair - stream->writeUint32LE(iter->_key); - stream->writeUint32LE(iter->_value); - } + stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G')); + + int32 slots = 20000; + if (_engine->getGameId() == GID_NEMESIS) + slots = 30000; + + stream->writeUint32LE(slots * 2); + + for (int32 i = 0; i < slots; i++) + stream->writeUint16LE(getStateFlag(i)); + + stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z')); + + stream->writeUint32LE(slots * 2); + + for (int32 i = 0; i < slots; i++) + stream->writeSint16LE(getStateValue(i)); } -void ScriptManager::deserializeStateTable(Common::SeekableReadStream *stream) { +void ScriptManager::deserialize(Common::SeekableReadStream *stream) { // Clear out the current table values _globalState.clear(); + _globalStateFlags.clear(); - // Read the number of key/value pairs - uint32 numberOfPairs = stream->readUint32LE(); + cleanScriptScope(nodeview); + cleanScriptScope(room); + cleanScriptScope(world); - for (uint32 i = 0; i < numberOfPairs; ++i) { - uint32 key = stream->readUint32LE(); - uint32 value = stream->readUint32LE(); - // Directly access the state table so we don't trigger Puzzle checks - _globalState[key] = value; - } -} + _currentLocation.node = 0; + _currentLocation.world = 0; + _currentLocation.room = 0; + _currentLocation.view = 0; -void ScriptManager::serializeControls(Common::WriteStream *stream) { - // Count how many controls need to save their data - // Because WriteStream isn't seekable - uint32 numberOfControlsNeedingSerialization = 0; - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->needsSerialization()) { - numberOfControlsNeedingSerialization++; - } - } - stream->writeUint32LE(numberOfControlsNeedingSerialization); + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) + delete(*iter); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->serialize(stream); + _activeSideFx.clear(); + + _referenceTable.clear(); + + if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) { + changeLocation('g', 'a', 'r', 'y', 0); + return; } -} -void ScriptManager::deserializeControls(Common::SeekableReadStream *stream) { - uint32 numberOfControls = stream->readUint32LE(); + stream->seek(4, SEEK_CUR); - for (uint32 i = 0; i < numberOfControls; ++i) { - uint32 key = stream->readUint32LE(); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - if ((*iter)->getKey() == key) { - (*iter)->deserialize(stream); - break; - } + if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) { + changeLocation('g', 'a', 'r', 'y', 0); + return; + } + + Location nextLocation; + + nextLocation.world = stream->readByte(); + nextLocation.room = stream->readByte(); + nextLocation.node = stream->readByte(); + nextLocation.view = stream->readByte(); + nextLocation.offset = stream->readUint32LE() & 0x0000FFFF; + + while (stream->pos() < stream->size()) { + uint32 tag = stream->readUint32BE(); + uint32 tagSize = stream->readUint32LE(); + switch (tag) { + case MKTAG('T', 'I', 'M', 'R'): { + uint32 key = stream->readUint32LE(); + uint32 time = stream->readUint32LE(); + if (_engine->getGameId() == GID_GRANDINQUISITOR) + time /= 100; + else if (_engine->getGameId() == GID_NEMESIS) + time /= 1000; + addSideFX(new TimerNode(_engine, key, time)); + } + break; + case MKTAG('F', 'L', 'A', 'G'): + for (uint32 i = 0; i < tagSize / 2; i++) + setStateFlagSilent(i, stream->readUint16LE()); + break; + case MKTAG('P', 'U', 'Z', 'Z'): + for (uint32 i = 0; i < tagSize / 2; i++) + setStateValueSilent(i, stream->readUint16LE()); + break; + default: + stream->seek(tagSize, SEEK_CUR); } } + + _nextLocation = nextLocation; + + ChangeLocationReal(); + + _engine->setRenderDelay(10); + setStateValue(StateKey_RestoreFlag, 1); + + _engine->loadSettings(); } Location ScriptManager::getCurrentLocation() const { @@ -441,4 +753,64 @@ Location ScriptManager::getCurrentLocation() const { return location; } +Location ScriptManager::getLastLocation() { + Location location; + location.world = getStateValue(StateKey_LastWorld); + location.room = getStateValue(StateKey_LastRoom); + location.node = getStateValue(StateKey_LastNode); + location.view = getStateValue(StateKey_LastView); + location.offset = getStateValue(StateKey_LastViewPos); + + return location; +} + +Location ScriptManager::getLastMenuLocation() { + Location location; + location.world = getStateValue(StateKey_Menu_LastWorld); + location.room = getStateValue(StateKey_Menu_LastRoom); + location.node = getStateValue(StateKey_Menu_LastNode); + location.view = getStateValue(StateKey_Menu_LastView); + location.offset = getStateValue(StateKey_Menu_LastViewPos); + + return location; +} + +void ScriptManager::addEvent(Common::Event event) { + _controlEvents.push_back(event); +} + +void ScriptManager::flushEvent(Common::EventType type) { + EventList::iterator it = _controlEvents.begin(); + while (it != _controlEvents.end()) { + + if ((*it).type == type) + it = _controlEvents.erase(it); + else + it++; + } +} + +ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue): + _scriptManager(scriptManager) { + value = 0; + slot = false; + const char *isSlot = strstr(slotValue, "["); + if (isSlot) { + slot = true; + value = atoi(isSlot + 1); + } else { + slot = false; + value = atoi(slotValue); + } +} +int16 ValueSlot::getValue() { + if (slot) { + if (value >= 0) + return _scriptManager->getStateValue(value); + else + return 0; + } else + return value; +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h index 4d1b1f359b5f..5701cde6d0d8 100644 --- a/engines/zvision/scripting/script_manager.h +++ b/engines/zvision/scripting/script_manager.h @@ -8,12 +8,12 @@ * 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. @@ -25,9 +25,11 @@ #include "zvision/scripting/puzzle.h" #include "zvision/scripting/control.h" +#include "zvision/scripting/sidefx.h" #include "common/hashmap.h" #include "common/queue.h" +#include "common/events.h" namespace Common { @@ -39,6 +41,63 @@ namespace ZVision { class ZVision; +enum StateKey { + StateKey_World = 3, + StateKey_Room = 4, + StateKey_Node = 5, + StateKey_View = 6, + StateKey_ViewPos = 7, + StateKey_KeyPress = 8, + StateKey_InventoryItem = 9, + StateKey_LMouse = 10, + StateKey_NotSet = 11, // This key doesn't set + StateKey_Rounds = 12, + StateKey_Venus = 13, + StateKey_RMouse = 18, + StateKey_MenuState = 19, + StateKey_RestoreFlag = 20, + StateKey_Quitting = 39, + StateKey_LastWorld = 40, + StateKey_LastRoom = 41, + StateKey_LastNode = 42, + StateKey_LastView = 43, + StateKey_LastViewPos = 44, + StateKey_Menu_LastWorld = 45, + StateKey_Menu_LastRoom = 46, + StateKey_Menu_LastNode = 47, + StateKey_Menu_LastView = 48, + StateKey_Menu_LastViewPos = 49, + StateKey_KbdRotateSpeed = 50, + StateKey_Subtitles = 51, + StateKey_StreamSkipKey = 52, + StateKey_RotateSpeed = 53, + StateKey_Volume = 56, + StateKey_Qsound = 57, + StateKey_VenusEnable = 58, + StateKey_HighQuality = 59, + StateKey_VideoLineSkip = 65, + StateKey_Platform = 66, + StateKey_InstallLevel = 67, + StateKey_CountryCode = 68, + StateKey_CPU = 69, + StateKey_MovieCursor = 70, + StateKey_NoTurnAnim = 71, + StateKey_WIN958 = 72, + StateKey_ShowErrorDlg = 73, + StateKey_DebugCheats = 74, + StateKey_JapanFonts = 75, + StateKey_ExecScopeStyle = 76, + StateKey_Brightness = 77, + StateKey_EF9_R = 91, + StateKey_EF9_G = 92, + StateKey_EF9_B = 93, + StateKey_EF9_Speed = 94, + StateKey_Inv_Cnt_Slot = 100, + StateKey_Inv_1_Slot = 101, + StateKey_Inv_49_Slot = 149, + StateKey_Inv_TotalSlots = 150 +}; + struct Location { Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {} @@ -49,68 +108,99 @@ struct Location { uint32 offset; }; -typedef Common::HashMap > PuzzleMap; typedef Common::List PuzzleList; typedef Common::Queue PuzzleQueue; typedef Common::List ControlList; -typedef Common::HashMap StateMap; -typedef Common::HashMap StateFlagMap; +typedef Common::HashMap StateMap; +typedef Common::List SideFXList; +typedef Common::List EventList; class ScriptManager { public: ScriptManager(ZVision *engine); ~ScriptManager(); -public: - enum StateFlags { - ONCE_PER_INST = 0x01, - DO_ME_NOW = 0x02, // Somewhat useless flag since anything that needs to be done immediately has no criteria - DISABLED = 0x04 - }; - private: ZVision *_engine; + + struct ScriptScope { + uint32 procCount; + + PuzzleList *scopeQueue; // For adding puzzles to queue + PuzzleList *execQueue; // Switch to it when execute + PuzzleList privQueueOne; + PuzzleList privQueueTwo; + + PuzzleList puzzles; + ControlList controls; + }; + + struct PuzzleRef { + Puzzle *puz; + ScriptScope *scope; + }; + + typedef Common::HashMap > PuzzleMap; + /** * Holds the global state variable. Do NOT directly modify this. Use the accessors and * mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a * particular state key are checked after the key is modified. */ StateMap _globalState; - /** - * Holds the flags for the global states. This is used to enable/disable puzzles and/or - * controls as well as which puzzles should are allowed to be re-executed - */ - StateFlagMap _globalStateFlags; + /** Holds execute flags */ + StateMap _globalStateFlags; /** References _globalState keys to Puzzles */ PuzzleMap _referenceTable; - /** Holds the Puzzles that should be checked this frame */ - PuzzleQueue _puzzlesToCheck; - /** Holds the currently active puzzles */ - PuzzleList _activePuzzles; - /** Holds the global puzzles */ - PuzzleList _globalPuzzles; /** Holds the currently active controls */ - ControlList _activeControls; + ControlList *_activeControls; + + EventList _controlEvents; + + ScriptScope universe; + ScriptScope world; + ScriptScope room; + ScriptScope nodeview; + + /** Holds the currently active timers, musics, other */ + SideFXList _activeSideFx; Location _currentLocation; + Location _nextLocation; uint32 _currentlyFocusedControl; public: void initialize(); void update(uint deltaTimeMillis); + void queuePuzzles(uint32 key); - uint getStateValue(uint32 key); - void setStateValue(uint32 key, uint value); - void addToStateValue(uint32 key, uint valueToAdd); + int getStateValue(uint32 key); + void setStateValue(uint32 key, int value); - uint getStateFlags(uint32 key); - void setStateFlags(uint32 key, uint flags); + uint getStateFlag(uint32 key); + void setStateFlag(uint32 key, uint value); + void unsetStateFlag(uint32 key, uint value); void addControl(Control *control); Control *getControl(uint32 key); + void enableControl(uint32 key); + void disableControl(uint32 key); + void focusControl(uint32 key); + // Only change focus control without call focus/unfocus. + void setFocusControlKey(uint32 key); + + void addSideFX(SideFX *fx); + SideFX *getSideFX(uint32 key); + void deleteSideFx(uint32 key); + void stopSideFx(uint32 key); + void killSideFx(uint32 key); + void killSideFxType(SideFX::SideFXType type); + + void addEvent(Common::Event); + void flushEvent(Common::EventType type); /** * Called when LeftMouse is pushed. @@ -147,30 +237,51 @@ class ScriptManager { */ void onKeyUp(Common::KeyState keyState); + /** Mark next location */ void changeLocation(char world, char room, char node, char view, uint32 offset); + void changeLocation(const Location &_newLocation); - void serializeStateTable(Common::WriteStream *stream); - void deserializeStateTable(Common::SeekableReadStream *stream); - void serializeControls(Common::WriteStream *stream); - void deserializeControls(Common::SeekableReadStream *stream); + void serialize(Common::WriteStream *stream); + void deserialize(Common::SeekableReadStream *stream); Location getCurrentLocation() const; + Location getLastLocation(); + Location getLastMenuLocation(); private: - void createReferenceTable(); + void referenceTableAddPuzzle(uint32 key, PuzzleRef ref); + void addPuzzlesToReferenceTable(ScriptScope &scope); void updateNodes(uint deltaTimeMillis); - void checkPuzzleCriteria(); + void updateControls(uint deltaTimeMillis); + bool checkPuzzleCriteria(Puzzle *puzzle, uint counter); void cleanStateTable(); + void cleanScriptScope(ScriptScope &scope); + bool execScope(ScriptScope &scope); + + /** Perform change location */ + void ChangeLocationReal(); + + int8 inventoryGetCount(); + void inventorySetCount(int8 cnt); + int16 inventoryGetItem(int8 id); + void inventorySetItem(int8 id, int16 item); + + void setStateFlagSilent(uint32 key, uint value); + void setStateValueSilent(uint32 key, int value); -// TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it public: + void inventoryAdd(int16 item); + void inventoryDrop(int16 item); + void inventoryCycle(); + + // TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it /** * Parses a script file into triggers and events * * @param fileName Name of the .scr file * @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes */ - void parseScrFile(const Common::String &fileName, bool isGlobal = false); + void parseScrFile(const Common::String &fileName, ScriptScope &scope); private: /** @@ -216,7 +327,17 @@ class ScriptManager { * @param line The line initially read * @param stream Scr file stream */ - void parseControl(Common::String &line, Common::SeekableReadStream &stream); + Control *parseControl(Common::String &line, Common::SeekableReadStream &stream); +}; + +class ValueSlot { +public: + ValueSlot(ScriptManager *scriptManager, const char *slotValue); + int16 getValue(); +private: + int16 value; + bool slot; + ScriptManager *_scriptManager; }; diff --git a/engines/zvision/scripting/sidefx.cpp b/engines/zvision/scripting/sidefx.cpp new file mode 100644 index 000000000000..c0b8a4d12fe9 --- /dev/null +++ b/engines/zvision/scripting/sidefx.cpp @@ -0,0 +1,36 @@ +/* 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 "zvision/scripting/sidefx.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/utility/utility.h" + +#include "common/stream.h" + +namespace ZVision { + + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx.h b/engines/zvision/scripting/sidefx.h new file mode 100644 index 000000000000..5bb14f0cddc6 --- /dev/null +++ b/engines/zvision/scripting/sidefx.h @@ -0,0 +1,114 @@ +/* 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 SIDEFX_H_INCLUDED +#define SIDEFX_H_INCLUDED + +namespace Common { +class SeekableReadStream; +struct Point; +class WriteStream; +} + +namespace ZVision { + +class ZVision; + +class SideFX { +public: + + enum SideFXType { + SIDEFX_ANIM = 1, + SIDEFX_AUDIO = 2, + SIDEFX_DISTORT = 4, + SIDEFX_PANTRACK = 8, + SIDEFX_REGION = 16, + SIDEFX_TIMER = 32, + SIDEFX_TTYTXT = 64, + SIDEFX_UNK = 128, + SIDEFX_ALL = 255 + }; + + SideFX() : _engine(0), _key(0), _type(SIDEFX_UNK) {} + SideFX(ZVision *engine, uint32 key, SideFXType type) : _engine(engine), _key(key), _type(type) {} + virtual ~SideFX() {} + + uint32 getKey() { + return _key; + } + SideFXType getType() { + return _type; + } + + virtual bool process(uint32 deltaTimeInMillis) { + return false; + } + /** + * Serialize a SideFX for save game use. This should only be used if a SideFX needs + * to save values that would be different from initialization. AKA a TimerNode needs to + * store the amount of time left on the timer. Any Controls overriding this *MUST* write + * their key as the first data outputted. The default implementation is NOP. + * + * NOTE: If this method is overridden, you MUST also override deserialize() + * and needsSerialization() + * + * @param stream Stream to write any needed data to + */ + virtual void serialize(Common::WriteStream *stream) {} + /** + * De-serialize data from a save game stream. This should only be implemented if the + * SideFX also implements serialize(). The calling method assumes the size of the + * data read from the stream exactly equals that written in serialize(). The default + * implementation is NOP. + * + * NOTE: If this method is overridden, you MUST also override serialize() + * and needsSerialization() + * + * @param stream Save game file stream + */ + virtual void deserialize(Common::SeekableReadStream *stream) {} + /** + * If a SideFX overrides serialize() and deserialize(), this should return true + * + * @return Does the SideFX need save game serialization? + */ + virtual inline bool needsSerialization() { + return false; + } + + virtual bool stop() { + return true; + } + virtual void kill() {} + +protected: + ZVision *_engine; + uint32 _key; + SideFXType _type; + +// Static member functions +public: + +}; +} // End of namespace ZVision + +#endif // SIDEFX_H_INCLUDED diff --git a/engines/zvision/scripting/sidefx/animation_node.cpp b/engines/zvision/scripting/sidefx/animation_node.cpp new file mode 100644 index 000000000000..98ac4e3b373d --- /dev/null +++ b/engines/zvision/scripting/sidefx/animation_node.cpp @@ -0,0 +1,207 @@ +/* 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 "zvision/scripting/sidefx/animation_node.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/animation/meta_animation.h" + +#include "graphics/surface.h" + + +namespace ZVision { + +AnimationNode::AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse) + : SideFX(engine, controlKey, SIDEFX_ANIM), + _DisposeAfterUse(DisposeAfterUse), + _mask(mask), + _animation(NULL) { + + _animation = new MetaAnimation(fileName, engine); + _frmDelay = _animation->frameTime(); + + if (frate > 0) + _frmDelay = 1000.0 / frate; +} + +AnimationNode::~AnimationNode() { + if (_animation) + delete _animation; + + _engine->getScriptManager()->setStateValue(_key, 2); + + PlayNodes::iterator it = _playList.begin(); + if (it != _playList.end()) { + _engine->getScriptManager()->setStateValue((*it).slot, 2); + + if ((*it)._scaled) + delete(*it)._scaled; + } + + _playList.clear(); +} + +bool AnimationNode::process(uint32 deltaTimeInMillis) { + PlayNodes::iterator it = _playList.begin(); + if (it != _playList.end()) { + playnode *nod = &(*it); + + nod->_delay -= deltaTimeInMillis; + if (nod->_delay <= 0) { + nod->_delay += _frmDelay; + + const Graphics::Surface *frame = NULL; + + if (nod->_curFrame == -1) { // Start of new playlist node + nod->_curFrame = nod->start; + + _animation->seekToFrame(nod->_curFrame); + frame = _animation->decodeNextFrame(); + + nod->_delay = _frmDelay; + if (nod->slot) + _engine->getScriptManager()->setStateValue(nod->slot, 1); + } else { + nod->_curFrame++; + + if (nod->_curFrame > nod->stop) { + nod->loop--; + + if (nod->loop == 0) { + if (nod->slot >= 0) + _engine->getScriptManager()->setStateValue(nod->slot, 2); + if (nod->_scaled) + delete nod->_scaled; + _playList.erase(it); + return _DisposeAfterUse; + } + + nod->_curFrame = nod->start; + _animation->seekToFrame(nod->_curFrame); + } + + frame = _animation->decodeNextFrame(); + } + + if (frame) { + + uint32 dstw; + uint32 dsth; + if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + dstw = nod->pos.height(); + dsth = nod->pos.width(); + } else { + dstw = nod->pos.width(); + dsth = nod->pos.height(); + } + + if (frame->w != dstw || frame->h != dsth) { + if (nod->_scaled) + if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) { + delete nod->_scaled; + nod->_scaled = NULL; + } + + if (!nod->_scaled) { + nod->_scaled = new Graphics::Surface; + nod->_scaled->create(dstw, dsth, frame->format); + } + + _engine->getRenderManager()->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); + frame = nod->_scaled; + } + + if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + Graphics::Surface *transposed = RenderManager::tranposeSurface(frame); + if (_mask > 0) + _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); + else + _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top); + delete transposed; + } else { + if (_mask > 0) + _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); + else + _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top); + } + } + } + } + + return false; +} + + + +void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { + playnode nod; + nod.loop = loops; + nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1); + nod.start = startFrame; + nod.stop = endFrame; + + if (nod.stop >= (int)_animation->frameCount()) + nod.stop = _animation->frameCount() - 1; + + nod.slot = slot; + nod._curFrame = -1; + nod._delay = 0; + nod._scaled = NULL; + _playList.push_back(nod); +} + +bool AnimationNode::stop() { + PlayNodes::iterator it = _playList.begin(); + if (it != _playList.end()) { + _engine->getScriptManager()->setStateValue((*it).slot, 2); + if ((*it)._scaled) + delete(*it)._scaled; + } + + _playList.clear(); + + // We don't need to delete, it's may be reused + return false; +} + +void AnimationNode::setNewFrameDelay(int32 newDelay) { + if (newDelay > 0) { + PlayNodes::iterator it = _playList.begin(); + if (it != _playList.end()) { + playnode *nod = &(*it); + float percent = (float)nod->_delay / (float)_frmDelay; + nod->_delay = percent * newDelay; // Scale to new max + } + + _frmDelay = newDelay; + } +} + +int32 AnimationNode::getFrameDelay() { + return _frmDelay; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/animation_node.h b/engines/zvision/scripting/sidefx/animation_node.h new file mode 100644 index 000000000000..dab3d88d802b --- /dev/null +++ b/engines/zvision/scripting/sidefx/animation_node.h @@ -0,0 +1,84 @@ +/* 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 ZVISION_ANIMATION_NODE_H +#define ZVISION_ANIMATION_NODE_H + +#include "zvision/scripting/sidefx.h" +#include "common/rect.h" +#include "common/list.h" + + +namespace Common { +class String; +} + +namespace Graphics { +struct Surface; +} + +namespace ZVision { + +class ZVision; +class MetaAnimation; + +class AnimationNode : public SideFX { +public: + AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse = true); + ~AnimationNode(); + + struct playnode { + Common::Rect pos; + int32 slot; + int32 start; + int32 stop; + int32 loop; + int32 _curFrame; + int32 _delay; + Graphics::Surface *_scaled; + }; + +private: + typedef Common::List PlayNodes; + + PlayNodes _playList; + + int32 _mask; + bool _DisposeAfterUse; + + MetaAnimation *_animation; + int32 _frmDelay; + +public: + bool process(uint32 deltaTimeInMillis); + + void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1); + + bool stop(); + + void setNewFrameDelay(int32 newDelay); + int32 getFrameDelay(); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/sidefx/distort_node.cpp b/engines/zvision/scripting/sidefx/distort_node.cpp new file mode 100644 index 000000000000..9be6b29413bc --- /dev/null +++ b/engines/zvision/scripting/sidefx/distort_node.cpp @@ -0,0 +1,109 @@ +/* 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 "zvision/scripting/sidefx/distort_node.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/graphics/render_table.h" + +#include "common/stream.h" + + +namespace ZVision { + +DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale) + : SideFX(engine, key, SIDEFX_DISTORT) { + + _angle = _engine->getRenderManager()->getRenderTable()->getAngle(); + _linScale = _engine->getRenderManager()->getRenderTable()->getLinscale(); + + _speed = speed; + _incr = true; + _startAngle = startAngle; + _endAngle = endAngle; + _startLineScale = startLineScale; + _endLineScale = endLineScale; + + _curFrame = 1.0; + + _diffAngle = endAngle - startAngle; + _diffLinScale = endLineScale - startLineScale; + + _frmSpeed = (float)speed / 15.0; + _frames = ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed); + if (_frames <= 0) + _frames = 1; + + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 1); +} + +DistortNode::~DistortNode() { + setParams(_angle, _linScale); +} + +bool DistortNode::process(uint32 deltaTimeInMillis) { + + float updTime = deltaTimeInMillis / (1000.0 / 60.0); + + if (_incr) + _curFrame += updTime; + else + _curFrame -= updTime; + + if (_curFrame < 1.0) { + _curFrame = 1.0; + _incr = true; + } else if (_curFrame > _frames) { + _curFrame = _frames; + _incr = false; + } + + float diff = (1.0 / (5.0 - (_curFrame * _frmSpeed))) / (5.0 - _frmSpeed); + + + setParams(_startAngle + diff * _diffAngle, _startLineScale + diff * _diffLinScale); + + return false; +} + +void DistortNode::setParams(float angl, float linScale) { + RenderTable *table = _engine->getRenderManager()->getRenderTable(); + if (table->getRenderState() == RenderTable::PANORAMA) { + table->setPanoramaFoV(angl); + table->setPanoramaScale(linScale); + table->generateRenderTable(); + _engine->getRenderManager()->markDirty(); + } else if (table->getRenderState() == RenderTable::TILT) { + table->setTiltFoV(angl); + table->setTiltScale(linScale); + table->generateRenderTable(); + _engine->getRenderManager()->markDirty(); + } +} + + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/distort_node.h b/engines/zvision/scripting/sidefx/distort_node.h new file mode 100644 index 000000000000..787a69bdde06 --- /dev/null +++ b/engines/zvision/scripting/sidefx/distort_node.h @@ -0,0 +1,63 @@ +/* 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 ZVISION_DISTORT_NODE_H +#define ZVISION_DISTORT_NODE_H + +#include "zvision/scripting/sidefx.h" + +namespace ZVision { + +class ZVision; + +class DistortNode : public SideFX { +public: + DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale); + ~DistortNode(); + + bool process(uint32 deltaTimeInMillis); + +private: + int16 _speed; + float _startAngle; + float _endAngle; + float _startLineScale; + float _endLineScale; + + float _frmSpeed; + float _diffAngle; + float _diffLinScale; + bool _incr; + int16 _frames; + + float _curFrame; + + float _angle; + float _linScale; + +private: + void setParams(float angl, float linScale); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/sidefx/music_node.cpp b/engines/zvision/scripting/sidefx/music_node.cpp new file mode 100644 index 000000000000..e9baadb011a7 --- /dev/null +++ b/engines/zvision/scripting/sidefx/music_node.cpp @@ -0,0 +1,240 @@ +/* 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 "zvision/scripting/sidefx/music_node.h" + +#include "zvision/zvision.h" +#include "zvision/core/midi.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/sound/zork_raw.h" + +#include "common/stream.h" +#include "common/file.h" +#include "audio/decoders/wave.h" + + +namespace ZVision { + +MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, int8 volume) + : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + _loop = loop; + _volume = volume; + _crossfade = false; + _crossfadeTarget = 0; + _crossfadeTime = 0; + _attenuate = 0; + _pantrack = false; + _pantrackPosition = 0; + _sub = NULL; + + Audio::RewindableAudioStream *audioStream; + + if (filename.contains(".wav")) { + Common::File *file = new Common::File(); + if (_engine->getSearchManager()->openFile(*file, filename)) { + audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); + } + } else { + audioStream = makeRawZorkStream(filename, _engine); + } + + _stereo = audioStream->isStereo(); + + if (_loop) { + Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, _volume); + } else { + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, _volume); + } + + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 1); + + Common::String subname = filename; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + + if (_engine->getSearchManager()->hasFile(subname)) + _sub = new Subtitle(_engine, subname); +} + +MusicNode::~MusicNode() { + _engine->_mixer->stopHandle(_handle); + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 2); + if (_sub) + delete _sub; + debug(1, "MusicNode: %d destroyed\n", _key); +} + +void MusicNode::setPanTrack(int16 pos) { + if (!_stereo) { + _pantrack = true; + _pantrackPosition = pos; + setVolume(_volume); + } +} + +void MusicNode::unsetPanTrack() { + _pantrack = false; + setVolume(_volume); +} + +void MusicNode::setFade(int32 time, uint8 target) { + _crossfadeTarget = target; + _crossfadeTime = time; + _crossfade = true; +} + +bool MusicNode::process(uint32 deltaTimeInMillis) { + if (! _engine->_mixer->isSoundHandleActive(_handle)) + return stop(); + else { + uint8 _newvol = _volume; + + if (_crossfade) { + if (_crossfadeTime > 0) { + if ((int32)deltaTimeInMillis > _crossfadeTime) + deltaTimeInMillis = _crossfadeTime; + _newvol += floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis; + _crossfadeTime -= deltaTimeInMillis; + } else { + _crossfade = false; + _newvol = _crossfadeTarget; + } + } + + if (_pantrack || _volume != _newvol) + setVolume(_newvol); + + if (_sub) + _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); + } + return false; +} + +void MusicNode::setVolume(uint8 newVolume) { + if (_pantrack) { + int curX = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + curX -= _pantrackPosition; + int32 _width = _engine->getRenderManager()->getBkgSize().x; + if (curX < (-_width) / 2) + curX += _width; + else if (curX >= _width / 2) + curX -= _width; + + float norm = (float)curX / ((float)_width / 2.0); + float lvl = fabs(norm); + if (lvl > 0.5) + lvl = (lvl - 0.5) * 1.7; + else + lvl = 1.0; + + float bal = sin(-norm * 3.1415926) * 127.0; + + if (_engine->_mixer->isSoundHandleActive(_handle)) { + _engine->_mixer->setChannelBalance(_handle, bal); + _engine->_mixer->setChannelVolume(_handle, newVolume * lvl); + } + } else { + if (_engine->_mixer->isSoundHandleActive(_handle)) { + _engine->_mixer->setChannelBalance(_handle, 0); + _engine->_mixer->setChannelVolume(_handle, newVolume); + } + } + + _volume = newVolume; +} + +PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos) + : SideFX(engine, key, SIDEFX_PANTRACK) { + _slot = slot; + + SideFX *fx = _engine->getScriptManager()->getSideFX(slot); + if (fx && fx->getType() == SIDEFX_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; + mus->setPanTrack(pos); + } +} + +PanTrackNode::~PanTrackNode() { + SideFX *fx = _engine->getScriptManager()->getSideFX(_slot); + if (fx && fx->getType() == SIDEFX_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; + mus->unsetPanTrack(); + } +} + + +MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume) + : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + _volume = volume; + _prog = program; + _noteNumber = note; + _pan = 0; + + _chan = _engine->getMidiManager()->getFreeChannel(); + + if (_chan >= 0) { + _engine->getMidiManager()->setVolume(_chan, _volume); + _engine->getMidiManager()->setPan(_chan, _pan); + _engine->getMidiManager()->setProgram(_chan, _prog); + _engine->getMidiManager()->noteOn(_chan, _noteNumber, _volume); + } + + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 1); +} + +MusicMidiNode::~MusicMidiNode() { + if (_chan >= 0) { + _engine->getMidiManager()->noteOff(_chan); + } + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 2); +} + +void MusicMidiNode::setPanTrack(int16 pos) { +} + +void MusicMidiNode::unsetPanTrack() { +} + +void MusicMidiNode::setFade(int32 time, uint8 target) { +} + +bool MusicMidiNode::process(uint32 deltaTimeInMillis) { + return false; +} + +void MusicMidiNode::setVolume(uint8 newVolume) { + if (_chan >= 0) { + _engine->getMidiManager()->setVolume(_chan, newVolume); + } + _volume = newVolume; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/music_node.h b/engines/zvision/scripting/sidefx/music_node.h new file mode 100644 index 000000000000..954e2f474ebc --- /dev/null +++ b/engines/zvision/scripting/sidefx/music_node.h @@ -0,0 +1,135 @@ +/* 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 ZVISION_MUSIC_NODE_H +#define ZVISION_MUSIC_NODE_H + +#include "audio/mixer.h" +#include "zvision/scripting/sidefx.h" +#include "zvision/subtitles/subtitles.h" + +namespace Common { +class String; +} + +namespace ZVision { + +class MusicNodeBASE : public SideFX { +public: + MusicNodeBASE(ZVision *engine, uint32 key, SideFXType type) : SideFX(engine, key, type) {} + ~MusicNodeBASE() {} + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + virtual bool process(uint32 deltaTimeInMillis) = 0; + + virtual void setVolume(uint8 volume) = 0; + + virtual void setPanTrack(int16 pos) = 0; + virtual void unsetPanTrack() = 0; + + virtual void setFade(int32 time, uint8 target) = 0; +}; + +class MusicNode : public MusicNodeBASE { +public: + MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, int8 volume); + ~MusicNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); + + void setVolume(uint8 volume); + + void setPanTrack(int16 pos); + void unsetPanTrack(); + + void setFade(int32 time, uint8 target); + +private: + int32 _timeLeft; + bool _pantrack; + int32 _pantrackPosition; + int32 _attenuate; + uint8 _volume; + bool _loop; + bool _crossfade; + uint8 _crossfadeTarget; + int32 _crossfadeTime; + bool _stereo; + Audio::SoundHandle _handle; + Subtitle *_sub; +}; + +class MusicMidiNode : public MusicNodeBASE { +public: + MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume); + ~MusicMidiNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); + + void setVolume(uint8 volume); + + void setPanTrack(int16 pos); + void unsetPanTrack(); + + void setFade(int32 time, uint8 target); + +private: + int8 _chan; + int8 _noteNumber; + int8 _velocity; + int8 _pan; + int8 _volume; + int8 _prog; +}; + +class PanTrackNode : public SideFX { +public: + PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos); + ~PanTrackNode(); + +private: + uint32 _slot; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/sidefx/region_node.cpp b/engines/zvision/scripting/sidefx/region_node.cpp new file mode 100644 index 000000000000..de613d8af237 --- /dev/null +++ b/engines/zvision/scripting/sidefx/region_node.cpp @@ -0,0 +1,56 @@ +/* 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 "zvision/scripting/sidefx/region_node.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" + +namespace ZVision { + +RegionNode::RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay) + : SideFX(engine, key, SIDEFX_REGION) { + _effect = effect; + _delay = delay; + _timeLeft = 0; +} + +RegionNode::~RegionNode() { + _engine->getRenderManager()->deleteEffect(_key); +} + +bool RegionNode::process(uint32 deltaTimeInMillis) { + _timeLeft -= deltaTimeInMillis; + + if (_timeLeft <= 0) { + _timeLeft = _delay; + if (_effect) + _effect->update(); + } + + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/region_node.h b/engines/zvision/scripting/sidefx/region_node.h new file mode 100644 index 000000000000..ec716b6e3ee7 --- /dev/null +++ b/engines/zvision/scripting/sidefx/region_node.h @@ -0,0 +1,57 @@ +/* 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 ZVISION_REGION_NODE_H +#define ZVISION_REGION_NODE_H + +#include "graphics/surface.h" + +#include "zvision/scripting/sidefx.h" +#include "zvision/graphics/effect.h" + +namespace ZVision { + +class ZVision; + +class RegionNode : public SideFX { +public: + RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay); + ~RegionNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); + +private: + int32 _timeLeft; + uint32 _delay; + Effect *_effect; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/sidefx/syncsound_node.cpp b/engines/zvision/scripting/sidefx/syncsound_node.cpp new file mode 100644 index 000000000000..2bfdc349d108 --- /dev/null +++ b/engines/zvision/scripting/sidefx/syncsound_node.cpp @@ -0,0 +1,86 @@ +/* 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 "zvision/scripting/sidefx/syncsound_node.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/sound/zork_raw.h" + +#include "common/stream.h" +#include "common/file.h" +#include "audio/decoders/wave.h" + + +namespace ZVision { + +SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::String &filename, int32 syncto) + : SideFX(engine, key, SIDEFX_AUDIO) { + _syncto = syncto; + _sub = NULL; + + Audio::RewindableAudioStream *audioStream; + + if (filename.contains(".wav")) { + Common::File *file = new Common::File(); + if (_engine->getSearchManager()->openFile(*file, filename)) { + audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); + } + } else { + audioStream = makeRawZorkStream(filename, _engine); + } + + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream); + + Common::String subname = filename; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + + if (_engine->getSearchManager()->hasFile(subname)) + _sub = new Subtitle(_engine, subname); +} + +SyncSoundNode::~SyncSoundNode() { + _engine->_mixer->stopHandle(_handle); + if (_sub) + delete _sub; +} + +bool SyncSoundNode::process(uint32 deltaTimeInMillis) { + if (! _engine->_mixer->isSoundHandleActive(_handle)) + return stop(); + else { + + if (_engine->getScriptManager()->getSideFX(_syncto) == NULL) + return stop(); + + if (_sub) + _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); + } + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/syncsound_node.h b/engines/zvision/scripting/sidefx/syncsound_node.h new file mode 100644 index 000000000000..c16ffebe7ff6 --- /dev/null +++ b/engines/zvision/scripting/sidefx/syncsound_node.h @@ -0,0 +1,56 @@ +/* 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 ZVISION_SYNCSOUND_NODE_H +#define ZVISION_SYNCSOUND_NODE_H + +#include "audio/mixer.h" +#include "zvision/scripting/sidefx.h" +#include "zvision/subtitles/subtitles.h" + +namespace Common { +class String; +} + +namespace ZVision { +class SyncSoundNode : public SideFX { +public: + SyncSoundNode(ZVision *engine, uint32 key, Common::String &file, int32 syncto); + ~SyncSoundNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); +private: + int32 _syncto; + Audio::SoundHandle _handle; + Subtitle *_sub; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scripting/controls/timer_node.cpp b/engines/zvision/scripting/sidefx/timer_node.cpp similarity index 70% rename from engines/zvision/scripting/controls/timer_node.cpp rename to engines/zvision/scripting/sidefx/timer_node.cpp index 6f88b056a16f..6e71101acd3d 100644 --- a/engines/zvision/scripting/controls/timer_node.cpp +++ b/engines/zvision/scripting/sidefx/timer_node.cpp @@ -8,12 +8,12 @@ * 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. @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/controls/timer_node.h" +#include "zvision/scripting/sidefx/timer_node.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -33,35 +33,42 @@ namespace ZVision { TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) - : Control(engine, key) { - if (_engine->getGameId() == GID_NEMESIS) { + : SideFX(engine, key, SIDEFX_TIMER) { + if (_engine->getGameId() == GID_NEMESIS) _timeLeft = timeInSeconds * 1000; - } else if (_engine->getGameId() == GID_GRANDINQUISITOR) { + else if (_engine->getGameId() == GID_GRANDINQUISITOR) _timeLeft = timeInSeconds * 100; - } - _engine->getScriptManager()->setStateValue(_key, 1); + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 1); } TimerNode::~TimerNode() { - if (_timeLeft <= 0) + if (_key != StateKey_NotSet) _engine->getScriptManager()->setStateValue(_key, 2); - else - _engine->getScriptManager()->setStateValue(_key, _timeLeft); // If timer was stopped by stop or kill + int32 timeLeft = _timeLeft / (_engine->getGameId() == GID_NEMESIS ? 1000 : 100); + if (timeLeft > 0) + _engine->getScriptManager()->setStateValue(_key, timeLeft); // If timer was stopped by stop or kill } bool TimerNode::process(uint32 deltaTimeInMillis) { _timeLeft -= deltaTimeInMillis; - if (_timeLeft <= 0) { - // Let the destructor reset the state value - return true; - } + if (_timeLeft <= 0) + return stop(); return false; } +bool TimerNode::stop() { + if (_key != StateKey_NotSet) + _engine->getScriptManager()->setStateValue(_key, 2); + return true; +} + void TimerNode::serialize(Common::WriteStream *stream) { + stream->writeUint32BE(MKTAG('T', 'I', 'M', 'R')); + stream->writeUint32LE(8); // size stream->writeUint32LE(_key); stream->writeUint32LE(_timeLeft); } diff --git a/engines/zvision/scripting/controls/timer_node.h b/engines/zvision/scripting/sidefx/timer_node.h similarity index 92% rename from engines/zvision/scripting/controls/timer_node.h rename to engines/zvision/scripting/sidefx/timer_node.h index 48b5fad1e944..7a26aff2513b 100644 --- a/engines/zvision/scripting/controls/timer_node.h +++ b/engines/zvision/scripting/sidefx/timer_node.h @@ -8,12 +8,12 @@ * 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. @@ -23,13 +23,13 @@ #ifndef ZVISION_TIMER_NODE_H #define ZVISION_TIMER_NODE_H -#include "zvision/scripting/control.h" +#include "zvision/scripting/sidefx.h" namespace ZVision { class ZVision; -class TimerNode : public Control { +class TimerNode : public SideFX { public: TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); ~TimerNode(); @@ -44,7 +44,11 @@ class TimerNode : public Control { bool process(uint32 deltaTimeInMillis); void serialize(Common::WriteStream *stream); void deserialize(Common::SeekableReadStream *stream); - inline bool needsSerialization() { return true; } + inline bool needsSerialization() { + return true; + } + + bool stop(); private: int32 _timeLeft; diff --git a/engines/zvision/scripting/sidefx/ttytext_node.cpp b/engines/zvision/scripting/sidefx/ttytext_node.cpp new file mode 100644 index 000000000000..68636eb422ce --- /dev/null +++ b/engines/zvision/scripting/sidefx/ttytext_node.cpp @@ -0,0 +1,175 @@ +/* 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 "zvision/scripting/sidefx/ttytext_node.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/text/text.h" + +#include "common/stream.h" +#include "common/file.h" + + +namespace ZVision { + +ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay) : + SideFX(engine, key, SIDEFX_TTYTXT), + _fnt(engine) { + _delay = delay; + _r = r; + _txtpos = 0; + _nexttime = 0; + _dx = 0; + _dy = 0; + + Common::File *infile = _engine->getSearchManager()->openFile(file); + if (infile) { + while (!infile->eos()) { + Common::String asciiLine = readWideLine(*infile); + if (asciiLine.empty()) { + continue; + } + _txtbuf += asciiLine; + } + + delete infile; + } + _img.create(_r.width(), _r.height(), _engine->_pixelFormat); + _style.sharp = true; + _style.readAllStyle(_txtbuf); + _style.setFont(_fnt); + _engine->getScriptManager()->setStateValue(_key, 1); +} + +ttyTextNode::~ttyTextNode() { + _engine->getScriptManager()->setStateValue(_key, 2); + _img.free(); +} + +bool ttyTextNode::process(uint32 deltaTimeInMillis) { + _nexttime -= deltaTimeInMillis; + + if (_nexttime < 0) { + if (_txtpos < _txtbuf.size()) { + if (_txtbuf[_txtpos] == '<') { + int32 strt = _txtpos; + int32 endt = 0; + int16 ret = 0; + while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size()) + _txtpos++; + endt = _txtpos; + if (strt != -1) + if ((endt - strt - 1) > 0) + ret = _style.parseStyle(_txtbuf.c_str() + strt + 1, endt - strt - 1); + + if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { + if (ret & TXT_RET_FNTCHG) + _style.setFont(_fnt); + if (ret & TXT_RET_FNTSTL) + _style.setFontStyle(_fnt); + + if (ret & TXT_RET_NEWLN) + newline(); + } + + if (ret & TXT_RET_HASSTBOX) { + Common::String buf; + buf.format("%d", _style.statebox); + + for (uint8 j = 0; j < buf.size(); j++) + outchar(buf[j]); + } + + _txtpos++; + } else { + int8 charsz = getUtf8CharSize(_txtbuf[_txtpos]); + + uint16 chr = readUtf8Char(_txtbuf.c_str() + _txtpos); + + if (chr == ' ') { + uint32 i = _txtpos + charsz; + uint16 width = _fnt.getCharWidth(chr); + + while (i < _txtbuf.size() && _txtbuf[i] != ' ' && _txtbuf[i] != '<') { + + int8 chsz = getUtf8CharSize(_txtbuf[i]); + uint16 uchr = readUtf8Char(_txtbuf.c_str() + _txtpos); + + width += _fnt.getCharWidth(uchr); + + i += chsz; + } + + if (_dx + width > _r.width()) + newline(); + else + outchar(chr); + } else + outchar(chr); + + _txtpos += charsz; + } + _nexttime = _delay; + _engine->getRenderManager()->blitSurfaceToBkg(_img, _r.left, _r.top); + } else + return stop(); + } + + return false; +} + +void ttyTextNode::scroll() { + int32 scrl = 0; + while (_dy - scrl > _r.height() - _fnt.getFontHeight()) + scrl += _fnt.getFontHeight(); + int8 *pixels = (int8 *)_img.getPixels(); + for (uint16 h = scrl; h < _img.h; h++) + memcpy(pixels + _img.pitch * (h - scrl), pixels + _img.pitch * h, _img.pitch); + + _img.fillRect(Common::Rect(0, _img.h - scrl, _img.w, _img.h), 0); + _dy -= scrl; +} + +void ttyTextNode::newline() { + _dy += _fnt.getFontHeight(); + _dx = 0; +} + +void ttyTextNode::outchar(uint16 chr) { + uint32 clr = _engine->_pixelFormat.RGBToColor(_style.red, _style.green, _style.blue); + + if (_dx + _fnt.getCharWidth(chr) > _r.width()) + newline(); + + if (_dy + _fnt.getFontHeight() >= _r.height()) + scroll(); + + _fnt.drawChar(&_img, chr, _dx, _dy, clr); + + _dx += _fnt.getCharWidth(chr); +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/ttytext_node.h b/engines/zvision/scripting/sidefx/ttytext_node.h new file mode 100644 index 000000000000..a229129b9d52 --- /dev/null +++ b/engines/zvision/scripting/sidefx/ttytext_node.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 ZVISION_TTYTEXT_NODE_H +#define ZVISION_TTYTEXT_NODE_H + +#include "common/rect.h" +#include "graphics/surface.h" + +#include "zvision/scripting/sidefx.h" +#include "zvision/text/text.h" +#include "zvision/fonts/truetype_font.h" + +namespace Common { +class String; +} + +namespace ZVision { +class ttyTextNode : public SideFX { +public: + ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay); + ~ttyTextNode(); + + /** + * Decrement the timer by the delta time. If the timer is finished, set the status + * in _globalState and let this node be deleted + * + * @param deltaTimeInMillis The number of milliseconds that have passed since last frame + * @return If true, the node can be deleted after process() finishes + */ + bool process(uint32 deltaTimeInMillis); +private: + Common::Rect _r; + + cTxtStyle _style; + StyledTTFont _fnt; + Common::String _txtbuf; + uint32 _txtpos; + + int32 _delay; + int32 _nexttime; + Graphics::Surface _img; + int16 _dx; + int16 _dy; +private: + + void newline(); + void scroll(); + void outchar(uint16 chr); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp index edee1fd16e7c..a0f660f65fee 100644 --- a/engines/zvision/sound/zork_raw.cpp +++ b/engines/zvision/sound/zork_raw.cpp @@ -8,12 +8,12 @@ * 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. @@ -41,65 +41,62 @@ namespace ZVision { -const int16 RawZorkStream::_stepAdjustmentTable[8] = {-1, -1, -1, 1, 4, 7, 10, 12}; - -const int32 RawZorkStream::_amplitudeLookupTable[89] = {0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, - 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, - 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, - 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, - 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, - 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, - 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, - 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, - 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, - 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, - 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF}; - -const SoundParams RawZorkStream::_zNemSoundParamLookupTable[6] = {{'6', 0x2B11, false, false}, - {'a', 0x5622, false, true}, - {'b', 0x5622, true, true}, - {'n', 0x2B11, false, true}, - {'s', 0x5622, false, true}, - {'t', 0x5622, true, true} -}; - -const SoundParams RawZorkStream::_zgiSoundParamLookupTable[5] = {{'a',0x5622, false, false}, - {'k',0x2B11, true, true}, - {'p',0x5622, false, true}, - {'q',0x5622, true, true}, - {'u',0xAC44, true, true} -}; - -RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) - : _rate(rate), - _stereo(0), - _stream(stream, disposeStream), - _endOfData(false) { +const int16 RawChunkStream::_stepAdjustmentTable[8] = { -1, -1, -1, 1, 4, 7, 10, 12}; + +const int32 RawChunkStream::_amplitudeLookupTable[89] = {0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, + 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, + 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, + 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, + 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, + 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, + 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, + 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, + 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, + 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, + 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF + }; + +RawChunkStream::RawChunkStream(bool stereo) { if (stereo) _stereo = 1; + else + _stereo = 0; + init(); +} + +void RawChunkStream::init() { _lastSample[0].index = 0; _lastSample[0].sample = 0; _lastSample[1].index = 0; _lastSample[1].sample = 0; +} - // Calculate the total playtime of the stream - if (stereo) - _playtime = Audio::Timestamp(0, _stream->size() / 2, rate); - else - _playtime = Audio::Timestamp(0, _stream->size(), rate); +RawChunkStream::RawChunk RawChunkStream::readNextChunk(Common::SeekableReadStream *stream) { + RawChunk tmp; + tmp.size = 0; + tmp.data = NULL; + + if (stream && (stream->size() == 0 || stream->eos())) + return tmp; + + tmp.size = (stream->size() - stream->pos()) * 2; + tmp.data = (int16 *)calloc(tmp.size, 1); + + readBuffer(tmp.data, stream, stream->size() - stream->pos()); + + return tmp; } -int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { - int bytesRead = 0; +int RawChunkStream::readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples) { + int32 bytesRead = 0; // 0: Left, 1: Right uint channel = 0; while (bytesRead < numSamples) { - byte encodedSample = _stream->readByte(); - if (_stream->eos()) { - _endOfData = true; + byte encodedSample = stream->readByte(); + if (stream->eos()) { return bytesRead; } bytesRead++; @@ -140,6 +137,91 @@ int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { // Increment and wrap the channel channel = (channel + 1) & _stereo; } + return bytesRead; +} + +const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = {{'0', 0x1F40, false, false, false}, + {'1', 0x1F40, true, false, false}, + {'2', 0x1F40, false, false, true}, + {'3', 0x1F40, true, false, true}, + {'4', 0x2B11, false, false, false}, + {'5', 0x2B11, true, false, false}, + {'6', 0x2B11, false, false, true}, + {'7', 0x2B11, true, false, true}, + {'8', 0x5622, false, false, false}, + {'9', 0x5622, true, false, false}, + {'a', 0x5622, false, false, true}, + {'b', 0x5622, true, false, true}, + {'c', 0xAC44, false, false, false}, + {'d', 0xAC44, true, false, false}, + {'e', 0xAC44, false, false, true}, + {'f', 0xAC44, true, false, true}, + {'g', 0x1F40, false, true, false}, + {'h', 0x1F40, true, true, false}, + {'j', 0x1F40, false, true, true}, + {'k', 0x1F40, true, true, true}, + {'l', 0x2B11, false, true, false}, + {'m', 0x2B11, true, true, false}, + {'n', 0x2B11, false, true, true}, + {'p', 0x2B11, true, true, true}, + {'q', 0x5622, false, true, false}, + {'r', 0x5622, true, true, false}, + {'s', 0x5622, false, true, true}, + {'t', 0x5622, true, true, true}, + {'u', 0xAC44, false, true, false}, + {'v', 0xAC44, true, true, false}, + {'w', 0xAC44, false, true, true}, + {'x', 0xAC44, true, true, true} +}; + +const SoundParams RawZorkStream::_zgiSoundParamLookupTable[24] = {{'4', 0x2B11, false, false, false}, + {'5', 0x2B11, true, false, false}, + {'6', 0x2B11, false, false, true}, + {'7', 0x2B11, true, false, true}, + {'8', 0x5622, false, false, false}, + {'9', 0x5622, true, false, false}, + {'a', 0x5622, false, false, true}, + {'b', 0x5622, true, false, true}, + {'c', 0xAC44, false, false, false}, + {'d', 0xAC44, true, false, false}, + {'e', 0xAC44, false, false, true}, + {'f', 0xAC44, true, false, true}, + {'g', 0x2B11, false, true, false}, + {'h', 0x2B11, true, true, false}, + {'j', 0x2B11, false, true, true}, + {'k', 0x2B11, true, true, true}, + {'m', 0x5622, false, true, false}, + {'n', 0x5622, true, true, false}, + {'p', 0x5622, false, true, true}, + {'q', 0x5622, true, true, true}, + {'r', 0xAC44, false, true, false}, + {'s', 0xAC44, true, true, false}, + {'t', 0xAC44, false, true, true}, + {'u', 0xAC44, true, true, true} +}; + +RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) + : _rate(rate), + _stereo(0), + _stream(stream, disposeStream), + _endOfData(false), + _streamReader(stereo) { + if (stereo) + _stereo = 1; + + // Calculate the total playtime of the stream + if (stereo) + _playtime = Audio::Timestamp(0, _stream->size() / 2, rate); + else + _playtime = Audio::Timestamp(0, _stream->size(), rate); +} + +int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { + + int32 bytesRead = _streamReader.readBuffer(buffer, _stream.get(), numSamples); + + if (_stream->eos()) + _endOfData = true; return bytesRead; } @@ -148,18 +230,15 @@ bool RawZorkStream::rewind() { _stream->seek(0, 0); _stream->clearErr(); _endOfData = false; - _lastSample[0].index = 0; - _lastSample[0].sample = 0; - _lastSample[1].index = 0; - _lastSample[1].sample = 0; + _streamReader.init(); return true; } Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { if (stereo) assert(stream->size() % 2 == 0); @@ -167,46 +246,39 @@ Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stre } Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); } Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { Common::File *file = new Common::File(); - assert(file->open(filePath)); + assert(engine->getSearchManager()->openFile(*file, filePath)); Common::String fileName = getFileName(filePath); fileName.toLowercase(); - SoundParams soundParams = { ' ', 0, false, false }; - bool foundParams = false; - char fileIdentifier = (engine->getGameId() == GID_NEMESIS) ? fileName[6] : fileName[7]; + SoundParams soundParams; if (engine->getGameId() == GID_NEMESIS) { - for (uint i = 0; i < ARRAYSIZE(RawZorkStream::_zNemSoundParamLookupTable); ++i) { - if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == fileIdentifier) { + for (int i = 0; i < 32; ++i) { + if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == (fileName[6])) soundParams = RawZorkStream::_zNemSoundParamLookupTable[i]; - foundParams = true; - } } } else if (engine->getGameId() == GID_GRANDINQUISITOR) { - for (uint i = 0; i < ARRAYSIZE(RawZorkStream::_zgiSoundParamLookupTable); ++i) { - if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == fileIdentifier) { + for (int i = 0; i < 24; ++i) { + if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == (fileName[7])) soundParams = RawZorkStream::_zgiSoundParamLookupTable[i]; - foundParams = true; - } } } - if (!foundParams) - error("Unable to find sound params for file '%s'. File identifier is '%c'", filePath.c_str(), fileIdentifier); - if (soundParams.packed) { return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams.rate, soundParams.stereo, DisposeAfterUse::YES); } else { byte flags = 0; + if (soundParams.bits16) + flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; if (soundParams.stereo) flags |= Audio::FLAG_STEREO; diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h index ef98e3e1ef26..a5e346dfbb01 100644 --- a/engines/zvision/sound/zork_raw.h +++ b/engines/zvision/sound/zork_raw.h @@ -8,12 +8,12 @@ * 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. @@ -39,27 +39,20 @@ struct SoundParams { uint32 rate; bool stereo; bool packed; + bool bits16; }; + /** - * This is a stream, which allows for playing raw ADPCM data from a stream. + * This is a ADPCM stream-reader, this class holds context for multi-chunk reading and no buffers. */ -class RawZorkStream : public Audio::RewindableAudioStream { +class RawChunkStream { public: - RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream); + RawChunkStream(bool stereo); - ~RawZorkStream() { + ~RawChunkStream() { } - -public: - static const SoundParams _zNemSoundParamLookupTable[6]; - static const SoundParams _zgiSoundParamLookupTable[5]; - private: - const int _rate; // Sample rate of stream - Audio::Timestamp _playtime; // Calculated total play time - Common::DisposablePtr _stream; // Stream to read data from - bool _endOfData; // Whether the stream end has been reached uint _stereo; /** @@ -74,14 +67,59 @@ class RawZorkStream : public Audio::RewindableAudioStream { static const int16 _stepAdjustmentTable[8]; static const int32 _amplitudeLookupTable[89]; +public: + + struct RawChunk { + int16 *data; + uint32 size; + }; + + void init(); + //Read next audio portion in new stream (needed for avi), return structure with buffer + RawChunk readNextChunk(Common::SeekableReadStream *stream); + //Read numSamples from stream to buffer + int readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples); +}; + +/** + * This is a stream, which allows for playing raw ADPCM data from a stream. + */ +class RawZorkStream : public Audio::RewindableAudioStream { +public: + RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream); + + ~RawZorkStream() { + } + +public: + static const SoundParams _zNemSoundParamLookupTable[32]; + static const SoundParams _zgiSoundParamLookupTable[24]; + +private: + const int _rate; // Sample rate of stream + Audio::Timestamp _playtime; // Calculated total play time + Common::DisposablePtr _stream; // Stream to read data from + bool _endOfData; // Whether the stream end has been reached + uint _stereo; + + RawChunkStream _streamReader; + public: int readBuffer(int16 *buffer, const int numSamples); - bool isStereo() const { return true; } - bool endOfData() const { return _endOfData; } + bool isStereo() const { + return _stereo; + } + bool endOfData() const { + return _endOfData; + } - int getRate() const { return _rate; } - Audio::Timestamp getLength() const { return _playtime; } + int getRate() const { + return _rate; + } + Audio::Timestamp getLength() const { + return _playtime; + } bool rewind(); }; @@ -96,9 +134,9 @@ class RawZorkStream : public Audio::RewindableAudioStream { * @return The new SeekableAudioStream (or 0 on failure). */ Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); /** * Creates an audio stream, which plays from the given stream. @@ -109,9 +147,9 @@ Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, * @return The new SeekableAudioStream (or 0 on failure). */ Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine); diff --git a/engines/zvision/subtitles/subtitles.cpp b/engines/zvision/subtitles/subtitles.cpp new file mode 100644 index 000000000000..1f68b2435f15 --- /dev/null +++ b/engines/zvision/subtitles/subtitles.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 "zvision/subtitles/subtitles.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/core/search_manager.h" +#include "zvision/text/text.h" + +namespace ZVision { + +Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : + _engine(engine), + _areaId(-1), + _subId(-1) { + Common::File file; + if (_engine->getSearchManager()->openFile(file, subname)) { + while (!file.eos()) { + Common::String str = file.readLine(); + if (str.lastChar() == '~') + str.deleteLastChar(); + + if (str.matchString("*Initialization*", true)) { + // Not used + } else if (str.matchString("*Rectangle*", true)) { + int32 x1, y1, x2, y2; + sscanf(str.c_str(), "%*[^:]:%d %d %d %d", &x1, &y1, &x2, &y2); + Common::Rect rct = Common::Rect(x1, y1, x2, y2); + _areaId = _engine->getRenderManager()->createSubArea(rct); + } else if (str.matchString("*TextFile*", true)) { + char filename[64]; + sscanf(str.c_str(), "%*[^:]:%s", filename); + Common::File txt; + if (_engine->getSearchManager()->openFile(txt, filename)) { + while (!txt.eos()) { + Common::String txtline = readWideLine(txt); + sub curSubtitle; + curSubtitle.start = -1; + curSubtitle.stop = -1; + curSubtitle.sub = txtline; + + _subs.push_back(curSubtitle); + } + txt.close(); + } + } else { + int32 st; + int32 en; + int32 sb; + if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) { + if (sb <= (int32)_subs.size()) { + _subs[sb].start = st; + _subs[sb].stop = en; + } + } + } + } + } +} + +Subtitle::~Subtitle() { + if (_areaId != -1) + _engine->getRenderManager()->deleteSubArea(_areaId); + + _subs.clear(); +} + +void Subtitle::process(int32 time) { + int16 j = -1; + for (uint16 i = 0; i < _subs.size(); i++) + if (time >= _subs[i].start && time <= _subs[i].stop) { + j = i; + break; + } + + if (j == -1 && _subId != -1) { + if (_areaId != -1) + _engine->getRenderManager()->updateSubArea(_areaId, ""); + _subId = -1; + } + + if (j != -1 && j != _subId) { + if (_subs[j].sub.size()) + if (_areaId != -1) + _engine->getRenderManager()->updateSubArea(_areaId, _subs[j].sub); + _subId = j; + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/subtitles/subtitles.h b/engines/zvision/subtitles/subtitles.h index 776ddd3a97c7..09e079bba4d1 100644 --- a/engines/zvision/subtitles/subtitles.h +++ b/engines/zvision/subtitles/subtitles.h @@ -23,6 +23,32 @@ #ifndef ZVISION_SUBTITLES_H #define ZVISION_SUBTITLES_H -// TODO: Implement Subtitles +#include "zvision/zvision.h" + +namespace ZVision { + +class ZVision; + +class Subtitle { +public: + Subtitle(ZVision *engine, const Common::String &subname); + ~Subtitle(); + + void process(int32 time); +private: + ZVision *_engine; + int32 _areaId; + int16 _subId; + + struct sub { + int start; + int stop; + Common::String sub; + }; + + Common::Array _subs; +}; + +} #endif diff --git a/engines/zvision/strings/string_manager.cpp b/engines/zvision/text/string_manager.cpp similarity index 91% rename from engines/zvision/strings/string_manager.cpp rename to engines/zvision/text/string_manager.cpp index 22331d8a2462..114f29850508 100644 --- a/engines/zvision/strings/string_manager.cpp +++ b/engines/zvision/text/string_manager.cpp @@ -22,7 +22,9 @@ #include "common/scummsys.h" -#include "zvision/strings/string_manager.h" +#include "zvision/zvision.h" +#include "zvision/core/search_manager.h" +#include "zvision/text/string_manager.h" #include "zvision/fonts/truetype_font.h" @@ -49,10 +51,25 @@ StringManager::~StringManager() { void StringManager::initialize(ZVisionGameId gameId) { if (gameId == GID_NEMESIS) { // TODO: Check this hardcoded filename against all versions of Nemesis - parseStrFile("nemesis.str"); + loadStrFile("nemesis.str"); } else if (gameId == GID_GRANDINQUISITOR) { // TODO: Check this hardcoded filename against all versions of Grand Inquisitor - parseStrFile("inquis.str"); + loadStrFile("inquis.str"); + } +} + +void StringManager::loadStrFile(const Common::String &fileName) { + Common::File file; + if (!_engine->getSearchManager()->openFile(file, fileName)) { + warning("%s does not exist. String parsing failed", fileName.c_str()); + return; + } + uint lineNumber = 0; + while (!file.eos()) { + _lines[lineNumber] = readWideLine(file); + + lineNumber++; + assert(lineNumber <= NUM_TEXT_LINES); } } @@ -252,4 +269,8 @@ StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) { return _inGameText[stringNumber].fragments.front().style; } +const Common::String StringManager::getTextLine(uint stringNumber) { + return _lines[stringNumber]; +} + } // End of namespace ZVision diff --git a/engines/zvision/strings/string_manager.h b/engines/zvision/text/string_manager.h similarity index 88% rename from engines/zvision/strings/string_manager.h rename to engines/zvision/text/string_manager.h index af8324b890c2..5420e1f3ee6e 100644 --- a/engines/zvision/strings/string_manager.h +++ b/engines/zvision/text/string_manager.h @@ -52,6 +52,13 @@ class StringManager { Common::String text; }; + enum { + ZVISION_STR_SAVEEXIST = 23, + ZVISION_STR_SAVED = 4, + ZVISION_STR_SAVEEMPTY = 21, + ZVISION_STR_EXITPROMT = 6 + }; + private: struct InGameText { Common::List fragments; @@ -63,6 +70,8 @@ class StringManager { private: ZVision *_engine; + Common::String _lines[NUM_TEXT_LINES]; + InGameText _inGameText[NUM_TEXT_LINES]; Common::HashMap _fonts; @@ -71,8 +80,10 @@ class StringManager { public: void initialize(ZVisionGameId gameId); StringManager::TextStyle getTextStyle(uint stringNumber); + const Common::String getTextLine(uint stringNumber); private: + void loadStrFile(const Common::String &fileName); void parseStrFile(const Common::String &fileName); void parseTag(const Common::String &tagString, uint lineNumber); diff --git a/engines/zvision/text/text.cpp b/engines/zvision/text/text.cpp new file mode 100644 index 000000000000..872d6875b78b --- /dev/null +++ b/engines/zvision/text/text.cpp @@ -0,0 +1,549 @@ +/* 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 "zvision/text/text.h" + +#include "zvision/fonts/truetype_font.h" + +#include "common/file.h" +#include "common/tokenizer.h" +#include "common/debug.h" +#include "common/rect.h" + +#include "graphics/fontman.h" +#include "graphics/colormasks.h" +#include "graphics/surface.h" +#include "graphics/font.h" +#include "graphics/fonts/ttf.h" + +#include "zvision/graphics/render_manager.h" +#include "zvision/scripting/script_manager.h" + + +namespace ZVision { + +cTxtStyle::cTxtStyle() { + fontname = "Arial"; + blue = 255; + green = 255; + red = 255; + bold = false; + escapement = 0; + italic = false; + justify = TXT_JUSTIFY_LEFT; + newline = false; + size = 12; + skipcolor = false; + strikeout = false; + underline = false; + statebox = 0; + sharp = false; +} + +txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { + Common::String buf = Common::String(strin.c_str(), ln); + + int8 retval = TXT_RET_NOTHING; + + Common::StringTokenizer tokenizer(buf, " "); + Common::String token; + + while (!tokenizer.empty()) { + token = tokenizer.nextToken(); + + if (token.matchString("font", true)) { + token = tokenizer.nextToken(); + if (token[0] == '"') { + Common::String _tmp = Common::String(token.c_str() + 1); + + while (token.lastChar() != '"' && !tokenizer.empty()) { + token = tokenizer.nextToken(); + _tmp += " " + token; + } + + if (_tmp.lastChar() == '"') + _tmp.deleteLastChar(); + + fontname = _tmp; + } else { + if (!tokenizer.empty()) + fontname = token; + } + retval |= TXT_RET_FNTCHG; + + } else if (token.matchString("blue", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + int32 tmp = atoi(token.c_str()); + if (blue != tmp) { + blue = tmp; + retval |= TXT_RET_FNTSTL; + } + } + } else if (token.matchString("red", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + int32 tmp = atoi(token.c_str()); + if (red != tmp) { + red = tmp; + retval |= TXT_RET_FNTSTL; + } + } + } else if (token.matchString("green", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + int32 tmp = atoi(token.c_str()); + if (green != tmp) { + green = tmp; + retval |= TXT_RET_FNTSTL; + } + } + } else if (token.matchString("newline", true)) { + if ((retval & TXT_RET_NEWLN) == 0) + newline = 0; + + newline++; + retval |= TXT_RET_NEWLN; + } else if (token.matchString("point", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + int32 tmp = atoi(token.c_str()); + if (size != tmp) { + size = tmp; + retval |= TXT_RET_FNTCHG; + } + } + } else if (token.matchString("escapement", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + int32 tmp = atoi(token.c_str()); + escapement = tmp; + } + } else if (token.matchString("italic", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("on", true)) { + if (italic != true) { + italic = true; + retval |= TXT_RET_FNTSTL; + } + } else if (token.matchString("off", true)) { + if (italic != false) { + italic = false; + retval |= TXT_RET_FNTSTL; + } + } + } + } else if (token.matchString("underline", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("on", true)) { + if (underline != true) { + underline = true; + retval |= TXT_RET_FNTSTL; + } + } else if (token.matchString("off", true)) { + if (underline != false) { + underline = false; + retval |= TXT_RET_FNTSTL; + } + } + } + } else if (token.matchString("strikeout", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("on", true)) { + if (strikeout != true) { + strikeout = true; + retval |= TXT_RET_FNTSTL; + } + } else if (token.matchString("off", true)) { + if (strikeout != false) { + strikeout = false; + retval |= TXT_RET_FNTSTL; + } + } + } + } else if (token.matchString("bold", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("on", true)) { + if (bold != true) { + bold = true; + retval |= TXT_RET_FNTSTL; + } + } else if (token.matchString("off", true)) { + if (bold != false) { + bold = false; + retval |= TXT_RET_FNTSTL; + } + } + } + } else if (token.matchString("skipcolor", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("on", true)) { + skipcolor = true; + } else if (token.matchString("off", true)) { + skipcolor = false; + } + } + } else if (token.matchString("image", true)) { + // Not used + } else if (token.matchString("statebox", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + statebox = atoi(token.c_str()); + retval |= TXT_RET_HASSTBOX; + } + } else if (token.matchString("justify", true)) { + if (!tokenizer.empty()) { + token = tokenizer.nextToken(); + if (token.matchString("center", true)) + justify = TXT_JUSTIFY_CENTER; + else if (token.matchString("left", true)) + justify = TXT_JUSTIFY_LEFT; + else if (token.matchString("right", true)) + justify = TXT_JUSTIFY_RIGHT; + } + } + } + return (txtReturn)retval; +} + +void cTxtStyle::readAllStyle(const Common::String &txt) { + int16 startTextPosition = -1; + int16 endTextPosition = -1; + + for (uint16 i = 0; i < txt.size(); i++) { + if (txt[i] == '<') + startTextPosition = i; + else if (txt[i] == '>') { + endTextPosition = i; + if (startTextPosition != -1) + if ((endTextPosition - startTextPosition - 1) > 0) + parseStyle(Common::String(txt.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } + + } +} + +void cTxtStyle::setFontStyle(StyledTTFont &font) { + uint tempStyle = 0; + + if (bold) + tempStyle |= StyledTTFont::STTF_BOLD; + + if (italic) + tempStyle |= StyledTTFont::STTF_ITALIC; + + if (underline) + tempStyle |= StyledTTFont::STTF_UNDERLINE; + + if (strikeout) + tempStyle |= StyledTTFont::STTF_STRIKEOUT; + + if (sharp) + tempStyle |= StyledTTFont::STTF_SHARP; + + font.setStyle(tempStyle); +} + +void cTxtStyle::setFont(StyledTTFont &font) { + uint tempStyle = 0; + + if (bold) + tempStyle |= StyledTTFont::STTF_BOLD; + + if (italic) + tempStyle |= StyledTTFont::STTF_ITALIC; + + if (underline) + tempStyle |= StyledTTFont::STTF_UNDERLINE; + + if (strikeout) + tempStyle |= StyledTTFont::STTF_STRIKEOUT; + + if (sharp) + tempStyle |= StyledTTFont::STTF_SHARP; + + font.loadFont(fontname, size, tempStyle); +} + +Graphics::Surface *TextRenderer::render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style) { + style.setFontStyle(fnt); + uint32 clr = _engine->_pixelFormat.RGBToColor(style.red, style.green, style.blue); + return fnt.renderSolidText(txt, clr); +} + +void TextRenderer::drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify) { + if (justify == TXT_JUSTIFY_LEFT) + fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignLeft); + else if (justify == TXT_JUSTIFY_CENTER) + fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignCenter); + else if (justify == TXT_JUSTIFY_RIGHT) + fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignRight); +} + +int32 TextRenderer::drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst) { + StyledTTFont font(_engine); + fontStyle.setFont(font); + + dst.fillRect(Common::Rect(dst.w, dst.h), 0); + + uint32 clr = _engine->_pixelFormat.RGBToColor(fontStyle.red, fontStyle.green, fontStyle.blue); + + int16 w; + + w = font.getStringWidth(txt); + + drawTxtWithJustify(txt, font, clr, dst, 0, fontStyle.justify); + + return w; +} + +void TextRenderer::drawTxtInOneLine(const Common::String &text, Graphics::Surface &dst) { + const int16 TXT_CFG_TEXTURES_LINES = 256; // For now I don't want remake it + const int TXT_CFG_TEXTURES_PER_LINE = 6; + cTxtStyle style, style2; + int16 startTextPosition = -1; + int16 endTextPosition = -1; + int16 i = 0; + int16 dx = 0, dy = 0; + int16 textPixelWidth; + int16 textPosition = 0; + Common::String buf; + Common::String buf2; + + Graphics::Surface *TxtSurfaces[TXT_CFG_TEXTURES_LINES][TXT_CFG_TEXTURES_PER_LINE]; + int16 currentline = 0, currentlineitm = 0; + + int TxtJustify[TXT_CFG_TEXTURES_LINES]; + int TxtPoint[TXT_CFG_TEXTURES_LINES]; + + for (int16 k = 0; k < TXT_CFG_TEXTURES_LINES; k++) { + TxtPoint[k] = 0; + for (int j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) + TxtSurfaces[k][j] = NULL; + } + + int16 stringlen = text.size(); + + StyledTTFont font(_engine); + + style.setFont(font); + + int16 prevbufspace = 0, prevtxtspace = 0; + + while (i < stringlen) { + TxtJustify[currentline] = style.justify; + if (text[i] == '<') { + int16 ret = 0; + + startTextPosition = i; + while (i < stringlen && text[i] != '>') + i++; + endTextPosition = i; + if (startTextPosition != -1) + if ((endTextPosition - startTextPosition - 1) > 0) { + style2 = style; + ret = style.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } + + if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { + if (buf.size() > 0) { + textPixelWidth = font.getStringWidth(buf); + + TxtSurfaces[currentline][currentlineitm] = render(font, buf, style2); + TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + + currentlineitm++; + + buf.clear(); + prevbufspace = 0; + textPosition = 0; + dx += textPixelWidth; + + } + if (ret & TXT_RET_FNTCHG) { + style.setFont(font); + } + if (ret & TXT_RET_FNTSTL) + style.setFontStyle(font); + + if (ret & TXT_RET_NEWLN) { + currentline++; + currentlineitm = 0; + dx = 0; + } + } + + if (ret & TXT_RET_HASSTBOX) { + Common::String buf3; + buf3.format("%d", _engine->getScriptManager()->getStateValue(style.statebox)); + buf += buf3; + textPosition += buf3.size(); + } + + } else { + + buf += text[i]; + textPosition++; + + if (text[i] == ' ') { + prevbufspace = textPosition - 1; + prevtxtspace = i; + } + + if (font.isLoaded()) { + textPixelWidth = font.getStringWidth(buf); + if (textPixelWidth + dx > dst.w) { + if (prevbufspace == 0) { + prevtxtspace = i; + prevbufspace = textPosition - 1; + } + buf2 = Common::String(buf.c_str(), prevbufspace + 1); + + if (buf2.size() > 0) { + TxtSurfaces[currentline][currentlineitm] = render(font, buf2, style); + TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + } + + buf.clear(); + i = prevtxtspace; + prevbufspace = 0; + textPosition = 0; + currentline++; + currentlineitm = 0; + dx = 0; + } + } + } + i++; + } + + if (buf.size() > 0) { + TxtSurfaces[currentline][currentlineitm] = render(font, buf, style); + TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + } + + dy = 0; + for (i = 0; i <= currentline; i++) { + int16 j = 0; + int16 width = 0; + while (TxtSurfaces[i][j] != NULL) { + width += TxtSurfaces[i][j]->w; + j++; + } + dx = 0; + for (int32_t jj = 0; jj < j; jj++) { + if (TxtJustify[i] == TXT_JUSTIFY_LEFT) + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + + else if (TxtJustify[i] == TXT_JUSTIFY_CENTER) + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, ((dst.w - width) / 2) + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + + else if (TxtJustify[i] == TXT_JUSTIFY_RIGHT) + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dst.w - width + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + + dx += TxtSurfaces[i][jj]->w; + } + + dy += TxtPoint[i]; + } + + for (i = 0; i < TXT_CFG_TEXTURES_LINES; i++) + for (int32_t j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) + if (TxtSurfaces[i][j] != NULL) { + TxtSurfaces[i][j]->free(); + delete TxtSurfaces[i][j]; + } +} + +Common::String readWideLine(Common::SeekableReadStream &stream) { + Common::String asciiString; + + while (!stream.eos()) { + uint32 value = stream.readUint16LE(); + // Check for CRLF + if (value == 0x0A0D) { + // Read in the extra NULL char + stream.readByte(); // \0 + // End of the line. Break + break; + } + + // Crush each octet pair to a UTF-8 sequence + if (value < 0x80) { + asciiString += (char)(value & 0x7F); + } else if (value >= 0x80 && value < 0x800) { + asciiString += (char)(0xC0 | ((value >> 6) & 0x1F)); + asciiString += (char)(0x80 | (value & 0x3F)); + } else if (value >= 0x800 && value < 0x10000) { + asciiString += (char)(0xE0 | ((value >> 12) & 0xF)); + asciiString += (char)(0x80 | ((value >> 6) & 0x3F)); + asciiString += (char)(0x80 | (value & 0x3F)); + } else if (value >= 0x10000 && value < 0x200000) { + asciiString += (char)(0xF0); + asciiString += (char)(0x80 | ((value >> 12) & 0x3F)); + asciiString += (char)(0x80 | ((value >> 6) & 0x3F)); + asciiString += (char)(0x80 | (value & 0x3F)); + } + } + + return asciiString; +} + +int8 getUtf8CharSize(char chr) { + if ((chr & 0x80) == 0) + return 1; + else if ((chr & 0xE0) == 0xC0) + return 2; + else if ((chr & 0xF0) == 0xE0) + return 3; + else if ((chr & 0xF8) == 0xF0) + return 4; + else if ((chr & 0xFC) == 0xF8) + return 5; + else if ((chr & 0xFE) == 0xFC) + return 6; + + return 1; +} + +uint16 readUtf8Char(const char *chr) { + uint16 result = 0; + if ((chr[0] & 0x80) == 0) + result = chr[0]; + else if ((chr[0] & 0xE0) == 0xC0) + result = ((chr[0] & 0x1F) << 6) | (chr[1] & 0x3F); + else if ((chr[0] & 0xF0) == 0xE0) + result = ((chr[0] & 0x0F) << 12) | ((chr[1] & 0x3F) << 6) | (chr[2] & 0x3F); + else + result = chr[0]; + + return result; +} + +} // End of namespace ZVision diff --git a/engines/zvision/text/text.h b/engines/zvision/text/text.h new file mode 100644 index 000000000000..c2468383d3d3 --- /dev/null +++ b/engines/zvision/text/text.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 ZVISION_TEXT_H +#define ZVISION_TEXT_H + +#include "zvision/detection.h" +#include "zvision/fonts/truetype_font.h" +#include "zvision/zvision.h" + + +namespace Graphics { +class FontManager; +} + +namespace ZVision { + +class ZVision; + +enum txtJustify { + TXT_JUSTIFY_CENTER = 0, + TXT_JUSTIFY_LEFT = 1, + TXT_JUSTIFY_RIGHT = 2 +}; + +enum txtReturn { + TXT_RET_NOTHING = 0x0, + TXT_RET_FNTCHG = 0x1, + TXT_RET_FNTSTL = 0x2, + TXT_RET_NEWLN = 0x4, + TXT_RET_HASSTBOX = 0x8 +}; + +class cTxtStyle { +public: + cTxtStyle(); + txtReturn parseStyle(const Common::String &strin, int16 len); + void readAllStyle(const Common::String &txt); + void setFontStyle(StyledTTFont &font); + void setFont(StyledTTFont &font); + +public: + Common::String fontname; + txtJustify justify; // 0 - center, 1-left, 2-right + int16 size; + uint8 red; // 0-255 + uint8 green; // 0-255 + uint8 blue; // 0-255 + int8 newline; + int8 escapement; + bool italic; + bool bold; + bool underline; + bool strikeout; + bool skipcolor; + int32 statebox; + bool sharp; + // char image ?? +}; + +class TextRenderer { +public: + TextRenderer(ZVision *engine): _engine(engine) {}; + + void drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify); + int32 drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst); + Graphics::Surface *render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style); + void drawTxtInOneLine(const Common::String &txt, Graphics::Surface &dst); + +private: + ZVision *_engine; +}; + +Common::String readWideLine(Common::SeekableReadStream &stream); +int8 getUtf8CharSize(char chr); +uint16 readUtf8Char(const char *chr); + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/utility/clock.h b/engines/zvision/utility/clock.h index 6ae0f86161bb..9a50116a8cbb 100644 --- a/engines/zvision/utility/clock.h +++ b/engines/zvision/utility/clock.h @@ -52,13 +52,17 @@ class Clock { * * @return Delta time since the last frame (in milliseconds) */ - uint32 getDeltaTime() const { return _deltaTime; } + uint32 getDeltaTime() const { + return _deltaTime; + } /** * Get the time from the program starting to the last update() call * * @return Time from program start to last update() call (in milliseconds) */ - uint32 getLastMeasuredTime() { return _lastTime; } + uint32 getLastMeasuredTime() { + return _lastTime; + } /** * Pause the clock. Any future delta times will take this pause into account. diff --git a/engines/zvision/utility/lzss_read_stream.cpp b/engines/zvision/utility/lzss_read_stream.cpp index e094188ef665..14613a6fb219 100644 --- a/engines/zvision/utility/lzss_read_stream.cpp +++ b/engines/zvision/utility/lzss_read_stream.cpp @@ -28,10 +28,10 @@ namespace ZVision { LzssReadStream::LzssReadStream(Common::SeekableReadStream *source) - : _source(source), - // It's convention to set the starting cursor position to blockSize - 16 - _windowCursor(0x0FEE), - _eosFlag(false) { + : _source(source), + // It's convention to set the starting cursor position to blockSize - 16 + _windowCursor(0x0FEE), + _eosFlag(false) { // Clear the window to null memset(_window, 0, BLOCK_SIZE); } @@ -69,9 +69,9 @@ uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) } uint16 length = (high & 0xF) + 2; - uint16 offset = low | ((high & 0xF0)<<4); + uint16 offset = low | ((high & 0xF0) << 4); - for(int j = 0; j <= length; ++j) { + for (int j = 0; j <= length; ++j) { byte temp = _window[(offset + j) & 0xFFF]; _window[_windowCursor] = temp; destination[destinationCursor++] = temp; diff --git a/engines/zvision/utility/lzss_read_stream.h b/engines/zvision/utility/lzss_read_stream.h index b51cf3905f7a..b7ae5ac2cb07 100644 --- a/engines/zvision/utility/lzss_read_stream.h +++ b/engines/zvision/utility/lzss_read_stream.h @@ -64,7 +64,7 @@ class LzssReadStream : public Common::ReadStream { * * @param numberOfBytes How many bytes to decompress. This is a count of source bytes, not destination bytes */ - uint32 decompressBytes(byte* destination, uint32 numberOfBytes); + uint32 decompressBytes(byte *destination, uint32 numberOfBytes); }; } diff --git a/engines/zvision/utility/single_value_container.h b/engines/zvision/utility/single_value_container.h index 951383661afe..ac6e99039a41 100644 --- a/engines/zvision/utility/single_value_container.h +++ b/engines/zvision/utility/single_value_container.h @@ -60,7 +60,7 @@ class SingleValueContainer { explicit SingleValueContainer(Common::String value); // Copy constructor - explicit SingleValueContainer(const SingleValueContainer& other); + explicit SingleValueContainer(const SingleValueContainer &other); // Destructor ~SingleValueContainer(); @@ -91,7 +91,7 @@ class SingleValueContainer { SingleValueContainer &operator=(const double &rhs); SingleValueContainer &operator=(const Common::String &rhs); - SingleValueContainer& operator=(const SingleValueContainer &rhs); + SingleValueContainer &operator=(const SingleValueContainer &rhs); /** * Retrieve a bool from the container. If the container is not storing a diff --git a/engines/zvision/utility/utility.cpp b/engines/zvision/utility/utility.cpp index 2079d2373399..537f525bd4c7 100644 --- a/engines/zvision/utility/utility.cpp +++ b/engines/zvision/utility/utility.cpp @@ -39,7 +39,7 @@ void writeFileContentsToFile(const Common::String &sourceFile, const Common::Str return; } - byte* buffer = new byte[f.size()]; + byte *buffer = new byte[f.size()]; f.read(buffer, f.size()); Common::DumpFile dumpFile; @@ -63,10 +63,10 @@ void trimCommentsAndWhiteSpace(Common::String *string) { } void tryToDumpLine(const Common::String &key, - Common::String &line, - Common::HashMap *count, - Common::HashMap *fileAlreadyUsed, - Common::DumpFile &output) { + Common::String &line, + Common::HashMap *count, + Common::HashMap *fileAlreadyUsed, + Common::DumpFile &output) { const byte numberOfExamplesPerType = 8; if ((*count)[key] < numberOfExamplesPerType && !(*fileAlreadyUsed)[key]) { diff --git a/engines/zvision/utility/utility.h b/engines/zvision/utility/utility.h index 063d4c06630b..380034404a45 100644 --- a/engines/zvision/utility/utility.h +++ b/engines/zvision/utility/utility.h @@ -71,8 +71,8 @@ void removeDuplicateEntries(Common::Array &container) { uint newLength = 1; uint j; - for(uint i = 1; i < container.size(); i++) { - for(j = 0; j < newLength; j++) { + for (uint i = 1; i < container.size(); i++) { + for (j = 0; j < newLength; j++) { if (container[i] == container[j]) { break; } diff --git a/engines/zvision/utility/win_keys.cpp b/engines/zvision/utility/win_keys.cpp new file mode 100644 index 000000000000..86ed7c596f61 --- /dev/null +++ b/engines/zvision/utility/win_keys.cpp @@ -0,0 +1,129 @@ +/* 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/keyboard.h" + +namespace ZVision { + +uint8 VKkey(Common::KeyCode scummKeyCode) { + if (scummKeyCode >= Common::KEYCODE_a && scummKeyCode <= Common::KEYCODE_z) + return 0x41 + scummKeyCode - Common::KEYCODE_a; + if (scummKeyCode >= Common::KEYCODE_0 && scummKeyCode <= Common::KEYCODE_9) + return 0x30 + scummKeyCode - Common::KEYCODE_0; + if (scummKeyCode >= Common::KEYCODE_F1 && scummKeyCode <= Common::KEYCODE_F15) + return 0x70 + scummKeyCode - Common::KEYCODE_F1; + if (scummKeyCode >= Common::KEYCODE_KP0 && scummKeyCode <= Common::KEYCODE_KP9) + return 0x60 + scummKeyCode - Common::KEYCODE_KP0; + + switch (scummKeyCode) { + case Common::KEYCODE_BACKSPACE: + return 0x8; + case Common::KEYCODE_TAB: + return 0x9; + case Common::KEYCODE_CLEAR: + return 0xC; + case Common::KEYCODE_RETURN: + return 0xD; + case Common::KEYCODE_CAPSLOCK: + return 0x14; + case Common::KEYCODE_ESCAPE: + return 0x1B; + case Common::KEYCODE_SPACE: + return 0x20; + case Common::KEYCODE_PAGEUP: + return 0x21; + case Common::KEYCODE_PAGEDOWN: + return 0x22; + case Common::KEYCODE_END: + return 0x23; + case Common::KEYCODE_HOME: + return 0x24; + case Common::KEYCODE_LEFT: + return 0x25; + case Common::KEYCODE_UP: + return 0x26; + case Common::KEYCODE_RIGHT: + return 0x27; + case Common::KEYCODE_DOWN: + return 0x28; + case Common::KEYCODE_PRINT: + return 0x2A; + case Common::KEYCODE_INSERT: + return 0x2D; + case Common::KEYCODE_DELETE: + return 0x2E; + case Common::KEYCODE_HELP: + return 0x2F; + case Common::KEYCODE_KP_MULTIPLY: + return 0x6A; + case Common::KEYCODE_KP_PLUS: + return 0x6B; + case Common::KEYCODE_KP_MINUS: + return 0x6D; + case Common::KEYCODE_KP_PERIOD: + return 0x6E; + case Common::KEYCODE_KP_DIVIDE: + return 0x6F; + case Common::KEYCODE_NUMLOCK: + return 0x90; + case Common::KEYCODE_SCROLLOCK: + return 0x91; + case Common::KEYCODE_LSHIFT: + return 0xA0; + case Common::KEYCODE_RSHIFT: + return 0xA1; + case Common::KEYCODE_LCTRL: + return 0xA2; + case Common::KEYCODE_RCTRL: + return 0xA3; + case Common::KEYCODE_MENU: + return 0xA5; + case Common::KEYCODE_LEFTBRACKET: + return 0xDB; + case Common::KEYCODE_RIGHTBRACKET: + return 0xDD; + case Common::KEYCODE_SEMICOLON: + return 0xBA; + case Common::KEYCODE_BACKSLASH: + return 0xDC; + case Common::KEYCODE_QUOTE: + return 0xDE; + case Common::KEYCODE_SLASH: + return 0xBF; + case Common::KEYCODE_TILDE: + return 0xC0; + case Common::KEYCODE_COMMA: + return 0xBC; + case Common::KEYCODE_PERIOD: + return 0xBE; + case Common::KEYCODE_MINUS: + return 0xBD; + case Common::KEYCODE_PLUS: + return 0xBB; + default: + return 0; + } + + return 0; +} + +} diff --git a/engines/zvision/inventory/inventory_manager.h b/engines/zvision/utility/win_keys.h similarity index 84% rename from engines/zvision/inventory/inventory_manager.h rename to engines/zvision/utility/win_keys.h index f9d2ff294a7e..53d76c4d5fd2 100644 --- a/engines/zvision/inventory/inventory_manager.h +++ b/engines/zvision/utility/win_keys.h @@ -20,9 +20,13 @@ * */ -#ifndef ZVISION_INVENTORY_MANAGER_H -#define ZVISION_INVENTORY_MANAGER_H +#ifndef ZVISION_WIN_KEYS_H +#define ZVISION_WIN_KEYS_H -// TODO: Implement InventoryManager +#include "common/keyboard.h" + +namespace ZVision { +uint8 VKkey(Common::KeyCode scummKeyCode); +} // End of namespace ZVision #endif diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp index d1fff304084a..7a120df76b5b 100644 --- a/engines/zvision/video/video.cpp +++ b/engines/zvision/video/video.cpp @@ -8,12 +8,12 @@ * 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. @@ -26,6 +26,7 @@ #include "zvision/utility/clock.h" #include "zvision/graphics/render_manager.h" +#include "zvision/subtitles/subtitles.h" #include "common/system.h" @@ -38,90 +39,32 @@ namespace ZVision { -// Taken/modified from SCI -void scaleBuffer(const byte *src, byte *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint scaleAmount) { - assert(bytesPerPixel == 1 || bytesPerPixel == 2); - - const uint32 newWidth = srcWidth * scaleAmount; - const uint32 pitch = newWidth * bytesPerPixel; - const byte *srcPtr = src; - - if (bytesPerPixel == 1) { - for (uint32 y = 0; y < srcHeight; ++y) { - for (uint32 x = 0; x < srcWidth; ++x) { - const byte color = *srcPtr++; - - for (uint i = 0; i < scaleAmount; ++i) { - dst[i] = color; - dst[pitch + i] = color; - } - dst += scaleAmount; - } - dst += pitch; - } - } else if (bytesPerPixel == 2) { - for (uint32 y = 0; y < srcHeight; ++y) { - for (uint32 x = 0; x < srcWidth; ++x) { - const byte color = *srcPtr++; - const byte color2 = *srcPtr++; - - for (uint i = 0; i < scaleAmount; ++i) { - uint index = i *2; - - dst[index] = color; - dst[index + 1] = color2; - dst[pitch + index] = color; - dst[pitch + index + 1] = color2; - } - dst += 2 * scaleAmount; - } - dst += pitch; - } - } -} - -void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect, bool skippable) { - byte bytesPerPixel = videoDecoder.getPixelFormat().bytesPerPixel; - - uint16 origWidth = videoDecoder.getWidth(); - uint16 origHeight = videoDecoder.getHeight(); - - uint scale = 1; +void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, bool skippable, Subtitle *sub) { + Common::Rect dst = destRect; // If destRect is empty, no specific scaling was requested. However, we may choose to do scaling anyway - if (destRect.isEmpty()) { - // Most videos are very small. Therefore we do a simple 2x scale - if (origWidth * 2 <= 640 && origHeight * 2 <= 480) { - scale = 2; - } - } else { - // Assume bilinear scaling. AKA calculate the scale from just the width. - // Also assume that the scaling is in integral intervals. AKA no 1.5x scaling - // TODO: Test ^these^ assumptions - scale = destRect.width() / origWidth; - - // TODO: Test if we need to support downscale. - } + if (dst.isEmpty()) + dst = Common::Rect(vid.getWidth(), vid.getHeight()); - uint16 pitch = origWidth * bytesPerPixel; + Graphics::Surface *scaled = NULL; - uint16 finalWidth = origWidth * scale; - uint16 finalHeight = origHeight * scale; - - byte *scaledVideoFrameBuffer = 0; - if (scale != 1) { - scaledVideoFrameBuffer = new byte[finalWidth * finalHeight * bytesPerPixel]; + if (vid.getWidth() != dst.width() || vid.getHeight() != dst.height()) { + scaled = new Graphics::Surface; + scaled->create(dst.width(), dst.height(), vid.getPixelFormat()); } - uint16 x = ((WINDOW_WIDTH - finalWidth) / 2) + destRect.left; - uint16 y = ((WINDOW_HEIGHT - finalHeight) / 2) + destRect.top; + + uint16 x = _workingWindow.left + dst.left; + uint16 y = _workingWindow.top + dst.top; + uint16 finalWidth = dst.width() < _workingWindow.width() ? dst.width() : _workingWindow.width(); + uint16 finalHeight = dst.height() < _workingWindow.height() ? dst.height() : _workingWindow.height(); _clock.stop(); - videoDecoder.start(); + vid.start(); // Only continue while the video is still playing - while (!shouldQuit() && !videoDecoder.endOfVideo() && videoDecoder.isPlaying()) { + while (!shouldQuit() && !vid.endOfVideo() && vid.isPlaying()) { // Check for engine quit and video stop key presses - while (!videoDecoder.endOfVideo() && videoDecoder.isPlaying() && _eventMan->pollEvent(_event)) { + while (_eventMan->pollEvent(_event)) { switch (_event.type) { case Common::EVENT_KEYDOWN: switch (_event.kbd.keycode) { @@ -131,7 +74,7 @@ void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &d break; case Common::KEYCODE_SPACE: if (skippable) { - videoDecoder.stop(); + vid.stop(); } break; default: @@ -142,29 +85,32 @@ void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &d } } - if (videoDecoder.needsUpdate()) { - const Graphics::Surface *frame = videoDecoder.decodeNextFrame(); + if (vid.needsUpdate()) { + const Graphics::Surface *frame = vid.decodeNextFrame(); + if (sub) + sub->process(vid.getCurFrame()); if (frame) { - if (scale != 1) { - scaleBuffer((const byte *)frame->getPixels(), scaledVideoFrameBuffer, origWidth, origHeight, bytesPerPixel, scale); - _system->copyRectToScreen(scaledVideoFrameBuffer, pitch * 2, x, y, finalWidth, finalHeight); - } else { - _system->copyRectToScreen((const byte *)frame->getPixels(), pitch, x, y, finalWidth, finalHeight); + if (scaled) { + _renderManager->scaleBuffer(frame->getPixels(), scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, scaled->w, scaled->h); + frame = scaled; } + _system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, x, y, finalWidth, finalHeight); + _renderManager->processSubs(0); } } // Always update the screen so the mouse continues to render _system->updateScreen(); - _system->delayMillis(videoDecoder.getTimeToNextFrame()); + _system->delayMillis(vid.getTimeToNextFrame() / 2); } _clock.start(); - if (scale != 1) { - delete[] scaledVideoFrameBuffer; + if (scaled) { + scaled->free(); + delete scaled; } } diff --git a/engines/zvision/video/zork_avi_decoder.cpp b/engines/zvision/video/zork_avi_decoder.cpp index f22a4203ab1d..415a20d3f242 100644 --- a/engines/zvision/video/zork_avi_decoder.cpp +++ b/engines/zvision/video/zork_avi_decoder.cpp @@ -29,6 +29,7 @@ #include "common/stream.h" #include "audio/audiostream.h" +#include "audio/decoders/raw.h" namespace ZVision { @@ -42,11 +43,19 @@ void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *s if (_audStream) { if (_wvInfo.tag == kWaveFormatZorkPCM) { assert(_wvInfo.size == 8); - _audStream->queueAudioStream(makeRawZorkStream(stream, _wvInfo.samplesPerSec, _audStream->isStereo(), DisposeAfterUse::YES), DisposeAfterUse::YES); + RawChunkStream::RawChunk chunk = decoder->readNextChunk(stream); + delete stream; + + if (chunk.data) + _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO); + } else { + AVIAudioTrack::queueSound(stream); } - } else { - delete stream; } } +void ZorkAVIDecoder::ZorkAVIAudioTrack::resetStream() { + decoder->init(); +} + } // End of namespace ZVision diff --git a/engines/zvision/video/zork_avi_decoder.h b/engines/zvision/video/zork_avi_decoder.h index c47f007f9b3d..89c0d1e4b9ea 100644 --- a/engines/zvision/video/zork_avi_decoder.h +++ b/engines/zvision/video/zork_avi_decoder.h @@ -8,41 +8,53 @@ * 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 ZORK_AVI_DECODER_H #define ZORK_AVI_DECODER_H #include "video/avi_decoder.h" - +#include "zvision/sound/zork_raw.h" namespace ZVision { class ZorkAVIDecoder : public Video::AVIDecoder { public: ZorkAVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) : - Video::AVIDecoder(soundType) {} + Video::AVIDecoder(soundType) {} - virtual ~ZorkAVIDecoder() {} + virtual ~ZorkAVIDecoder() {} private: class ZorkAVIAudioTrack : public Video::AVIDecoder::AVIAudioTrack { public: ZorkAVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) : - Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType) {} - virtual ~ZorkAVIAudioTrack() {} + Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType), + decoder(NULL) { + if (_audStream) { + decoder = new RawChunkStream(_audStream->isStereo()); + } + } + virtual ~ZorkAVIAudioTrack() { + if (decoder) + delete decoder; + } void queueSound(Common::SeekableReadStream *stream); + void resetStream(); + private: + RawChunkStream *decoder; }; Video::AVIDecoder::AVIAudioTrack *createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo); @@ -50,7 +62,7 @@ class ZorkAVIDecoder : public Video::AVIDecoder { private: // Audio Codecs enum { - kWaveFormatZorkPCM = 17 // special Zork PCM audio format (clashes with MS IMA ADPCM) + kWaveFormatZorkPCM = 17 // special Zork PCM audio format (clashes with MS IMA ADPCM) }; }; diff --git a/engines/zvision/zvision.cpp b/engines/zvision/zvision.cpp index b464f6ee8171..62342f02b9bd 100644 --- a/engines/zvision/zvision.cpp +++ b/engines/zvision/zvision.cpp @@ -8,12 +8,12 @@ * 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. @@ -29,11 +29,17 @@ #include "zvision/graphics/render_manager.h" #include "zvision/cursors/cursor_manager.h" #include "zvision/core/save_manager.h" -#include "zvision/strings/string_manager.h" +#include "zvision/text/string_manager.h" #include "zvision/archives/zfs_archive.h" #include "zvision/detection.h" +#include "zvision/core/menu.h" +#include "zvision/core/search_manager.h" +#include "zvision/text/text.h" +#include "zvision/fonts/truetype_font.h" +#include "zvision/core/midi.h" #include "common/config-manager.h" +#include "common/str.h" #include "common/debug.h" #include "common/debug-channels.h" #include "common/textconsole.h" @@ -48,21 +54,55 @@ namespace ZVision { +#define ZVISION_SETTINGS_KEYS_COUNT 17 + +struct zvisionIniSettings { + const char *name; + int16 slot; + int16 deflt; +} settingsKeys[ZVISION_SETTINGS_KEYS_COUNT] = { + {"ZVision_KeyboardTurnSpeed", StateKey_KbdRotateSpeed, 5}, + {"ZVision_PanaRotateSpeed", StateKey_RotateSpeed, 540}, + {"ZVision_QSoundEnabled", StateKey_Qsound, 1}, + {"ZVision_VenusEnabled", StateKey_VenusEnable, 1}, + {"ZVision_HighQuality", StateKey_HighQuality, 1}, + {"ZVision_Platform", StateKey_Platform, 0}, + {"ZVision_InstallLevel", StateKey_InstallLevel, 0}, + {"ZVision_CountryCode", StateKey_CountryCode, 0}, + {"ZVision_CPU", StateKey_CPU, 1}, + {"ZVision_MovieCursor", StateKey_MovieCursor, 1}, + {"ZVision_NoAnimWhileTurning", StateKey_NoTurnAnim, 0}, + {"ZVision_Win958", StateKey_WIN958, 0}, + {"ZVision_ShowErrorDialogs", StateKey_ShowErrorDlg, 0}, + {"ZVision_ShowSubtitles", StateKey_Subtitles, 1}, + {"ZVision_DebugCheats", StateKey_DebugCheats, 0}, + {"ZVision_JapaneseFonts", StateKey_JapanFonts, 0}, + {"ZVision_Brightness", StateKey_Brightness, 0} +}; + ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) - : Engine(syst), - _gameDescription(gameDesc), - _workingWindow(gameDesc->gameId == GID_NEMESIS ? Common::Rect((WINDOW_WIDTH - ZNEM_WORKING_WINDOW_WIDTH) / 2, (WINDOW_HEIGHT - ZNEM_WORKING_WINDOW_HEIGHT) / 2, ((WINDOW_WIDTH - ZNEM_WORKING_WINDOW_WIDTH) / 2) + ZNEM_WORKING_WINDOW_WIDTH, ((WINDOW_HEIGHT - ZNEM_WORKING_WINDOW_HEIGHT) / 2) + ZNEM_WORKING_WINDOW_HEIGHT) : - Common::Rect((WINDOW_WIDTH - ZGI_WORKING_WINDOW_WIDTH) / 2, (WINDOW_HEIGHT - ZGI_WORKING_WINDOW_HEIGHT) / 2, ((WINDOW_WIDTH - ZGI_WORKING_WINDOW_WIDTH) / 2) + ZGI_WORKING_WINDOW_WIDTH, ((WINDOW_HEIGHT - ZGI_WORKING_WINDOW_HEIGHT) / 2) + ZGI_WORKING_WINDOW_HEIGHT)), - _pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /*RGB 565*/ - _desiredFrameTime(33), /* ~30 fps */ - _clock(_system), - _scriptManager(nullptr), - _renderManager(nullptr), - _saveManager(nullptr), - _stringManager(nullptr), - _cursorManager(nullptr) { + : Engine(syst), + _gameDescription(gameDesc), + _workingWindow_ZGI((WINDOW_WIDTH - WORKING_WINDOW_WIDTH) / 2, (WINDOW_HEIGHT - WORKING_WINDOW_HEIGHT) / 2, ((WINDOW_WIDTH - WORKING_WINDOW_WIDTH) / 2) + WORKING_WINDOW_WIDTH, ((WINDOW_HEIGHT - WORKING_WINDOW_HEIGHT) / 2) + WORKING_WINDOW_HEIGHT), + _workingWindow_ZNM((WINDOW_WIDTH - ZNM_WORKING_WINDOW_WIDTH) / 2, (WINDOW_HEIGHT - ZNM_WORKING_WINDOW_HEIGHT) / 2, ((WINDOW_WIDTH - ZNM_WORKING_WINDOW_WIDTH) / 2) + ZNM_WORKING_WINDOW_WIDTH, ((WINDOW_HEIGHT - ZNM_WORKING_WINDOW_HEIGHT) / 2) + ZNM_WORKING_WINDOW_HEIGHT), + _workingWindow(gameDesc->gameId == GID_NEMESIS ? _workingWindow_ZNM : _workingWindow_ZGI), + _pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /*RGB 565*/ + _desiredFrameTime(33), /* ~30 fps */ + _clock(_system), + _scriptManager(nullptr), + _renderManager(nullptr), + _saveManager(nullptr), + _stringManager(nullptr), + _cursorManager(nullptr), + _midiManager(nullptr), + _audioId(0), + _rendDelay(2), + _kbdVelocity(0), + _mouseVelocity(0) { debug(1, "ZVision::ZVision"); + + memset(_cheatBuff, 0, sizeof(_cheatBuff)); } ZVision::~ZVision() { @@ -76,39 +116,60 @@ ZVision::~ZVision() { delete _renderManager; delete _scriptManager; delete _rnd; + delete _midiManager; // Remove all of our debug levels DebugMan.clearAllDebugChannels(); } +void ZVision::registerDefaultSettings() { + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) + ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].deflt); + ConfMan.registerDefault("doublefps", false); +} + +void ZVision::loadSettings() { + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) + _scriptManager->setStateValue(settingsKeys[i].slot, ConfMan.getInt(settingsKeys[i].name)); + + if (getGameId() == GID_NEMESIS) + _scriptManager->setStateValue(StateKey_ExecScopeStyle, 1); + else + _scriptManager->setStateValue(StateKey_ExecScopeStyle, 0); +} + +void ZVision::saveSettings() { + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) + ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot)); + ConfMan.flushToDisk(); +} + void ZVision::initialize() { const Common::FSNode gameDataDir(ConfMan.get("path")); - // TODO: There are 10 file clashes when we flatten the directories. - // From a quick look, the files are exactly the same, so it shouldn't matter. - // But I'm noting it here just in-case it does become a problem. - SearchMan.addSubDirectoryMatching(gameDataDir, "data1", 0, 4, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "data2", 0, 4, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "data3", 0, 4, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "zassets1", 0, 2, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "zassets2", 0, 2, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "znemmx", 0, 1, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "zgi", 0, 4, true); - SearchMan.addSubDirectoryMatching(gameDataDir, "fonts", 0, 1, true); - - // Find zfs archive files - Common::ArchiveMemberList list; - SearchMan.listMatchingMembers(list, "*.zfs"); - - // Register the file entries within the zfs archives with the SearchMan - for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { - Common::String name = (*iter)->getName(); - Common::SeekableReadStream *stream = (*iter)->createReadStream(); - ZfsArchive *archive = new ZfsArchive(name, stream); - - delete stream; - - SearchMan.add(name, archive); - } + + _searchManager = new SearchManager(ConfMan.get("path"), 6); + + _searchManager->addDir("FONTS"); + _searchManager->addDir("addon"); + + if (_gameDescription->gameId == GID_GRANDINQUISITOR) { + _searchManager->loadZix("INQUIS.ZIX"); + _searchManager->addPatch("C000H01Q.RAW", "C000H01Q.SRC"); + _searchManager->addPatch("CM00H01Q.RAW", "CM00H01Q.SRC"); + _searchManager->addPatch("DM00H01Q.RAW", "DM00H01Q.SRC"); + _searchManager->addPatch("E000H01Q.RAW", "E000H01Q.SRC"); + _searchManager->addPatch("EM00H50Q.RAW", "EM00H50Q.SRC"); + _searchManager->addPatch("GJNPH65P.RAW", "GJNPH65P.SRC"); + _searchManager->addPatch("GJNPH72P.RAW", "GJNPH72P.SRC"); + _searchManager->addPatch("H000H01Q.RAW", "H000H01Q.SRC"); + _searchManager->addPatch("M000H01Q.RAW", "M000H01Q.SRC"); + _searchManager->addPatch("P000H01Q.RAW", "P000H01Q.SRC"); + _searchManager->addPatch("Q000H01Q.RAW", "Q000H01Q.SRC"); + _searchManager->addPatch("SW00H01Q.RAW", "SW00H01Q.SRC"); + _searchManager->addPatch("T000H01Q.RAW", "T000H01Q.SRC"); + _searchManager->addPatch("U000H01Q.RAW", "U000H01Q.SRC"); + } else if (_gameDescription->gameId == GID_NEMESIS) + _searchManager->loadZix("NEMESIS.ZIX"); initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_pixelFormat); @@ -117,18 +178,30 @@ void ZVision::initialize() { // Create managers _scriptManager = new ScriptManager(this); - _renderManager = new RenderManager(_system, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _pixelFormat); + _renderManager = new RenderManager(this, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _pixelFormat); _saveManager = new SaveManager(this); _stringManager = new StringManager(this); _cursorManager = new CursorManager(this, &_pixelFormat); + _textRenderer = new TextRenderer(this); + _midiManager = new MidiManager(); + + if (_gameDescription->gameId == GID_GRANDINQUISITOR) + _menu = new MenuZGI(this); + else + _menu = new MenuNemesis(this); // Initialize the managers _cursorManager->initialize(); _scriptManager->initialize(); _stringManager->initialize(_gameDescription->gameId); + registerDefaultSettings(); + + loadSettings(); + // Create debugger console. It requires GFX to be initialized _console = new Console(this); + _halveDelay = ConfMan.getBool("doublefps"); } Common::Error ZVision::run() { @@ -140,29 +213,111 @@ Common::Error ZVision::run() { uint32 currentTime = _clock.getLastMeasuredTime(); uint32 deltaTime = _clock.getDeltaTime(); + _cursorManager->setItemID(_scriptManager->getStateValue(StateKey_InventoryItem)); + processEvents(); + updateRotation(); // Call _renderManager->update() first so the background renders // before anything that puzzles/controls will render - _renderManager->update(deltaTime); _scriptManager->update(deltaTime); + _menu->process(deltaTime); // Render the backBuffer to the screen + _renderManager->prepareBkg(); + _renderManager->renderMenuToScreen(); + _renderManager->processSubs(deltaTime); _renderManager->renderBackbufferToScreen(); // Update the screen - _system->updateScreen(); + if (_rendDelay <= 0) + _system->updateScreen(); + else + _rendDelay--; // Calculate the frame delay based off a desired frame time int delay = _desiredFrameTime - int32(_system->getMillis() - currentTime); // Ensure non-negative delay = delay < 0 ? 0 : delay; + if (_halveDelay) + delay >>= 1; _system->delayMillis(delay); } return Common::kNoError; } +bool ZVision::askQuestion(const Common::String &str) { + uint16 msgid = _renderManager->createSubArea(); + _renderManager->updateSubArea(msgid, str); + _renderManager->processSubs(0); + _renderManager->renderBackbufferToScreen(); + _clock.stop(); + + int result = 0; + + while (result == 0) { + Common::Event evnt; + while (_eventMan->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN) { + switch (evnt.kbd.keycode) { + case Common::KEYCODE_y: + result = 2; + break; + case Common::KEYCODE_n: + result = 1; + break; + default: + break; + } + } + } + _system->updateScreen(); + if (_halveDelay) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + _renderManager->deleteSubArea(msgid); + _clock.start(); + return result == 2; +} + +void ZVision::delayedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = _renderManager->createSubArea(); + _renderManager->updateSubArea(msgid, str); + _renderManager->processSubs(0); + _renderManager->renderBackbufferToScreen(); + _clock.stop(); + + uint32 stopTime = _system->getMillis() + milsecs; + while (_system->getMillis() < stopTime) { + Common::Event evnt; + while (_eventMan->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN && + (evnt.kbd.keycode == Common::KEYCODE_SPACE || + evnt.kbd.keycode == Common::KEYCODE_RETURN || + evnt.kbd.keycode == Common::KEYCODE_ESCAPE)) + break; + } + _system->updateScreen(); + if (_halveDelay) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + _renderManager->deleteSubArea(msgid); + _clock.start(); +} + +void ZVision::timedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = _renderManager->createSubArea(); + _renderManager->updateSubArea(msgid, str); + _renderManager->processSubs(0); + _renderManager->renderBackbufferToScreen(); + _renderManager->deleteSubArea(msgid, milsecs); +} + void ZVision::pauseEngineIntern(bool pause) { _mixer->pauseAll(pause); @@ -181,4 +336,196 @@ Common::String ZVision::generateAutoSaveFileName() { return Common::String::format("%s.auto", _targetName.c_str()); } +void ZVision::setRenderDelay(uint delay) { + _rendDelay = delay; +} + +bool ZVision::canRender() { + return _rendDelay <= 0; +} + +void ZVision::updateRotation() { + int16 _velocity = _mouseVelocity + _kbdVelocity; + + if (_halveDelay) + _velocity /= 2; + + if (_velocity) { + RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + (_renderManager->getRenderTable()->getPanoramaReverse() ? -_velocity : _velocity); + + int16 zeroPoint = _renderManager->getRenderTable()->getPanoramaZeroPoint(); + if (startPosition >= zeroPoint && newPosition < zeroPoint) + _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) - 1); + if (startPosition <= zeroPoint && newPosition > zeroPoint) + _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) + 1); + + int16 screenWidth = _renderManager->getBkgSize().x; + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + _renderManager->setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + _velocity; + + int16 screenHeight = _renderManager->getBkgSize().y; + int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + _renderManager->setBackgroundPosition(newPosition); + } + } +} + +void ZVision::checkBorders() { + RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenWidth = _renderManager->getBkgSize().x; + + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + if (startPosition != newPosition) + _renderManager->setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenHeight = _renderManager->getBkgSize().y; + int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + if (startPosition != newPosition) + _renderManager->setBackgroundPosition(newPosition); + } +} + +void ZVision::rotateTo(int16 _toPos, int16 _time) { + if (_renderManager->getRenderTable()->getRenderState() != RenderTable::PANORAMA) + return; + + if (_time == 0) + _time = 1; + + int32 maxX = _renderManager->getBkgSize().x; + int32 curX = _renderManager->getCurrentBackgroundOffset(); + int32 dx = 0; + + if (curX == _toPos) + return; + + if (curX > _toPos) { + if (curX - _toPos > maxX / 2) + dx = (_toPos + (maxX - curX)) / _time; + else + dx = -(curX - _toPos) / _time; + } else { + if (_toPos - curX > maxX / 2) + dx = -((maxX - _toPos) + curX) / _time; + else + dx = (_toPos - curX) / _time; + } + + _clock.stop(); + + for (int16 i = 0; i <= _time; i++) { + if (i == _time) + curX = _toPos; + else + curX += dx; + + if (curX < 0) + curX = maxX - curX; + else if (curX >= maxX) + curX %= maxX; + + _renderManager->setBackgroundPosition(curX); + + _renderManager->prepareBkg(); + _renderManager->renderBackbufferToScreen(); + + _system->updateScreen(); + + _system->delayMillis(500 / _time); + } + + _clock.start(); +} + +void ZVision::menuBarEnable(uint16 menus) { + if (_menu) + _menu->setEnable(menus); +} + +uint16 ZVision::getMenuBarEnable() { + if (_menu) + return _menu->getEnable(); + return 0; +} + +bool ZVision::ifQuit() { + if (askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) { + quitGame(); + return true; + } + return false; +} + +void ZVision::pushKeyToCheatBuf(uint8 key) { + for (int i = 0; i < KEYBUF_SIZE - 1; i++) + _cheatBuff[i] = _cheatBuff[i + 1]; + + _cheatBuff[KEYBUF_SIZE - 1] = key; +} + +bool ZVision::checkCode(const char *code) { + int codeLen = strlen(code); + + if (codeLen > KEYBUF_SIZE) + return false; + + for (int i = 0; i < codeLen; i++) + if (code[i] != _cheatBuff[KEYBUF_SIZE - codeLen + i] && code[i] != '?') + return false; + + return true; +} + +uint8 ZVision::getBufferedKey(uint8 pos) { + if (pos >= KEYBUF_SIZE) + return 0; + else + return _cheatBuff[KEYBUF_SIZE - pos - 1]; +} + +void ZVision::showDebugMsg(const Common::String &msg, int16 delay) { + uint16 msgid = _renderManager->createSubArea(); + _renderManager->updateSubArea(msgid, msg); + _renderManager->deleteSubArea(msgid, delay); +} + } // End of namespace ZVision diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h index 0b5a86f3708a..8b14cfa178fa 100644 --- a/engines/zvision/zvision.h +++ b/engines/zvision/zvision.h @@ -8,24 +8,25 @@ * 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 ZVISION_ZVISION_H #define ZVISION_ZVISION_H -#include "zvision/core/console.h" #include "zvision/detection.h" #include "zvision/utility/clock.h" +#include "zvision/core/search_manager.h" #include "common/random.h" #include "common/events.h" @@ -44,12 +45,17 @@ class VideoDecoder; namespace ZVision { struct ZVisionGameDescription; +class Console; class ScriptManager; class RenderManager; class CursorManager; class StringManager; class SaveManager; class RlfAnimation; +class MenuHandler; +class TextRenderer; +class Subtitle; +class MidiManager; class ZVision : public Engine { public: @@ -62,7 +68,7 @@ class ZVision : public Engine { * are given in this coordinate space. Also, all images are clipped to the * edges of this Rectangle */ - const Common::Rect _workingWindow; + const Common::Rect &_workingWindow; const Graphics::PixelFormat _pixelFormat; private: @@ -71,15 +77,17 @@ class ZVision : public Engine { WINDOW_HEIGHT = 480, //Zork nemesis working window sizes - ZNEM_WORKING_WINDOW_WIDTH = 512, - ZNEM_WORKING_WINDOW_HEIGHT = 320, + ZNM_WORKING_WINDOW_WIDTH = 512, + ZNM_WORKING_WINDOW_HEIGHT = 320, //ZGI(and default) working window sizes - ZGI_WORKING_WINDOW_WIDTH = 640, - ZGI_WORKING_WINDOW_HEIGHT = 344, + WORKING_WINDOW_WIDTH = 640, + WORKING_WINDOW_HEIGHT = 344, ROTATION_SCREEN_EDGE_OFFSET = 60, - MAX_ROTATION_SPEED = 400 // Pixels per second + MAX_ROTATION_SPEED = 400, // Pixels per second + + KEYBUF_SIZE = 20 }; Console *_console; @@ -96,27 +104,65 @@ class ZVision : public Engine { CursorManager *_cursorManager; SaveManager *_saveManager; StringManager *_stringManager; + MenuHandler *_menu; + SearchManager *_searchManager; + TextRenderer *_textRenderer; + MidiManager *_midiManager; // Clock Clock _clock; + // Audio ID + int _audioId; + // To prevent allocation every time we process events Common::Event _event; + const Common::Rect _workingWindow_ZGI; + const Common::Rect _workingWindow_ZNM; + + int _rendDelay; + int16 _mouseVelocity; + int16 _kbdVelocity; + bool _halveDelay; + + uint8 _cheatBuff[KEYBUF_SIZE]; public: uint32 getFeatures() const; Common::Language getLanguage() const; Common::Error run(); void pauseEngineIntern(bool pause); - ScriptManager *getScriptManager() const { return _scriptManager; } - RenderManager *getRenderManager() const { return _renderManager; } - CursorManager *getCursorManager() const { return _cursorManager; } - SaveManager *getSaveManager() const { return _saveManager; } - StringManager *getStringManager() const { return _stringManager; } - Common::RandomSource *getRandomSource() const { return _rnd; } - ZVisionGameId getGameId() const { return _gameDescription->gameId; } - GUI::Debugger *getDebugger() { return _console; } + ScriptManager *getScriptManager() const { + return _scriptManager; + } + RenderManager *getRenderManager() const { + return _renderManager; + } + CursorManager *getCursorManager() const { + return _cursorManager; + } + SaveManager *getSaveManager() const { + return _saveManager; + } + StringManager *getStringManager() const { + return _stringManager; + } + SearchManager *getSearchManager() const { + return _searchManager; + } + TextRenderer *getTextRenderer() const { + return _textRenderer; + } + MidiManager *getMidiManager() const { + return _midiManager; + } + Common::RandomSource *getRandomSource() const { + return _rnd; + } + ZVisionGameId getGameId() const { + return _gameDescription->gameId; + } /** * Play a video until it is finished. This is a blocking call. It will call @@ -127,11 +173,31 @@ class ZVision : public Engine { * @param destRect Where to put the video. (In working window coords) * @param skippable If true, the video can be skipped at any time using [Spacebar] */ - void playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect = Common::Rect(0, 0, 0, 0), bool skippable = true); + void playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect = Common::Rect(0, 0, 0, 0), bool skippable = true, Subtitle *sub = NULL); + + void rotateTo(int16 to, int16 time); Common::String generateSaveFileName(uint slot); Common::String generateAutoSaveFileName(); + bool askQuestion(const Common::String &str); + void delayedMessage(const Common::String &str, uint16 milsecs); + void timedMessage(const Common::String &str, uint16 milsecs); + + void setRenderDelay(uint); + bool canRender(); + + void loadSettings(); + void saveSettings(); + + void menuBarEnable(uint16 menus); + uint16 getMenuBarEnable(); + + bool ifQuit(); + + void checkBorders(); + void showDebugMsg(const Common::String &msg, int16 delay = 3000); + private: void initialize(); void initFonts(); @@ -141,9 +207,16 @@ class ZVision : public Engine { /** Called every frame from ZVision::run() to process any events from EventMan */ void processEvents(); - void onMouseDown(const Common::Point &pos); - void onMouseUp(const Common::Point &pos); void onMouseMove(const Common::Point &pos); + void updateRotation(); + + void registerDefaultSettings(); + void shortKeys(Common::Event); + + void cheatCodes(uint8 key); + void pushKeyToCheatBuf(uint8 key); + bool checkCode(const char *code); + uint8 getBufferedKey(uint8 pos); }; } // End of namespace ZVision