diff options
Diffstat (limited to 'engines')
73 files changed, 7490 insertions, 2390 deletions
diff --git a/engines/zvision/animation/rlf_animation.cpp b/engines/zvision/animation/rlf_animation.cpp index 945461e7b2..5784df0dac 100644 --- a/engines/zvision/animation/rlf_animation.cpp +++ b/engines/zvision/animation/rlf_animation.cpp @@ -1,28 +1,28 @@ /* 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/animation/rlf_animation.h" +#include "zvision/rlf_animation.h" #include "common/str.h" #include "common/file.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), + _currentFrame(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), + _currentFrame(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,8 +193,11 @@ void RlfAnimation::seekToFrame(int frameNumber) { assert(!_stream); assert(frameNumber < (int)_frameCount || frameNumber >= -1); - if (frameNumber == -1) { - _currentFrame = -1; + if (_currentFrame == frameNumber) + return; + + if (frameNumber < 0) { + _currentFrame = 0; return; } @@ -166,13 +205,15 @@ void RlfAnimation::seekToFrame(int frameNumber) { 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)) { + if (newDistance < 0) + break; + if (newDistance > 0 && newDistance < distance) { closestFrame = _completeFrames[i]; distance = newDistance; } } - for (int i = closestFrame; i <= frameNumber; ++i) { + for (int i = closestFrame; i < frameNumber; ++i) { applyFrameToCurrent(i); } @@ -184,24 +225,24 @@ 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 == _currentFrame - 1) { return &_currentFrameBuffer; - } else if (_currentFrame + 1 == (int)frameNumber) { - return getNextFrame(); + } else if (_currentFrame == (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(_currentFrame < (int)_frameCount); if (_stream) { applyFrameToCurrent(readNextFrame()); } else { - applyFrameToCurrent(_currentFrame + 1); + applyFrameToCurrent(_currentFrame); } _currentFrame++; @@ -227,6 +268,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 +277,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 +294,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 +315,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 +324,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 +341,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 +356,8 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 uint16 sampleColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(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 +365,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 4bb779301b..dfb2a60109 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 @@ private: }; private: - Common::File _file; + Common::SeekableReadStream *_readStream; bool _stream; uint _lastFrameRead; @@ -68,10 +69,18 @@ private: 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 @@ public: /** * 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 @@ public: */ 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 _currentFrame == (int)_frameCount; + } private: /** diff --git a/engines/zvision/animation_node.cpp b/engines/zvision/animation_node.cpp new file mode 100644 index 0000000000..365ff747f4 --- /dev/null +++ b/engines/zvision/animation_node.cpp @@ -0,0 +1,211 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +#include "zvision/animation_node.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/script_manager.h" +#include "zvision/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) { + + if (fileName.hasSuffix(".rlf") || fileName.hasSuffix(".avi")) { + _animation = new MetaAnimation(fileName, engine); + _frmDelay = _animation->frameTime(); + } else { + warning("Unrecognized animation file type: %s", fileName.c_str()); + } + + 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->_cur_frm == -1) { // Start of new playlist node + nod->_cur_frm = nod->start; + + _animation->seekToFrame(nod->_cur_frm); + frame = _animation->decodeNextFrame(); + + nod->_delay = _frmDelay; + if (nod->slot) + _engine->getScriptManager()->setStateValue(nod->slot, 1); + } else { + nod->_cur_frm++; + + if (nod->_cur_frm > 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->_cur_frm = nod->start; + _animation->seekToFrame(nod->_cur_frm); + } + + 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 start_frame, int end_frame, int loops) { + playnode nod; + nod.loop = loops; + nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1); + nod.start = start_frame; + nod.stop = end_frame; + + if (nod.stop >= (int)_animation->frameCount()) + nod.stop = _animation->frameCount() - 1; + + nod.slot = slot; + nod._cur_frm = -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/animation_node.h b/engines/zvision/animation_node.h new file mode 100644 index 0000000000..ff7636a31f --- /dev/null +++ b/engines/zvision/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/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 _cur_frm; + int32 _delay; + Graphics::Surface *_scaled; + }; + +private: + typedef Common::List<playnode> 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 start_frame, int end_frame, int loops = 1); + + bool stop(); + + void setNewFrameDelay(int32 newDelay); + int32 getFrameDelay(); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/archives/zfs_archive.cpp b/engines/zvision/archives/zfs_archive.cpp index f5fa6fc9bf..ed4dcec1fe 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; } } @@ -79,7 +79,7 @@ void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) { // Read in each entry header for (uint32 i = 0; i < _header.filesPerBlock; ++i) { ZfsEntryHeader entryHeader; - + entryHeader.name = readEntryName(stream); entryHeader.offset = stream->readUint32LE(); entryHeader.id = stream->readUint32LE(); @@ -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 3509cfee26..44e2c4b240 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<Common::String, ZfsEntryHeader*, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ZfsEntryHeaderMap; +typedef Common::HashMap<Common::String, ZfsEntryHeader *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ZfsEntryHeaderMap; class ZfsArchive : public Common::Archive { public: diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp index e610f34474..252a4b75ef 100644 --- a/engines/zvision/core/console.cpp +++ b/engines/zvision/core/console.cpp @@ -1,37 +1,37 @@ /* 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/core/console.h" +#include "zvision/console.h" #include "zvision/zvision.h" -#include "zvision/scripting/script_manager.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/strings/string_manager.h" -#include "zvision/video/zork_avi_decoder.h" -#include "zvision/sound/zork_raw.h" -#include "zvision/utility/utility.h" -#include "zvision/cursors/cursor.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/string_manager.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/zork_raw.h" +#include "zvision/utility.h" +#include "zvision/cursor.h" #include "common/system.h" #include "common/file.h" @@ -45,34 +45,34 @@ namespace ZVision { Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { - registerCmd("loadimage", WRAP_METHOD(Console, cmdLoadImage)); - registerCmd("loadvideo", WRAP_METHOD(Console, cmdLoadVideo)); - registerCmd("loadsound", WRAP_METHOD(Console, cmdLoadSound)); - registerCmd("raw2wav", WRAP_METHOD(Console, cmdRawToWav)); - registerCmd("setrenderstate", WRAP_METHOD(Console, cmdSetRenderState)); - registerCmd("generaterendertable", WRAP_METHOD(Console, cmdGenerateRenderTable)); - registerCmd("setpanoramafov", WRAP_METHOD(Console, cmdSetPanoramaFoV)); - registerCmd("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale)); - registerCmd("changelocation", WRAP_METHOD(Console, cmdChangeLocation)); - registerCmd("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); - registerCmd("parseallscrfiles", WRAP_METHOD(Console, cmdParseAllScrFiles)); - registerCmd("rendertext", WRAP_METHOD(Console, cmdRenderText)); + DCmd_Register("loadimage", WRAP_METHOD(Console, cmdLoadImage)); + DCmd_Register("loadvideo", WRAP_METHOD(Console, cmdLoadVideo)); + DCmd_Register("loadsound", WRAP_METHOD(Console, cmdLoadSound)); + DCmd_Register("raw2wav", WRAP_METHOD(Console, cmdRawToWav)); + DCmd_Register("setrenderstate", WRAP_METHOD(Console, cmdSetRenderState)); + DCmd_Register("generaterendertable", WRAP_METHOD(Console, cmdGenerateRenderTable)); + DCmd_Register("setpanoramafov", WRAP_METHOD(Console, cmdSetPanoramaFoV)); + DCmd_Register("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale)); + DCmd_Register("changelocation", WRAP_METHOD(Console, cmdChangeLocation)); + DCmd_Register("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); + DCmd_Register("parseallscrfiles", WRAP_METHOD(Console, cmdParseAllScrFiles)); + DCmd_Register("rendertext", WRAP_METHOD(Console, cmdRenderText)); } 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 <fileName> <destinationX> <destinationY> 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 <fileName> <destinationX> <destinationY> to load an image to the screen\n"); +// return true; +// } return true; } bool Console::cmdLoadVideo(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Use loadvideo <fileName> to load a video to the screen\n"); + DebugPrintf("Use loadvideo <fileName> to load a video to the screen\n"); return true; } @@ -86,7 +86,7 @@ bool Console::cmdLoadVideo(int argc, const char **argv) { bool Console::cmdLoadSound(int argc, const char **argv) { if (!Common::File::exists(argv[1])) { - debugPrintf("File does not exist\n"); + DebugPrintf("File does not exist\n"); return true; } @@ -105,7 +105,7 @@ bool Console::cmdLoadSound(int argc, const char **argv) { Audio::SoundHandle handle; _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); } else { - debugPrintf("Use loadsound <fileName> [<rate> <isStereo: 1 or 0>] to load a sound\n"); + DebugPrintf("Use loadsound <fileName> [<rate> <isStereo: 1 or 0>] to load a sound\n"); return true; } @@ -114,7 +114,7 @@ bool Console::cmdLoadSound(int argc, const char **argv) { bool Console::cmdRawToWav(int argc, const char **argv) { if (argc != 3) { - debugPrintf("Use raw2wav <rawFilePath> <wavFileName> to dump a .RAW file to .WAV\n"); + DebugPrintf("Use raw2wav <rawFilePath> <wavFileName> to dump a .RAW file to .WAV\n"); return true; } @@ -124,7 +124,7 @@ bool Console::cmdRawToWav(int argc, const char **argv) { bool Console::cmdSetRenderState(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Use setrenderstate <RenderState: panorama, tilt, flat> to change the current render state\n"); + DebugPrintf("Use setrenderstate <RenderState: panorama, tilt, flat> to change the current render state\n"); return true; } @@ -137,7 +137,7 @@ bool Console::cmdSetRenderState(int argc, const char **argv) { else if (renderState.matchString("flat", true)) _engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); else - debugPrintf("Use setrenderstate <RenderState: panorama, tilt, flat> to change the current render state\n"); + DebugPrintf("Use setrenderstate <RenderState: panorama, tilt, flat> to change the current render state\n"); return true; } @@ -150,7 +150,7 @@ bool Console::cmdGenerateRenderTable(int argc, const char **argv) { bool Console::cmdSetPanoramaFoV(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Use setpanoramafov <fieldOfView> to change the current panorama field of view\n"); + DebugPrintf("Use setpanoramafov <fieldOfView> to change the current panorama field of view\n"); return true; } @@ -161,7 +161,7 @@ bool Console::cmdSetPanoramaFoV(int argc, const char **argv) { bool Console::cmdSetPanoramaScale(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Use setpanoramascale <scale> to change the current panorama scale\n"); + DebugPrintf("Use setpanoramascale <scale> to change the current panorama scale\n"); return true; } @@ -172,7 +172,7 @@ bool Console::cmdSetPanoramaScale(int argc, const char **argv) { bool Console::cmdChangeLocation(int argc, const char **argv) { if (argc != 6) { - debugPrintf("Use changelocation <char: world> <char: room> <char:node> <char:view> <int: x position> to change your location\n"); + DebugPrintf("Use changelocation <char: world> <char: room> <char:node> <char:view> <int: x position> to change your location\n"); return true; } @@ -183,7 +183,7 @@ bool Console::cmdChangeLocation(int argc, const char **argv) { bool Console::cmdDumpFile(int argc, const char **argv) { if (argc != 2) { - debugPrintf("Use dumpfile <fileName> to dump a file\n"); + DebugPrintf("Use dumpfile <fileName> to dump a file\n"); 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; @@ -205,12 +204,12 @@ bool Console::cmdParseAllScrFiles(int argc, const char **argv) { bool Console::cmdRenderText(int argc, const char **argv) { if (argc != 7) { - debugPrintf("Use rendertext <text> <fontNumber> <destX> <destY> <maxWidth> <1 or 0: wrap> to render text\n"); + DebugPrintf("Use rendertext <text> <fontNumber> <destX> <destY> <maxWidth> <1 or 0: wrap> to render text\n"); 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 83d6c17dec..189bf007c1 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. @@ -24,11 +24,12 @@ #include "zvision/zvision.h" -#include "zvision/core/console.h" -#include "zvision/cursors/cursor_manager.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/scripting/script_manager.h" -#include "zvision/animation/rlf_animation.h" +#include "zvision/console.h" +#include "zvision/cursor_manager.h" +#include "zvision/render_manager.h" +#include "zvision/script_manager.h" +#include "zvision/rlf_animation.h" +#include "zvision/menu.h" #include "common/events.h" #include "common/system.h" @@ -43,17 +44,28 @@ void ZVision::processEvents() { while (_eventMan->pollEvent(_event)) { switch (_event.type) { case Common::EVENT_LBUTTONDOWN: + _cursorManager->cursorDown(true); + _scriptManager->setStateValue(StateKey_LMouse, 1); onMouseDown(_event.mouse); break; case Common::EVENT_LBUTTONUP: + _cursorManager->cursorDown(false); + _scriptManager->setStateValue(StateKey_LMouse, 0); onMouseUp(_event.mouse); break; case Common::EVENT_RBUTTONDOWN: + _cursorManager->cursorDown(true); + _scriptManager->setStateValue(StateKey_RMouse, 1); // TODO: Inventory logic break; + case Common::EVENT_RBUTTONUP: + _cursorManager->cursorDown(false); + _scriptManager->setStateValue(StateKey_RMouse, 0); + break; + case Common::EVENT_MOUSEMOVE: onMouseMove(_event.mouse); break; @@ -87,23 +99,24 @@ void ZVision::processEvents() { } void ZVision::onMouseDown(const Common::Point &pos) { - _cursorManager->cursorDown(true); + _menu->onMouseDown(pos); Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos)); _scriptManager->onMouseDown(pos, imageCoord); } void ZVision::onMouseUp(const Common::Point &pos) { - _cursorManager->cursorDown(false); + _menu->onMouseUp(pos); 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 +149,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; + _velocity = (((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; + _velocity = (((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); + _velocity = 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; + _velocity = (((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; + _velocity = (((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); + _velocity = 0; } + } else { + _velocity = 0; } } else { - _renderManager->setBackgroundVelocity(0); + _velocity = 0; } if (!cursorWasChanged) { - _cursorManager->revertToIdle(); + _cursorManager->changeCursor(CursorIndex_Idle); } } diff --git a/engines/zvision/core/menu.h b/engines/zvision/core/menu.h index 3ab6d4c2ec..7be03f62ef 100644 --- a/engines/zvision/core/menu.h +++ b/engines/zvision/core/menu.h @@ -23,6 +23,98 @@ #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/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) {}; +protected: + uint16 menu_bar_flag; + 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 item_id[50]; + + Graphics::Surface *magic[12][2]; + uint magic_id[12]; + + int menu_mousefocus; + bool inmenu; + + int mouse_on_item; + + bool scrolled[3]; + float scrollPos[3]; + + enum { + menu_ITEM = 0, + menu_MAGIC = 1, + menu_MAIN = 2 + }; + + bool clean; + bool redraw; + +}; + +class menuNem: public menuHandler { +public: + menuNem(ZVision *engine); + ~menuNem(); + 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 mouse_on_item; + + bool scrolled; + float scrollPos; + + bool redraw; + + int frm; + int16 delay; + +}; + +} #endif diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/core/save_manager.cpp index 07fb7637e7..8ec4f4d628 100644 --- a/engines/zvision/core/save_manager.cpp +++ b/engines/zvision/core/save_manager.cpp @@ -42,44 +42,27 @@ 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; } @@ -87,26 +70,29 @@ void SaveManager::saveGame(uint slot, const Common::String &saveName) { void SaveManager::autoSave() { Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); + writeSaveGameHeader(file, "auto"); - // Version - file->writeByte(SAVE_VERSION); - - file->writeString("auto"); - file->writeByte(0); - - 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); - + // Write out the save date/time TimeDate td; g_system->getTimeAndDate(td); @@ -115,28 +101,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,31 +118,63 @@ 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(); + 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; } - + // Read in the version header.version = in->readByte(); @@ -203,4 +206,29 @@ 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 standart save file + Common::String filename; + if (_engine->getGameId() == GID_GRANDINQUISITOR) + filename.format("inqsav%u.sav", slot); + else if (_engine->getGameId() == GID_NEMESIS) + filename.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; +} + } // End of namespace ZVision diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/core/save_manager.h index 43fb0c0faf..8ed64a3fdc 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; @@ -54,6 +55,7 @@ private: static const uint32 SAVEGAME_ID; enum { + SAVE_ORIGINAL = 0, SAVE_VERSION = 1 }; @@ -73,6 +75,7 @@ public: * @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); /** * Loads the state data from the save file that slot references. Uses * ZVision::generateSaveFileName(slot) to get the save file name. @@ -80,10 +83,12 @@ public: * @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); 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/cursors/cursor.cpp b/engines/zvision/cursors/cursor.cpp index 9b9b9a3f71..e2e3d7ee72 100644 --- a/engines/zvision/cursors/cursor.cpp +++ b/engines/zvision/cursors/cursor.cpp @@ -1,28 +1,28 @@ /* 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/cursors/cursor.h" +#include "zvision/cursor.h" #include "common/str.h" #include "common/file.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 be9fae64da..57db561655 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 @@ private: 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 7f70c8b4e3..c66fa650a8 100644 --- a/engines/zvision/cursors/cursor_manager.cpp +++ b/engines/zvision/cursors/cursor_manager.cpp @@ -1,28 +1,28 @@ /* 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/cursors/cursor_manager.h" +#include "zvision/cursor_manager.h" #include "zvision/zvision.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(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 0576517f58..0521a17337 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. @@ -23,7 +23,7 @@ #ifndef ZVISION_CURSOR_MANAGER_H #define ZVISION_CURSOR_MANAGER_H -#include "zvision/cursors/cursor.h" +#include "zvision/cursor.h" #include "common/str.h" @@ -37,6 +37,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 * or debug console. @@ -47,17 +62,17 @@ public: 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 @@ public: 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(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 @@ public: */ 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 9961db1215..5ad2e7d977 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -133,22 +133,22 @@ public: 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 { @@ -173,23 +173,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 +209,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 +230,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 +265,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 ba4d72bde8..e3eea403b6 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. @@ -22,14 +22,16 @@ #include "common/scummsys.h" -#include "zvision/fonts/truetype_font.h" +#include "zvision/truetype_font.h" #include "zvision/zvision.h" -#include "zvision/graphics/render_manager.h" +#include "zvision/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) { @@ -110,4 +115,230 @@ Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, u return surface; } +sTTFont::sTTFont(ZVision *engine) { + _engine = engine; + _style = 0; + _font = NULL; + _lineHeight = 0; +} + +sTTFont::~sTTFont() { + if (_font) + delete _font; +} + +bool sTTFont::loadFont(const Common::String &fontName, int32 point, uint style) { + _style = style; + return loadFont(fontName, point); +} + +bool sTTFont::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 2.5: 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); // 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); // 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 sTTFont::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 sTTFont::getFontHeight() { + if (_font) + return _font->getFontHeight(); + return 0; +} + +int sTTFont::getMaxCharWidth() { + if (_font) + return _font->getMaxCharWidth(); + return 0; +} + +int sTTFont::getCharWidth(byte chr) { + if (_font) + return _font->getCharWidth(chr); + return 0; +} + +int sTTFont::getKerningOffset(byte left, byte right) { + if (_font) + return _font->getKerningOffset(left, right); + return 0; +} + +void sTTFont::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 sTTFont::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 sTTFont::getStringWidth(const Common::String &str) { + if (_font) + return _font->getStringWidth(str); + return 0; +} + +Graphics::Surface *sTTFont::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 64f53a2c3b..bed1d5513e 100644 --- a/engines/zvision/fonts/truetype_font.h +++ b/engines/zvision/fonts/truetype_font.h @@ -76,6 +76,49 @@ public: Graphics::Surface *drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap); }; +// Styled TTF +class sTTFont { +public: + sTTFont(ZVision *engine); + ~sTTFont(); + + 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/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp index aed30ea12c..071affffab 100644 --- a/engines/zvision/graphics/render_manager.cpp +++ b/engines/zvision/graphics/render_manager.cpp @@ -1,30 +1,32 @@ /* 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/graphics/render_manager.h" +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/text.h" -#include "zvision/utility/lzss_read_stream.h" +#include "zvision/lzss_read_stream.h" #include "common/file.h" #include "common/system.h" @@ -32,202 +34,183 @@ #include "engines/util.h" -#include "image/tga.h" +#include "graphics/decoders/tga.h" 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); + _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; - } -} - -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++; - } - - // Choose the direction of movement using the sign of the velocity - moveBackground(_backgroundInverseVelocity < 0 ? -numberOfSteps : numberOfSteps); - } + _curBkg.free(); + _wrkWnd.free(); + _outWnd.free(); } 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()); - } + Graphics::Surface *out = &_outWnd; - // 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); + RenderTable::RenderState state = _renderTable.getRenderState(); + if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { + if (!_wrkWndDirtyRect.isEmpty()) { + _renderTable.mutateImage(&_outWnd, &_wrkWnd); + out = &_outWnd; + _outWndDirtyRect = Common::Rect(_wrkWidth, _wrkHeight); } - - // Clear the dirty rect - _workingWindowDirtyRect = Common::Rect(); + } else { + out = &_wrkWnd; + _outWndDirtyRect = _wrkWndDirtyRect; } - // TODO: Add menu rendering - // Render alpha entries - processAlphaEntries(); + 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()); - if (!_backBufferDirtyRect.isEmpty()) { - _system->copyRectToScreen(_backBuffer.getBasePtr(_backBufferDirtyRect.left, _backBufferDirtyRect.top), _backBuffer.pitch, _backBufferDirtyRect.left, _backBufferDirtyRect.top, _backBufferDirtyRect.width(), _backBufferDirtyRect.height()); - _backBufferDirtyRect = Common::Rect(); + _outWndDirtyRect = Common::Rect(); } } -void RenderManager::processAlphaEntries() { - // TODO: Add dirty rectangling support. AKA only draw an entry if the _backbufferDirtyRect intersects/contains the entry Rect - - 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(); +void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); - 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; - } - } + blitSurfaceToBkg(surface, destX, destY); + surface.free(); +} - destOffset += _backBuffer.w; - sourceOffset += iter->_value.width; - } +void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 keycolor) { + Graphics::Surface surface; + readImageToSurface(fileName, surface); - 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; + Graphics::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; } @@ -238,9 +221,8 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: uint32 imageWidth; uint32 imageHeight; - Image::TGADecoder tga; + Graphics::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 +261,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 +275,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 +300,495 @@ 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 (newPoint.x < 0) + newPoint.x += _bkgWidth; + if (newPoint.y < 0) + newPoint.y += _bkgHeight; - if (_workingWindowDirtyRect.isEmpty()) { - _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height); + return newPoint; } else { - _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height)); + return Common::Point(0, 0); } +} - // 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); +RenderTable *RenderManager::getRenderTable() { + return &_renderTable; } -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; +void RenderManager::setBackgroundImage(const Common::String &fileName) { + readImageToSurface(fileName, _curBkg); + _bkgWidth = _curBkg.w; + _bkgHeight = _curBkg.h; + _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); +} - uint32 sourceOffset = 0; - uint32 destOffset = 0; - uint16 *surfacePtr = (uint16 *)entry.data->getPixels(); +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; +} - for (int32 y = 0; y < height; ++y) { - for (int32 x = 0; x < width; ++x) { - surfacePtr[destOffset + x] = buffer[sourceOffset + x]; - } +uint32 RenderManager::getCurrentBackgroundOffset() { + RenderTable::RenderState state = _renderTable.getRenderState(); - destOffset += width; - sourceOffset += imageWidth; + if (state == RenderTable::PANORAMA) { + return _bkgOff; + } else if (state == RenderTable::TILT) { + return _bkgOff; + } else { + return 0; } - - _alphaDataEntries[idNumber] = entry; } -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; +Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { + Graphics::Surface *tranposedSurface = new Graphics::Surface(); + tranposedSurface->create(surface->h, surface->w, surface->format); - // 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; + const uint16 *source = (const uint16 *)surface->getPixels(); + uint16 *dest = (uint16 *)tranposedSurface->getPixels(); - _alphaDataEntries[idNumber] = entry; + for (uint32 y = 0; y < tranposedSurface->h; ++y) { + uint32 columnIndex = y * tranposedSurface->w; - return Common::Rect(destX, destY, destX + entry.width, destY + entry.height); + for (uint32 x = 0; x < tranposedSurface->w; ++x) { + dest[columnIndex + x] = source[x * surface->w + y]; + } + } + + return tranposedSurface; } -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::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight) { + assert(bytesPerPixel == 1 || bytesPerPixel == 2); - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint); - } + const float xscale = (float)srcWidth / (float)dstWidth; + const float yscale = (float)srcHeight / (float)dstHeight; - if (state == RenderTable::PANORAMA) { - newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset); - } else if (state == RenderTable::TILT) { - newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset); + 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++; + } + } } +} - 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; +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) { - return newPoint; -} + if (src.format != dst.format) + return; -const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) { - Common::Point newPoint(point); + 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); - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::PANORAMA) { - newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset); - } else if (state == RenderTable::TILT) { - newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset); - } + if (srcRect.isEmpty() || !srcRect.isValidRect()) + return; + + // Copy srcRect from src surface to dst surface + const byte *src_buf = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + + int xx = _x; + int yy = _y; + + if (xx < 0) + xx = 0; + if (yy < 0) + yy = 0; - return newPoint; + if (_x >= dst.w || _y >= dst.h) + return; + + byte *dst_buf = (byte *)dst.getBasePtr(xx, yy); + + int32 w = srcRect.width(); + int32 h = srcRect.height(); + + for (int32 y = 0; y < h; y++) { + memcpy(dst_buf, src_buf, w * src.format.bytesPerPixel); + src_buf += src.pitch; + dst_buf += dst.pitch; + } } -bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) { - if (!_workingWindow.contains(rect)) { - return false; +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 *src_buf = (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 *dst_buf = (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 *src_tmp = (const uint *)src_buf; + uint *dst_tmp = (uint *)dst_buf; + for (int32 x = 0; x < w; x++) { + if (*src_tmp != _keycolor) + *dst_tmp = *src_tmp; + src_tmp++; + dst_tmp++; + } + } + break; + + case 2: { + const uint16 *src_tmp = (const uint16 *)src_buf; + uint16 *dst_tmp = (uint16 *)dst_buf; + for (int32 x = 0; x < w; x++) { + if (*src_tmp != _keycolor) + *dst_tmp = *src_tmp; + src_tmp++; + dst_tmp++; + } + } + break; + + case 4: { + const uint32 *src_tmp = (const uint32 *)src_buf; + uint32 *dst_tmp = (uint32 *)dst_buf; + for (int32 x = 0; x < w; x++) { + if (*src_tmp != _keycolor) + *dst_tmp = *src_tmp; + src_tmp++; + dst_tmp++; + } + } + break; + + default: + break; + } + src_buf += src.pitch; + dst_buf += dst.pitch; } +} - // 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; +void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y) { + Common::Rect empt; + blitSurfaceToSurface(src, empt, dst, x, y); } -RenderTable *RenderManager::getRenderTable() { - return &_renderTable; +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); } -void RenderManager::setBackgroundImage(const Common::String &fileName) { - readImageToSurface(fileName, _currentBackground); +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); +} - moveBackground(0); +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); } -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::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::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; - } - } else { - _backgroundInverseVelocity = 1000 / velocity; - } +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); } -void RenderManager::moveBackground(int offset) { - RenderTable::RenderState state = _renderTable.getRenderState(); - if (state == RenderTable::TILT) { - _backgroundOffset += Common::Point(0, offset); +Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) { + Common::Rect dst = rect; + dst.clip(_bkgWidth, _bkgHeight); - _backgroundOffset.y = CLIP<int16>(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY); + if (dst.isEmpty() || !dst.isValidRect()) + return NULL; - renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true); - } else if (state == RenderTable::PANORAMA) { - _backgroundOffset += Common::Point(offset, 0); + Graphics::Surface *srf = new Graphics::Surface; + srf->create(dst.width(), dst.height(), _curBkg.format); - if (_backgroundOffset.x <= -_backgroundWidth) - _backgroundOffset.x += _backgroundWidth; - else if (_backgroundOffset.x >= _backgroundWidth) - _backgroundOffset.x -= _backgroundWidth; + srf->copyRectToSurface(_curBkg, 0, 0, Common::Rect(dst)); - renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true); - } else { - renderImageToScreen(_currentBackground, 0, 0); - } + return srf; } -uint32 RenderManager::getCurrentBackgroundOffset() { +Graphics::Surface *RenderManager::loadImage(Common::String &file) { + Graphics::Surface *tmp = new Graphics::Surface; + readImageToSurface(file, *tmp); + return tmp; +} + +Graphics::Surface *RenderManager::loadImage(const char *file) { + Common::String str = Common::String(file); + return loadImage(str); +} + +Graphics::Surface *RenderManager::loadImage(Common::String &file, bool transposed) { + Graphics::Surface *tmp = new Graphics::Surface; + readImageToSurface(file, *tmp, transposed); + return tmp; +} + +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) { - return _backgroundOffset.x; + 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) { - return _backgroundOffset.y; + 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 { - return 0; + if (!_bkgDirtyRect.isEmpty()) + blitSurfaceToSurface(_curBkg, _bkgDirtyRect, _wrkWnd, _bkgDirtyRect.left, _bkgDirtyRect.top); + _wrkWndDirtyRect = _bkgDirtyRect; + } + + _bkgDirtyRect = Common::Rect(); + + _wrkWndDirtyRect.clip(_wrkWidth, _wrkHeight); +} + +void RenderManager::clearMenuSurface() { + _menuWndDirtyRect = Common::Rect(0, 0, _menuWnd.w, _menuWnd.h); + _menuWnd.fillRect(_menuWndDirtyRect, 0); +} + +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(); } } -Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) { - Graphics::Surface *tranposedSurface = new Graphics::Surface(); - tranposedSurface->create(surface->h, surface->w, surface->format); +uint16 RenderManager::createSubArea(const Common::Rect &area) { + _subid++; - const uint16 *source = (const uint16 *)surface->getPixels(); - uint16 *dest = (uint16 *)tranposedSurface->getPixels(); + oneSub sub; + sub.redraw = false; + sub.timer = -1; + sub.todelete = false; + sub._r = area; - for (uint32 y = 0; y < tranposedSurface->h; ++y) { - uint32 columnIndex = y * tranposedSurface->w; + _subsList[_subid] = sub; - for (uint32 x = 0; x < tranposedSurface->w; ++x) { - dest[columnIndex + x] = source[x * surface->w + y]; + return _subid; +} + +void RenderManager::deleteSubArea(uint16 id) { + if (_subsList.contains(id)) + _subsList[id].todelete = true; +} + +void RenderManager::deleteSubArea(uint16 id, int16 delay) { + if (_subsList.contains(id)) + _subsList[id].timer = delay; +} + +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::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; } } - return tranposedSurface; + 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; + } + + _system->copyRectToScreen(_subWnd.getPixels(), _subWnd.pitch, + _subWndRect.left, + _subWndRect.top, + _subWnd.w, + _subWnd.h); + } +} + +Common::Point RenderManager::getBkgSize() { + return Common::Point(_bkgWidth, _bkgHeight); } } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h index 9feff4c030..668c8acc41 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. @@ -23,8 +23,8 @@ #ifndef ZVISION_RENDER_MANAGER_H #define ZVISION_RENDER_MANAGER_H -#include "zvision/graphics/render_table.h" -#include "zvision/fonts/truetype_font.h" +#include "zvision/render_table.h" +#include "zvision/truetype_font.h" #include "common/rect.h" #include "common/hashmap.h" @@ -47,43 +47,71 @@ 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<uint32, AlphaDataEntry> AlphaEntryMap; +// struct AlphaDataEntry { +// Graphics::Surface *data; +// uint16 alphaColor; +// uint16 destX; +// uint16 destY; +// uint16 width; +// uint16 height; +// }; +// + typedef Common::HashMap<uint16, oneSub> subMap; private: + ZVision *_engine; OSystem *_system; const Graphics::PixelFormat _pixelFormat; + // A buffer for blitting background image to working window + Graphics::Surface _wrkWnd; + + Common::Rect _wrkWndDirtyRect; + + Graphics::Surface _outWnd; + + Common::Rect _outWndDirtyRect; + + Common::Rect _bkgDirtyRect; + + Graphics::Surface _subWnd; + + Common::Rect _subWndDirtyRect; + + Graphics::Surface _menuWnd; + + Common::Rect _menuWndDirtyRect; + + // 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; + //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; + //Graphics::Surface _backBuffer; // A list of Alpha Entries that need to be blitted to the backbuffer - AlphaEntryMap _alphaDataEntries; + //AlphaEntryMap _alphaDataEntries; // A rectangle representing the portion of the working window where the pixels have been changed since last frame - Common::Rect _workingWindowDirtyRect; + //Common::Rect _workingWindowDirtyRect; // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame - Common::Rect _backBufferDirtyRect; + //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 +123,36 @@ private: * edges of this Rectangle */ const Common::Rect _workingWindow; + + Common::Rect _subWndRect; + + Common::Rect _menuWndRect; + /** Used to warp the background image */ RenderTable _renderTable; - Graphics::Surface _currentBackground; + 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; + + uint16 _subid; + + subMap _subsList; /** * 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; + //int _backgroundInverseVelocity; /** Holds any 'leftover' milliseconds between frames */ - uint _accumulatedVelocityMilliseconds; + //uint _accumulatedVelocityMilliseconds; 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 +160,23 @@ public: 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 - * - * @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 - */ - 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); - - /** - * Fills the entire workingWindow with the specified color. Internally, the color - * will be converted to RGB 565 and then blitted. - * - * @param color The color to fill the working window with. (In RGB 555) - */ - void clearWorkingWindowTo555Color(uint16 color); - - /** - * 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! */ - void renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap = false); + void renderImageToBackground(const Common::String &fileName, int16 destinationX, int16 destinationY); /** - * 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 stream Surface to read the image data from + * @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 colorkey Transparent color */ - void renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap = false); + void renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 colorkey); /** * Sets the current background image to be used by the RenderManager and immediately @@ -234,41 +199,15 @@ public: 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 * * @param point Point in screen coordinate space * @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); RenderTable *getRenderTable(); uint32 getCurrentBackgroundOffset(); - const Graphics::Surface *getBackBuffer() { return &_backBuffer; } /** * Creates a copy of surface and transposes the data. @@ -281,21 +220,37 @@ public: */ 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); + void scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight); + + + 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); + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y); + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey); + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y); + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey); + + uint16 createSubArea(const Common::Rect &area); + void deleteSubArea(uint16 id); + void deleteSubArea(uint16 id, int16 delay); + void updateSubArea(uint16 id, const Common::String &txt); + void processSubs(uint16 deltatime); + + Common::Point getBkgSize(); + + Graphics::Surface *getBkgRect(Common::Rect &rect); + 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); + + void clearMenuSurface(); + void clearMenuSurface(const Common::Rect &r); + void renderMenuToScreen(); + + void prepareBkg(); /** * Reads an image file pixel data into a Surface buffer. In the process @@ -308,19 +263,7 @@ private: * @param destination A reference to the Surface to store the pixel data in */ 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. - * - * @param offset The amount to move the background - */ - void moveBackground(int offset); + void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed); }; } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp index 49b934dc37..942420c700 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]; @@ -97,12 +97,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 +123,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: diff --git a/engines/zvision/graphics/render_table.h b/engines/zvision/graphics/render_table.h index f066187ad1..0b4d6e3919 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 { @@ -59,12 +60,15 @@ private: } _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); diff --git a/engines/zvision/inventory_manager.cpp b/engines/zvision/inventory_manager.cpp new file mode 100644 index 0000000000..3924c6b51a --- /dev/null +++ b/engines/zvision/inventory_manager.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/script_manager.h" + + +namespace ZVision { + +int8 ScriptManager::invertory_getCount() { + return getStateValue(StateKey_Inv_Cnt_Slot); +} + +void ScriptManager::invertory_setCount(int8 cnt) { + setStateValue(StateKey_Inv_Cnt_Slot, cnt); +} + +int16 ScriptManager::invertory_getItem(int8 id) { + if (id < 49 && id >= 0) + return getStateValue(StateKey_Inv_1_Slot + id); + return -1; +} + +void ScriptManager::invertory_setItem(int8 id, int16 item) { + if (id < 49 && id >= 0) + setStateValue(StateKey_Inv_1_Slot + id, item); +} + +void ScriptManager::invertory_add(int16 item) { + int8 cnt = invertory_getCount(); + + if (cnt < 49) { + bool not_exist = true; + + if (cnt == 0) { + invertory_setItem(0, 0); + invertory_setCount(1); // we needed empty item for cycle code + cnt = 1; + } + + for (int8 cur = 0; cur < cnt; cur++) + if (invertory_getItem(cur) == item) { + not_exist = false; + break; + } + + if (not_exist) { + for (int8 i = cnt; i > 0; i--) + invertory_setItem(i, invertory_getItem(i - 1)); + + invertory_setItem(0, item); + + setStateValue(StateKey_InventoryItem, item); + + invertory_setCount(cnt + 1); + } + } +} + +void ScriptManager::invertory_drop(int16 item) { + int8 items_cnt = invertory_getCount(); + + // if items in inventory > 0 + if (items_cnt != 0) { + int8 index = 0; + + // finding needed item + while (index < items_cnt) { + if (invertory_getItem(index) == item) + break; + + index++; + } + + // if item in the inventory + if (items_cnt != index) { + // shift all items left with rewrite founded item + for (int8 v = index; v < items_cnt - 1 ; v++) + invertory_setItem(v, invertory_getItem(v + 1)); + + // del last item + invertory_setItem(items_cnt - 1, 0); + invertory_setCount(invertory_getCount() - 1); + + setStateValue(StateKey_InventoryItem, invertory_getItem(0)); + } + } +} +void ScriptManager::invertory_cycle() { + int8 item_cnt = invertory_getCount(); + int8 cur_item = invertory_getItem(0); + if (item_cnt > 1) { + for (int8 i = 0; i < item_cnt - 1; i++) + invertory_setItem(i, invertory_getItem(i + 1)); + + invertory_setItem(item_cnt - 1, cur_item); + + setStateValue(StateKey_InventoryItem, invertory_getItem(0)); + + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/menu.cpp b/engines/zvision/menu.cpp new file mode 100644 index 0000000000..bf422b300f --- /dev/null +++ b/engines/zvision/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/menu.h" + +#include "zvision/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; + menu_bar_flag = 0xFFFF; +} + +menuZgi::menuZgi(ZVision *engine) : + menuHandler(engine) { + menu_mousefocus = -1; + inmenu = false; + scrolled[0] = false; + scrolled[1] = false; + scrolled[2] = false; + scrollPos[0] = 0.0; + scrollPos[1] = 0.0; + scrollPos[2] = 0.0; + mouse_on_item = -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; + item_id[i] = 0; + } + + for (int i = 0; i < 12; i++) { + magic[i][0] = NULL; + magic[i][1] = NULL; + magic_id[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 (menu_mousefocus) { + case menu_ITEM: + if (menu_bar_flag & menuBar_Items) { + int item_count = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (item_count == 0) + item_count = 20; + + for (int i = 0; i < item_count; i++) { + int itemspace = (600 - 28) / item_count; + + if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, + scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + int32 mouse_item = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + if (mouse_item >= 0 && mouse_item < 0xE0) { + _engine->getScriptManager()->invertory_drop(mouse_item); + _engine->getScriptManager()->invertory_add(_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i)); + _engine->getScriptManager()->setStateValue(SLOT_START_SLOT + i, mouse_item); + + redraw = true; + } + } + } + } + break; + + case menu_MAGIC: + if (menu_bar_flag & 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 (menu_bar_flag & menuBar_Exit) + if (Common::Rect(320 + 135, + scrollPos[menu_MAIN], + 320 + 135 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + // ifquit(); + } + + // Settings + if (menu_bar_flag & 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 (menu_bar_flag & 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 (menu_bar_flag & 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 (menu_mousefocus) { + case menu_ITEM: + if (menu_bar_flag & menuBar_Items) { + int item_count = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (item_count == 0) + item_count = 20; + else if (item_count > 50) + item_count = 50; + + int last_item = mouse_on_item; + + mouse_on_item = -1; + + for (int i = 0; i < item_count; i++) { + int itemspace = (600 - 28) / item_count; + + if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, + scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + mouse_on_item = i; + break; + } + } + + if (last_item != mouse_on_item) + if (_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + mouse_on_item) || + _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + last_item)) + redraw = true; + } + break; + + case menu_MAGIC: + if (menu_bar_flag & menuBar_Magic) { + int last_item = mouse_on_item; + mouse_on_item = -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)) { + mouse_on_item = i; + break; + } + } + + if (last_item != mouse_on_item) + if (_engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + mouse_on_item) || + _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + last_item)) + redraw = true; + + } + break; + + case menu_MAIN: { + int last_item = mouse_on_item; + mouse_on_item = -1; + + // Exit + if (menu_bar_flag & menuBar_Exit) + if (Common::Rect(320 + 135, + scrollPos[menu_MAIN], + 320 + 135 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_EXIT; + } + + // Settings + if (menu_bar_flag & menuBar_Settings) + if (Common::Rect(320 , + scrollPos[menu_MAIN], + 320 + 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_PREF; + } + + // Load + if (menu_bar_flag & menuBar_Restore) + if (Common::Rect(320 - 135, + scrollPos[menu_MAIN], + 320, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_REST; + } + + // Save + if (menu_bar_flag & menuBar_Save) + if (Common::Rect(320 - 135 * 2, + scrollPos[menu_MAIN], + 320 - 135, + scrollPos[menu_MAIN] + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_SAVE; + } + + if (last_item != mouse_on_item) + redraw = true; + } + break; + + default: + int cur_menu = menu_mousefocus; + if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main + menu_mousefocus = 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 (menu_bar_flag & menuBar_Magic) + if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic + menu_mousefocus = menu_MAGIC; + scrolled[menu_MAGIC] = false; + scrollPos[menu_MAGIC] = 28; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3); + } + + if (menu_bar_flag & menuBar_Items) + if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items + menu_mousefocus = menu_ITEM; + scrolled[menu_ITEM] = false; + scrollPos[menu_ITEM] = 28 - 600; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1); + } + + if (cur_menu != menu_mousefocus) + clean = true; + + break; + } + } else { + if (inmenu) + clean = true; + inmenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + menu_mousefocus = -1; + } +} + +void menuZgi::process(uint32 deltatime) { + if (clean) { + _engine->getRenderManager()->clearMenuSurface(); + clean = false; + } + switch (menu_mousefocus) { + case menu_ITEM: + if (menu_bar_flag & 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 item_count = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (item_count == 0) + item_count = 20; + else if (item_count > 50) + item_count = 50; + + + for (int i = 0; i < item_count; i++) { + int itemspace = (600 - 28) / item_count; + + bool inrect = false; + + if (mouse_on_item == i) + inrect = true; + + uint cur_item_id = _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i); + + if (cur_item_id != 0) { + if (item_id[i] != cur_item_id) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", cur_item_id); + items[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", cur_item_id); + items[i][1] = _engine->getRenderManager()->loadImage(buf, false); + item_id[i] = cur_item_id; + } + + 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; + } + item_id[i] = 0; + } + } + + redraw = false; + } + break; + + case menu_MAGIC: + if (menu_bar_flag & 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 (mouse_on_item == i) + inrect = true; + + uint cur_item_id = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + if (cur_item_id) { + if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + cur_item_id = 0xEE + i; + else + cur_item_id = 0xE0 + i; + } + + if (cur_item_id != 0) { + if (item_id[i] != cur_item_id) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", cur_item_id); + magic[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", cur_item_id); + magic[i][1] = _engine->getRenderManager()->loadImage(buf, false); + magic_id[i] = cur_item_id; + } + + 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; + } + magic_id[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 (menu_bar_flag & menuBar_Exit) { + if (mouse_on_item == 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 (menu_bar_flag & menuBar_Settings) { + if (mouse_on_item == 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 (menu_bar_flag & menuBar_Restore) { + if (mouse_on_item == 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 (menu_bar_flag & menuBar_Save) { + if (mouse_on_item == 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 (menu_bar_flag & menuBar_Items) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][1], 0, 0); + + if (menu_bar_flag & menuBar_Magic) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][1], 640 - 28, 0); + } + redraw = false; + } + break; + } +} + + +menuNem::menuNem(ZVision *engine) : + menuHandler(engine) { + inmenu = false; + scrolled = false; + scrollPos = 0.0; + mouse_on_item = -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; +} + +menuNem::~menuNem() { + 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 menuNem::onMouseUp(const Common::Point &Pos) { + if (Pos.y < 40) { + // Exit + if (menu_bar_flag & menuBar_Exit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + // ifquit(); + frm = 5; + redraw = true; + } + + // Settings + if (menu_bar_flag & 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 (menu_bar_flag & 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 (menu_bar_flag & 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 menuNem::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 last_item = mouse_on_item; + mouse_on_item = -1; + + // Exit + if (menu_bar_flag & menuBar_Exit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_EXIT; + } + + // Settings + if (menu_bar_flag & menuBar_Settings) + if (Common::Rect(buts[2][1], + scrollPos, + buts[2][0] + buts[2][1], + scrollPos + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_PREF; + } + + // Load + if (menu_bar_flag & menuBar_Restore) + if (Common::Rect(buts[1][1], + scrollPos, + buts[1][0] + buts[1][1], + scrollPos + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_REST; + } + + // Save + if (menu_bar_flag & menuBar_Save) + if (Common::Rect(buts[0][1], + scrollPos, + buts[0][0] + buts[0][1], + scrollPos + 32).contains(Pos)) { + mouse_on_item = menu_MAIN_SAVE; + } + + if (last_item != mouse_on_item) { + redraw = true; + frm = 0; + delay = 200; + } + } else { + inmenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + mouse_on_item = -1; + } +} + +void menuNem::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 (mouse_on_item != -1) { + delay -= deltatime; + if (delay <= 0 && frm < 4) { + delay = 200; + frm++; + redraw = true; + } + } + + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); + + if (menu_bar_flag & menuBar_Exit) + if (mouse_on_item == menu_MAIN_EXIT) + _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos); + + if (menu_bar_flag & menuBar_Settings) + if (mouse_on_item == menu_MAIN_PREF) + _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos); + + if (menu_bar_flag & menuBar_Restore) + if (mouse_on_item == menu_MAIN_REST) + _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos); + + if (menu_bar_flag & menuBar_Save) + if (mouse_on_item == 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/meta_animation.cpp b/engines/zvision/meta_animation.cpp new file mode 100644 index 0000000000..33ae92a523 --- /dev/null +++ b/engines/zvision/meta_animation.cpp @@ -0,0 +1,131 @@ +/* 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/meta_animation.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/script_manager.h" +#include "zvision/rlf_animation.h" +#include "zvision/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), + _cur_frame(NULL) { + if (fileName.hasSuffix(".rlf")) { + _fileType = RLF; + Common::File *_file = engine->getSearchManager()->openFile(fileName); + _animation.rlf = new RlfAnimation(_file, false); + _frmDelay = _animation.rlf->frameTime(); + } else if (fileName.hasSuffix(".avi")) { + _fileType = AVI; + Common::File *_file = engine->getSearchManager()->openFile(fileName); + _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) + _cur_frame = _animation.rlf->decodeNextFrame(); + else + _cur_frame = _animation.avi->decodeNextFrame(); + + return _cur_frame; +} + +const Graphics::Surface *MetaAnimation::getFrameData(uint frameNumber) { + if (frameNumber >= frameCount()) + frameNumber = frameCount() - 1; + + if (_fileType == RLF) { + _cur_frame = _animation.rlf->getFrameData(frameNumber); + return _cur_frame; + } else { + _animation.avi->seekToFrame(frameNumber); + _cur_frame = _animation.avi->decodeNextFrame(); + return _cur_frame; + } +} + +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/meta_animation.h index 6c4d6dfcf7..3754aae058 100644 --- a/engines/zvision/scripting/controls/animation_control.h +++ b/engines/zvision/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/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 _cur_frm; + int32 _delay; + Graphics::Surface *_scaled; + }; private: enum FileType { @@ -55,31 +69,29 @@ private: }; private: - uint32 _animationKey; - union { RlfAnimation *rlf; Video::VideoDecoder *avi; } _animation; FileType _fileType; - uint _loopCount; - int32 _x; - int32 _y; + int32 _frmDelay; - uint _accumulatedTime; - uint _currentLoop; - - Graphics::Surface *_cachedFrame; - bool _cachedFrameNeedsDeletion; + const Graphics::Surface *_cur_frame; 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/module.mk b/engines/zvision/module.mk index 2e298f24c6..d6d0dd7b5a 100644 --- a/engines/zvision/module.mk +++ b/engines/zvision/module.mk @@ -1,43 +1,54 @@ MODULE := engines/zvision - + MODULE_OBJS := \ - animation/rlf_animation.o \ - archives/zfs_archive.o \ - core/console.o \ - core/events.o \ - core/save_manager.o \ - cursors/cursor.o \ - cursors/cursor_manager.o \ + actions.o \ + animation_node.o \ + clock.o \ + console.o \ + control.o \ + cursor.o \ + cursor_manager.o \ detection.o \ - fonts/truetype_font.o \ - graphics/render_manager.o \ - graphics/render_table.o \ - scripting/actions.o \ - scripting/control.o \ - scripting/controls/animation_control.o \ - scripting/controls/input_control.o \ - scripting/controls/lever_control.o \ - scripting/controls/push_toggle_control.o \ - scripting/controls/timer_node.o \ - scripting/scr_file_handling.o \ - scripting/script_manager.o \ - sound/zork_raw.o \ - strings/string_manager.o \ - utility/clock.o \ - utility/lzss_read_stream.o \ - utility/single_value_container.o \ - utility/utility.o \ - video/video.o \ - video/zork_avi_decoder.o \ - zvision.o - + events.o \ + input_control.o \ + lever_control.o \ + lzss_read_stream.o \ + push_toggle_control.o \ + render_manager.o \ + render_table.o \ + rlf_animation.o \ + save_manager.o \ + scr_file_handling.o \ + script_manager.o \ + single_value_container.o \ + string_manager.o \ + timer_node.o \ + truetype_font.o \ + utility.o \ + video.o \ + zvision.o \ + zfs_archive.o \ + zork_avi_decoder.o \ + zork_raw.o \ + sidefx.o \ + music_node.o \ + inventory_manager.o \ + slot_control.o \ + menu.o \ + meta_animation.o \ + search_manager.o \ + text.o \ + subtitles.o \ + syncsound_node.o \ + ttytext_node.o + MODULE_DIRS += \ engines/zvision - + # This module can be built as a plugin ifeq ($(ENABLE_ZVISION), DYNAMIC_PLUGIN) PLUGIN := 1 endif - -# Include common rules + +# Include common rules include $(srcdir)/rules.mk diff --git a/engines/zvision/music_node.cpp b/engines/zvision/music_node.cpp new file mode 100644 index 0000000000..b716b3ead1 --- /dev/null +++ b/engines/zvision/music_node.cpp @@ -0,0 +1,190 @@ +/* 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/music_node.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/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) + : SideFX(engine, key, SIDEFX_AUDIO) { + _loop = loop; + _volume = volume; + _crossfade = false; + _crossfade_target = 0; + _crossfade_time = 0; + _attenuate = 0; + _pantrack = false; + _pantrack_X = 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; + _pantrack_X = pos; + setVolume(_volume); + } +} + +void MusicNode::unsetPanTrack() { + _pantrack = false; + setVolume(_volume); +} + +void MusicNode::setFade(int32 time, uint8 target) { + _crossfade_target = target; + _crossfade_time = time; + _crossfade = true; +} + +bool MusicNode::process(uint32 deltaTimeInMillis) { + if (! _engine->_mixer->isSoundHandleActive(_handle)) + return stop(); + else { + uint8 _newvol = _volume; + + if (_crossfade) { + if (_crossfade_time > 0) { + if ((int32)deltaTimeInMillis > _crossfade_time) + deltaTimeInMillis = _crossfade_time; + _newvol += floor(((float)(_crossfade_target - _newvol) / (float)_crossfade_time)) * (float)deltaTimeInMillis; + _crossfade_time -= deltaTimeInMillis; + } else { + _crossfade = false; + _newvol = _crossfade_target; + } + } + + if (_pantrack || _volume != _newvol) + setVolume(_newvol); + + if (_sub) + _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); + } + return false; +} + +void MusicNode::setVolume(uint8 new_volume) { + if (_pantrack) { + int cur_x = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + cur_x -= _pantrack_X; + int32 _width = _engine->getRenderManager()->getBkgSize().x; + if (cur_x < (-_width) / 2) + cur_x += _width; + else if (cur_x >= _width / 2) + cur_x -= _width; + + float norm = (float)cur_x / ((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, new_volume * lvl); + } + } else { + if (_engine->_mixer->isSoundHandleActive(_handle)) { + _engine->_mixer->setChannelBalance(_handle, 0); + _engine->_mixer->setChannelVolume(_handle, new_volume); + } + } + + _volume = new_volume; +} + +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) { + MusicNode *mus = (MusicNode *)fx; + mus->setPanTrack(pos); + } +} + +PanTrackNode::~PanTrackNode() { + SideFX *fx = _engine->getScriptManager()->getSideFX(_slot); + if (fx && fx->getType() == SIDEFX_AUDIO) { + MusicNode *mus = (MusicNode *)fx; + mus->unsetPanTrack(); + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/music_node.h b/engines/zvision/music_node.h new file mode 100644 index 0000000000..d24496fc02 --- /dev/null +++ b/engines/zvision/music_node.h @@ -0,0 +1,82 @@ +/* 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/sidefx.h" +#include "zvision/subtitles.h" + +namespace Common { +class String; +} + +namespace ZVision { +class MusicNode : public SideFX { +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 _pantrack_X; + int32 _attenuate; + uint8 _volume; + bool _loop; + bool _crossfade; + uint8 _crossfade_target; + int32 _crossfade_time; + bool _stereo; + Audio::SoundHandle _handle; + Subtitle *_sub; +}; + +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/actions.cpp b/engines/zvision/scripting/actions.cpp index e854378ae4..f60a69744a 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. @@ -22,15 +22,18 @@ #include "common/scummsys.h" -#include "zvision/scripting/actions.h" +#include "zvision/actions.h" #include "zvision/zvision.h" -#include "zvision/scripting/script_manager.h" -#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/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/zork_raw.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/timer_node.h" +#include "zvision/music_node.h" +#include "zvision/syncsound_node.h" +#include "zvision/animation_node.h" +#include "zvision/ttytext_node.h" #include "common/file.h" @@ -43,12 +46,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 +61,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 +84,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 +103,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,14 +120,35 @@ 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(ZVision *engine) { - // TODO: Implement +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; } @@ -117,16 +157,13 @@ bool ActionCrossfade::execute(ZVision *engine) { // 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); - - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); - +bool ActionDisableControl::execute() { + _engine->getScriptManager()->setStateFlag(_key, Puzzle::DISABLED); return true; } @@ -135,16 +172,102 @@ bool ActionDisableControl::execute(ZVision *engine) { // 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(ZVision *engine) { - debug("Enabling control %u", _key); +bool ActionEnableControl::execute() { + _engine->getScriptManager()->unsetStateFlag(_key, Puzzle::DISABLED); + 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); - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~ScriptManager::DISABLED); + 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; + } +} + +bool ActionInventory::execute() { + switch (_type) { + case 0: // add + _engine->getScriptManager()->invertory_add(_key); + break; + case 1: // addi + _engine->getScriptManager()->invertory_add(_engine->getScriptManager()->getStateValue(_key)); + break; + case 2: // drop + if (_key >= 0) + _engine->getScriptManager()->invertory_drop(_key); + else + _engine->getScriptManager()->invertory_drop(_engine->getScriptManager()->getStateValue(StateKey_InventoryItem)); + break; + case 3: // dropi + _engine->getScriptManager()->invertory_drop(_engine->getScriptManager()->getStateValue(_key)); + break; + case 4: // cycle + _engine->getScriptManager()->invertory_cycle(); + 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; } @@ -153,13 +276,16 @@ bool ActionEnableControl::execute(ZVision *engine) { // 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[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) { @@ -180,55 +306,81 @@ ActionMusic::ActionMusic(const Common::String &line) : _volume(255) { } } -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); - } - - if (_loop) { - Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); - } else { - engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume); - } +bool ActionMusic::execute() { + if (_engine->getScriptManager()->getSideFX(_slotkey)) + return true; + + 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), + _mus_slot(0) { + + sscanf(line.c_str(), "%u %d", &_mus_slot, &_pos); +} + +ActionPanTrack::~ActionPanTrack() { + _engine->getScriptManager()->killSideFx(_slotkey); +} + +bool ActionPanTrack::execute() { + if (_engine->getScriptManager()->getSideFX(_slotkey)) + return true; + + _engine->getScriptManager()->addSideFX(new PanTrackNode(_engine, _slotkey, _mus_slot, _pos)); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) { +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 - - // TODO: Check if the Control already exists +ActionPreloadAnimation::~ActionPreloadAnimation() { + _engine->getScriptManager()->deleteSideFx(_slotkey); +} - // 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); +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; } @@ -237,19 +389,40 @@ bool ActionPreloadAnimation::execute(ZVision *engine) { // ActionPlayAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) { +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 +431,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,8 +452,8 @@ bool ActionPlayPreloadAnimation::execute(ZVision *engine) { // ActionQuit ////////////////////////////////////////////////////////////////////////////// -bool ActionQuit::execute(ZVision *engine) { - engine->quitGame(); +bool ActionQuit::execute() { + _engine->quitGame(); return true; } @@ -296,13 +463,22 @@ bool ActionQuit::execute(ZVision *engine) { // 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 max_buf[64]; + memset(max_buf, 0, 64); + sscanf(line.c_str(), "%s", max_buf); + _max = new ValueSlot(_engine->getScriptManager(), max_buf); +} + +ActionRandom::~ActionRandom() { + if (_max) + delete _max; } -bool ActionRandom::execute(ZVision *engine) { - uint randNumber = engine->getRandomSource()->getRandomNumber(_max); - engine->getScriptManager()->setStateValue(_key, randNumber); +bool ActionRandom::execute() { + uint randNumber = _engine->getRandomSource()->getRandomNumber(_max->getValue()); + _engine->getScriptManager()->setStateValue(_slotkey, randNumber); return true; } @@ -311,27 +487,34 @@ bool ActionRandom::execute(ZVision *engine) { // ActionSetPartialScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { +ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { char fileName[25]; - uint color; + 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(); - - if (_backgroundColor > 0) { - renderManager->clearWorkingWindowTo555Color(_backgroundColor); - } - renderManager->renderImageToScreen(_fileName, _x, _y); +bool ActionSetPartialScreen::execute() { + RenderManager *renderManager = _engine->getRenderManager(); + if (_backgroundColor >= 0) + renderManager->renderImageToBackground(_fileName, _x, _y, _backgroundColor); + else + renderManager->renderImageToBackground(_fileName, _x, _y); return true; } @@ -341,16 +524,32 @@ bool ActionSetPartialScreen::execute(ZVision *engine) { // ActionSetScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetScreen::ActionSetScreen(const Common::String &line) { +ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { char fileName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", fileName); + 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; +} + +////////////////////////////////////////////////////////////////////////////// +// 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 +558,123 @@ bool ActionSetScreen::execute(ZVision *engine) { // ActionStreamVideo ////////////////////////////////////////////////////////////////////////////// -ActionStreamVideo::ActionStreamVideo(const Common::String &line) { +ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { char fileName[25]; - uint skippable; + 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); - Common::Rect destRect; - if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) { - destRect = Common::Rect(_x1, _y1, _x2, _y2); + if (_file) { + if (!decoder.loadStream(_file)) { + return true; + } + + 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); + + _engine->playVideo(decoder, destRect, _skippable, sub); + + 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 not_used; + + 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 time_buf[64]; + memset(time_buf, 0, 64); + sscanf(line.c_str(), "%s", time_buf); + _time = new ValueSlot(_engine->getScriptManager(), time_buf); } -bool ActionTimer::execute(ZVision *engine) { - engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time)); +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; +} + +////////////////////////////////////////////////////////////////////////////// +// 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 01457d21cc..c60aedcdf3 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,7 +51,10 @@ public: * @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; + virtual bool execute() = 0; +protected: + ZVision *_engine; + int32 _slotkey; }; @@ -85,38 +91,39 @@ public: 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,31 +135,31 @@ private: 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 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 @@ -161,8 +168,8 @@ private: 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,47 +177,67 @@ private: 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: }; 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: }; 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); + bool execute(); private: }; 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 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 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; @@ -218,31 +245,44 @@ private: Common::String _fileName; bool _loop; byte _volume; + bool _universe; +}; + +class ActionPanTrack : public ResultAction { +public: + ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line); + ~ActionPanTrack(); + bool execute(); + +private: + int32 _pos; + uint32 _mus_slot; }; 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 +298,75 @@ private: 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 ActionQuit : public ResultAction { public: - ActionQuit() {} - bool execute(ZVision *engine); + ActionQuit(ZVision *engine, int32 slotkey) : ResultAction(engine, slotkey) {} + bool execute(); }; // 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(); }; 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 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 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 +382,37 @@ private: 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 2343c83c56..ae717d6f8d 100644 --- a/engines/zvision/scripting/control.cpp +++ b/engines/zvision/scripting/control.cpp @@ -33,24 +33,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); } @@ -121,4 +103,28 @@ void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stre renderTable->generateRenderTable(); } +void Control::getParams(Common::String &input_str, Common::String ¶meter, Common::String &values) { + const char *chrs = input_str.c_str(); + uint lbr; + + for (lbr = 0; lbr < input_str.size(); lbr++) + if (chrs[lbr] == '(') + break; + + if (lbr >= input_str.size()) + return; + + uint rbr; + + for (rbr = lbr + 1; rbr < input_str.size(); rbr++) + if (chrs[rbr] == ')') + break; + + if (rbr >= input_str.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/control.h b/engines/zvision/scripting/control.h index ffeacb273d..93ad5f5df1 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,14 @@ class ZVision; class Control { public: - Control() : _engine(0), _key(0), _enabled(false) {} - Control(ZVision *engine, uint32 key) : _engine(engine), _key(key), _enabled(false) {} + Control() : _engine(0), _key(0) {} + Control(ZVision *engine, uint32 key) : _engine(engine), _key(key) {} virtual ~Control() {} - uint32 getKey() { return _key; } + uint32 getKey() { + return _key; + } - virtual void enable(); - virtual void disable(); virtual void focus() {} virtual void unfocus() {} /** @@ -54,14 +55,18 @@ public: * @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,7 +74,9 @@ public: * @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. * @@ -88,43 +95,15 @@ public: * @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; + } protected: - ZVision * _engine; + ZVision *_engine; uint32 _key; - bool _enabled; + void getParams(Common::String &input_str, Common::String ¶meter, Common::String &values); // Static member functions public: static void parseFlatControl(ZVision *engine); diff --git a/engines/zvision/scripting/controls/animation_control.cpp b/engines/zvision/scripting/controls/animation_control.cpp deleted file mode 100644 index e351e81d25..0000000000 --- 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/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp index 5cf5086691..a366e06923 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. @@ -22,13 +22,13 @@ #include "common/scummsys.h" -#include "zvision/scripting/controls/input_control.h" +#include "zvision/input_control.h" #include "zvision/zvision.h" -#include "zvision/scripting/script_manager.h" -#include "zvision/strings/string_manager.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/utility/utility.h" +#include "zvision/script_manager.h" +#include "zvision/string_manager.h" +#include "zvision/render_manager.h" +#include "zvision/utility.h" #include "common/str.h" #include "common/stream.h" @@ -38,11 +38,11 @@ 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), + _nextTabstop(0), + _focused(false), + _textChanged(false), + _cursorOffset(0) { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); @@ -93,8 +93,9 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre } } -void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { +bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { _engine->getScriptManager()->focusControl(_key); + return false; } void InputControl::onKeyDown(Common::KeyState keyState) { @@ -128,9 +129,9 @@ bool InputControl::process(uint32 deltaTimeInMillis) { // 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()); + //Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width()); - _cursorOffset = destRect.left - _textRectangle.left; + //_cursorOffset = destRect.left - _textRectangle.left; } // Render the next frame of the animation diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h index 32432438bb..4a63f228a0 100644 --- a/engines/zvision/scripting/controls/input_control.h +++ b/engines/zvision/scripting/controls/input_control.h @@ -48,9 +48,13 @@ private: uint _cursorOffset; public: - void focus() { _focused = true; } - void unfocus() { _focused = false; } - void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); + void focus() { + _focused = true; + } + void unfocus() { + _focused = false; + } + bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); void onKeyDown(Common::KeyState keyState); bool process(uint32 deltaTimeInMillis); }; diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index 9724e661b7..f68f256229 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. @@ -22,15 +22,14 @@ #include "common/scummsys.h" -#include "zvision/scripting/controls/lever_control.h" +#include "zvision/lever_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/rlf_animation.h" -#include "zvision/video/zork_avi_decoder.h" -#include "zvision/utility/utility.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/meta_animation.h" +#include "zvision/utility.h" #include "common/stream.h" #include "common/file.h" @@ -43,16 +42,16 @@ 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), + _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(); @@ -79,25 +78,24 @@ LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStre } 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; while (!file.eos()) { + line = file.readLine(); + if (line.matchString("*animation_id*", true)) { // Not used } else if (line.matchString("*filename*", true)) { @@ -106,14 +104,9 @@ void LeverControl::parseLevFile(const Common::String &fileName) { 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; - } + if (animationFileName.hasSuffix(".avi") || animationFileName.hasSuffix(".rlf")) + _animation = new MetaAnimation(animationFileName, _engine); + } else if (line.matchString("*skipcolor*", true)) { // Not used } else if (line.matchString("*anim_coords*", true)) { @@ -151,6 +144,8 @@ void LeverControl::parseLevFile(const Common::String &fileName) { 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 +153,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 +167,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(<from> to <to>) tokenizer.nextToken(); tokenizer.nextToken(); @@ -186,26 +181,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)) { _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; _engine->getScriptManager()->setStateValue(_key, _currentFrame); @@ -214,13 +208,13 @@ 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; if (_mouseIsCaptured) { @@ -240,7 +234,7 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common } } } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_cursorName); + _engine->getCursorManager()->changeCursor(_engine->getCursorManager()->getCursorId(_cursorName)); cursorWasChanged = true; } @@ -248,9 +242,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; @@ -276,7 +269,7 @@ bool LeverControl::process(uint32 deltaTimeInMillis) { renderFrame(_returnRoutesCurrentFrame); } } - + return false; } @@ -301,7 +294,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 +343,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 +370,13 @@ void LeverControl::renderFrame(uint frameNumber) { _lastRenderedFrame = frameNumber; } - const uint16 *frameData = 0; + const Graphics::Surface *frameData; 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; - } - _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height); + frameData = _animation->getFrameData(frameNumber); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, x, y); } } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h index 49e4fd3806..712d688523 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 @@ public: ~LeverControl(); private: - enum FileType { - RLF = 1, - AVI = 2 - }; struct Direction { Direction(uint a, uint t) : angle(a), toFrame(t) {} @@ -64,11 +60,7 @@ private: }; private: - union { - RlfAnimation *rlf; - ZorkAVIDecoder *avi; - } _animation; - FileType _fileType; + MetaAnimation *_animation; Common::String _cursorName; Common::Rect _animationCoords; @@ -88,8 +80,8 @@ private: 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); diff --git a/engines/zvision/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp index 82736b7576..16cd971ad5 100644 --- a/engines/zvision/scripting/controls/push_toggle_control.cpp +++ b/engines/zvision/scripting/controls/push_toggle_control.cpp @@ -35,10 +35,13 @@ namespace ZVision { PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : Control(engine, key) { + : Control(engine, key), + _countTo(2), + _event(Common::EVENT_LBUTTONUP) { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + line.toLowercase(); while (!stream.eos() && !line.contains('}')) { if (line.matchString("*_hotspot*", true)) { @@ -56,6 +59,27 @@ PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::Seekab sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer); _hoverCursor = Common::String(nameBuffer); + } else if (line.matchString("animation*", true)) { + // Not used + } else if (line.matchString("sound*", true)) { + // Not used + } else if (line.matchString("count_to*", true)) { + sscanf(line.c_str(), "%*[^(](%u)", &_countTo); + } else if (line.matchString("mouse_event*", true)) { + char nameBuffer[25]; + + sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer); + + Common::String evntStr(nameBuffer); + if (evntStr.equalsIgnoreCase("up")) { + _event = Common::EVENT_LBUTTONUP; + } else if (evntStr.equalsIgnoreCase("down")) { + _event = Common::EVENT_LBUTTONDOWN; + } else if (evntStr.equalsIgnoreCase("double")) { + // Not used + } + } else if (line.matchString("venus_id*", true)) { + // Not used } line = stream.readLine(); @@ -68,27 +92,46 @@ PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::Seekab } PushToggleControl::~PushToggleControl() { - // Clear the state value back to 0 - _engine->getScriptManager()->setStateValue(_key, 0); } -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 (_hotspot.contains(backgroundImageSpacePos)) { + 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 (_event != Common::EVENT_LBUTTONDOWN) + return false; + if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getScriptManager()->setStateValue(_key, 1); + 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); + _engine->getCursorManager()->changeCursor(_engine->getCursorManager()->getCursorId(_hoverCursor)); return true; } diff --git a/engines/zvision/scripting/controls/push_toggle_control.h b/engines/zvision/scripting/controls/push_toggle_control.h index 3854fc2005..6ba1bd77fa 100644 --- a/engines/zvision/scripting/controls/push_toggle_control.h +++ b/engines/zvision/scripting/controls/push_toggle_control.h @@ -26,6 +26,7 @@ #include "zvision/scripting/control.h" #include "common/rect.h" +#include "common/events.h" namespace ZVision { @@ -36,12 +37,19 @@ public: ~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. * @@ -60,6 +68,10 @@ private: Common::Rect _hotspot; /** The cursor to use when hovering over _hotspot */ Common::String _hoverCursor; + /** Button maximal values count */ + uint _countTo; + + Common::EventType _event; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/timer_node.cpp b/engines/zvision/scripting/controls/timer_node.cpp index c8c8a85d34..a94f6db19b 100644 --- a/engines/zvision/scripting/controls/timer_node.cpp +++ b/engines/zvision/scripting/controls/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,46 +22,53 @@ #include "common/scummsys.h" -#include "zvision/scripting/controls/timer_node.h" +#include "zvision/timer_node.h" #include "zvision/zvision.h" -#include "zvision/scripting/script_manager.h" +#include "zvision/script_manager.h" #include "common/stream.h" 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/controls/timer_node.h index 48b5fad1e9..f6584becda 100644 --- a/engines/zvision/scripting/controls/timer_node.h +++ b/engines/zvision/scripting/controls/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/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 @@ public: 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/puzzle.h b/engines/zvision/scripting/puzzle.h index ee9ce521c9..4d50756b87 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. @@ -23,7 +23,7 @@ #ifndef ZVISION_PUZZLE_H #define ZVISION_PUZZLE_H -#include "zvision/scripting/actions.h" +#include "zvision/actions.h" #include "common/list.h" #include "common/ptr.h" @@ -63,10 +63,17 @@ struct Puzzle { bool argumentIsAKey; }; + enum StateFlags { + ONCE_PER_INST = 0x01, + DISABLED = 0x02, + DO_ME_NOW = 0x04 + }; + uint32 key; Common::List<Common::List <CriteriaEntry> > criteriaList; // This has to be list of pointers because ResultAction is abstract Common::List<ResultAction *> 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 753ce4ac6a..9d1d0bf856 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,13 +22,15 @@ #include "common/scummsys.h" -#include "zvision/scripting/script_manager.h" +#include "zvision/zvision.h" +#include "zvision/script_manager.h" -#include "zvision/utility/utility.h" -#include "zvision/scripting/puzzle.h" -#include "zvision/scripting/actions.h" -#include "zvision/scripting/controls/push_toggle_control.h" -#include "zvision/scripting/controls/lever_control.h" +#include "zvision/utility.h" +#include "zvision/puzzle.h" +#include "zvision/actions.h" +#include "zvision/push_toggle_control.h" +#include "zvision/lever_control.h" +#include "zvision/slot_control.h" #include "common/textconsole.h" #include "common/file.h" @@ -37,14 +39,14 @@ namespace ZVision { -void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) { +void ScriptManager::parseScrFile(const Common::String &fileName, script_scope &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 +59,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.proc_count = 0; } void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) { @@ -81,12 +84,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<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const { @@ -148,103 +153,148 @@ 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 s_slot(chrs + startpos, chrs + pos); + slot = atoi(s_slot.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("debug", true)) { + // TODO: Implement ActionDebug + } else if (act.matchString("delay_render", true)) { + // TODO: Implement ActionDelayRender + } else if (act.matchString("disable_control", true)) { + actionList.push_back(new ActionDisableControl(_engine, slot, args)); + } else if (act.matchString("disable_venus", true)) { + // TODO: Implement ActionDisableVenus + } else if (act.matchString("display_message", true)) { + // TODO: Implement ActionDisplayMessage + } else if (act.matchString("dissolve", true)) { + // TODO: Implement ActionDissolve + } else if (act.matchString("distort", true)) { + // TODO: Implement ActionDistort + } else if (act.matchString("enable_control", true)) { + actionList.push_back(new ActionEnableControl(_engine, slot, args)); + } else if (act.matchString("flush_mouse_events", true)) { + // TODO: Implement ActionFlushMouseEvents + } 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)) { + // TODO: Implement ActionMenuBarEnable + } 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)) { + // TODO: Implement ActionPreferences + } 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)) { + // TODO: Implement ActionRegion + } else if (act.matchString("restore_game", true)) { + // TODO: Implement ActionRestoreGame + } else if (act.matchString("rotate_to", true)) { + // TODO: Implement ActionRotateTo + } else if (act.matchString("save_game", true)) { + // TODO: Implement ActionSaveGame + } 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)) { + // TODO: Implement ActionSetVenus + } 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 +309,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 +323,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 +332,22 @@ 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); } + return NULL; } } // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 41b835e550..1a567397a8 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -1,35 +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. - * - */ +* +* 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" +#include "zvision/script_manager.h" #include "zvision/zvision.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" -#include "zvision/scripting/actions.h" -#include "zvision/utility/utility.h" +#include "zvision/render_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/save_manager.h" +#include "zvision/actions.h" +#include "zvision/utility.h" +#include "zvision/timer_node.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -41,156 +42,189 @@ 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); } 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); } void ScriptManager::update(uint deltaTimeMillis) { + if (_currentLocation.node != _nextLocation.node || + _currentLocation.room != _nextLocation.room || + _currentLocation.view != _nextLocation.view || + _currentLocation.world != _nextLocation.world) + do_changeLocation(); + updateNodes(deltaTimeMillis); - checkPuzzleCriteria(); + execScope(nodeview); + execScope(room); + execScope(world); + execScope(universe); + 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<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*activePuzzleIter)->criteriaList.begin(); criteriaIter != (*activePuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List<Puzzle::CriteriaEntry>::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::execScope(script_scope &scope) { + // Swap queues + PuzzleList *tmp = scope.exec_queue; + scope.exec_queue = scope.scope_queue; + scope.scope_queue = tmp; + scope.scope_queue->clear(); + + for (PuzzleList::iterator PuzzleIter = scope._puzzles.begin(); PuzzleIter != scope._puzzles.end(); ++PuzzleIter) + (*PuzzleIter)->addedBySetState = 0; + + if (scope.proc_count < 2 || getStateValue(76)) { + for (PuzzleList::iterator PuzzleIter = scope._puzzles.begin(); PuzzleIter != scope._puzzles.end(); ++PuzzleIter) + checkPuzzleCriteria(*PuzzleIter, scope.proc_count); + } else { + for (PuzzleList::iterator PuzzleIter = scope.exec_queue->begin(); PuzzleIter != scope.exec_queue->end(); ++PuzzleIter) + checkPuzzleCriteria(*PuzzleIter, scope.proc_count); } - // Iterate through each global Puzzle - for (PuzzleList::iterator globalPuzzleIter = _globalPuzzles.begin(); globalPuzzleIter != _globalPuzzles.end(); ++globalPuzzleIter) { - Puzzle *puzzlePtr = (*globalPuzzleIter); + if (scope.proc_count < 2) { + scope.proc_count++; + } +} - // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*globalPuzzleIter)->criteriaList.begin(); criteriaIter != (*globalPuzzleIter)->criteriaList.end(); ++criteriaIter) { - for (Common::List<Puzzle::CriteriaEntry>::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, puzzle_ref ref) { + if (_referenceTable.contains(key)) { + Common::Array<puzzle_ref> *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(script_scope &scope) { + // Iterate through each local Puzzle + for (PuzzleList::iterator PuzzleIter = scope._puzzles.begin(); PuzzleIter != scope._puzzles.end(); ++PuzzleIter) { + Puzzle *puzzlePtr = (*PuzzleIter); + + puzzle_ref 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<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) + for (Common::List<Puzzle::CriteriaEntry>::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(); +void ScriptManager::updateControls(uint deltaTimeMillis) { + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) + (*iter)->process(deltaTimeMillis); +} - // 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::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) == 0) { + return; + } - // Check each Criteria - - bool criteriaMet = false; - for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { - criteriaMet = false; - - for (Common::List<Puzzle::CriteriaEntry>::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; - } + // Check each Criteria + if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) + return; + + bool criteriaMet = false; + for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { + criteriaMet = false; + + for (Common::List<Puzzle::CriteriaEntry>::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<ResultAction *>::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); + bool shouldContinue = true; + for (Common::List<ResultAction *>::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { + shouldContinue = shouldContinue && (*resultIter)->execute(); if (!shouldContinue) { break; } } + + if (!shouldContinue) { + return; + } } } @@ -205,62 +239,100 @@ void ScriptManager::cleanStateTable() { } } -uint ScriptManager::getStateValue(uint32 key) { +void ScriptManager::cleanScriptScope(script_scope &scope) { + scope._priv_queue_one.clear(); + scope._priv_queue_two.clear(); + scope.scope_queue = &scope._priv_queue_one; + scope.exec_queue = &scope._priv_queue_two; + 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.proc_count = 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<Puzzle *>::iterator iter = _referenceTable[key].begin(); iter != _referenceTable[key].end(); ++iter) { - _puzzlesToCheck.push((*iter)); - } + Common::Array<puzzle_ref> *arr = &_referenceTable[key]; + for (int32 i = arr->size() - 1; i >= 0; i--) + if (!(*arr)[i].puz->addedBySetState) { + (*arr)[i].scope->scope_queue->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<Puzzle *>::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); + + 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; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { uint32 controlKey = (*iter)->getKey(); - + if (controlKey == key) { (*iter)->focus(); } else if (controlKey == _currentlyFocusedControl) { @@ -271,167 +343,348 @@ void ScriptManager::focusControl(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) { + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { (*iter)->onKeyDown(keyState); } } void ScriptManager::onKeyUp(Common::KeyState keyState) { - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { + if (!_activeControls) + return; + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { (*iter)->onKeyUp(keyState); } } -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(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); + } + } +} + +void ScriptManager::do_changeLocation() { + 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)); + } + } // Auto save - _engine->getSaveManager()->autoSave(); + //_engine->getSaveManager()->autoSave(); + + 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); - // Clear all the containers _referenceTable.clear(); - _puzzlesToCheck.clear(); - for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) { - delete (*iter); - } - _activePuzzles.clear(); - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - delete (*iter); - } - _activeControls.clear(); + addPuzzlesToReferenceTable(universe); - // Revert to the idle cursor - _engine->getCursorManager()->revertToIdle(); + if (_nextLocation.world != _currentLocation.world) { + cleanScriptScope(nodeview); + cleanScriptScope(room); + cleanScriptScope(world); - // Reset the background velocity - _engine->getRenderManager()->setBackgroundVelocity(0); + Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view); + parseScrFile(fileName, nodeview); + addPuzzlesToReferenceTable(nodeview); - // Remove any alphaEntries - _engine->getRenderManager()->clearAlphaEntries(); + fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room); + parseScrFile(fileName, room); + addPuzzlesToReferenceTable(room); - // Clean the global state table - cleanStateTable(); + fileName = Common::String::format("%c.scr", _nextLocation.world); + parseScrFile(fileName, world); + addPuzzlesToReferenceTable(world); + } else if (_nextLocation.room != _currentLocation.room) { + cleanScriptScope(nodeview); + cleanScriptScope(room); - // Parse into puzzles and controls - Common::String fileName = Common::String::format("%c%c%c%c.scr", world, room, node, view); - parseScrFile(fileName); + addPuzzlesToReferenceTable(world); - // Change the background position - _engine->getRenderManager()->setBackgroundPosition(offset); + Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view); + parseScrFile(fileName, nodeview); + addPuzzlesToReferenceTable(nodeview); - // Enable all the controls - for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { - (*iter)->enable(); - } + fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room); + parseScrFile(fileName, room); + addPuzzlesToReferenceTable(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); - } + } else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) { + cleanScriptScope(nodeview); + + addPuzzlesToReferenceTable(room); + 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); - } + _activeControls = &nodeview._controls; + + // Revert to the idle cursor + _engine->getCursorManager()->changeCursor(CursorIndex_Idle); - _puzzlesToCheck.push((*iter)); + // 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); } +} - // Create the puzzle reference table - createReferenceTable(); +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); - // Update _currentLocation - _currentLocation.world = world; - _currentLocation.room = room; - _currentLocation.node = node; - _currentLocation.view = view; - _currentLocation.offset = offset; -} + stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G')); -void ScriptManager::serializeStateTable(Common::WriteStream *stream) { - // Write the number of state value entries - stream->writeUint32LE(_globalState.size()); + int32 slots = 20000; + if (_engine->getGameId() == GID_NEMESIS) + slots = 30000; - 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->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 next_loc; + + next_loc.world = stream->readByte(); + next_loc.room = stream->readByte(); + next_loc.node = stream->readByte(); + next_loc.view = stream->readByte(); + next_loc.offset = stream->readUint32LE() & 0x0000FFFF; + + // What the fck, eos is not 'return pos >= size' + // while (!stream->eos()) {*/ + while (stream->pos() < stream->size()) { + uint32 tag = stream->readUint32BE(); + uint32 tag_size = 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 < tag_size / 2; i++) + setStateFlagSilent(i, stream->readUint16LE()); + break; + case MKTAG('P', 'U', 'Z', 'Z'): + for (uint32 i = 0; i < tag_size / 2; i++) + setStateValueSilent(i, stream->readUint16LE()); + break; + default: + stream->seek(tag_size, SEEK_CUR); } } + + _nextLocation = next_loc; + + do_changeLocation(); + // Place for read prefs + _engine->setRenderDelay(10); + setStateValue(StateKey_RestoreFlag, 1); } Location ScriptManager::getCurrentLocation() const { @@ -441,4 +694,27 @@ Location ScriptManager::getCurrentLocation() const { return location; } +ValueSlot::ValueSlot(ScriptManager *sc_man, const char *slot_val): + _sc_man(sc_man) { + value = 0; + slot = false; + const char *is_slot = strstr(slot_val, "["); + if (is_slot) { + slot = true; + value = atoi(is_slot + 1); + } else { + slot = false; + value = atoi(slot_val); + } +} +int16 ValueSlot::getValue() { + if (slot) { + if (value >= 0) + return _sc_man->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 4d1b1f359b..6adade5745 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. @@ -23,8 +23,9 @@ #ifndef ZVISION_SCRIPT_MANAGER_H #define ZVISION_SCRIPT_MANAGER_H -#include "zvision/scripting/puzzle.h" -#include "zvision/scripting/control.h" +#include "zvision/puzzle.h" +#include "zvision/control.h" +#include "zvision/sidefx.h" #include "common/hashmap.h" #include "common/queue.h" @@ -39,6 +40,62 @@ 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_Brightness = 77, + StateKey_EF9_B = 91, + StateKey_EF9_G = 92, + StateKey_EF9_R = 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,69 +106,92 @@ struct Location { uint32 offset; }; -typedef Common::HashMap<uint32, Common::Array<Puzzle *> > PuzzleMap; typedef Common::List<Puzzle *> PuzzleList; typedef Common::Queue<Puzzle *> PuzzleQueue; typedef Common::List<Control *> ControlList; -typedef Common::HashMap<uint32, uint32> StateMap; -typedef Common::HashMap<uint32, uint> StateFlagMap; +typedef Common::HashMap<uint32, int32> StateMap; +typedef Common::List<SideFX *> SideFXList; 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 script_scope { + uint32 proc_count; + + PuzzleList *scope_queue; // For adding puzzles to queue + PuzzleList *exec_queue; // Switch to it when execute + PuzzleList _priv_queue_one; + PuzzleList _priv_queue_two; + + PuzzleList _puzzles; + ControlList _controls; + }; + + struct puzzle_ref { + Puzzle *puz; + script_scope *scope; + }; + + typedef Common::HashMap<uint32, Common::Array<puzzle_ref> > 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; + + script_scope universe; + script_scope world; + script_scope room; + script_scope 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); + 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); + /** * Called when LeftMouse is pushed. * @@ -147,30 +227,48 @@ public: */ void onKeyUp(Common::KeyState keyState); + /** Mark next location */ void changeLocation(char world, char room, char node, char view, uint32 offset); - 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; private: - void createReferenceTable(); + void referenceTableAddPuzzle(uint32 key, puzzle_ref ref); + void addPuzzlesToReferenceTable(script_scope &scope); void updateNodes(uint deltaTimeMillis); - void checkPuzzleCriteria(); + void updateControls(uint deltaTimeMillis); + void checkPuzzleCriteria(Puzzle *puzzle, uint counter); void cleanStateTable(); + void cleanScriptScope(script_scope &scope); + void execScope(script_scope &scope); + + /** Perform change location */ + void do_changeLocation(); + + int8 invertory_getCount(); + void invertory_setCount(int8 cnt); + int16 invertory_getItem(int8 id); + void invertory_setItem(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 invertory_add(int16 item); + void invertory_drop(int16 item); + void invertory_cycle(); + + // 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, script_scope &scope); private: /** @@ -216,7 +314,17 @@ private: * @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 *sc_man, const char *slot_val); + int16 getValue(); +private: + int16 value; + bool slot; + ScriptManager *_sc_man; }; diff --git a/engines/zvision/search_manager.cpp b/engines/zvision/search_manager.cpp new file mode 100644 index 0000000000..124a49912d --- /dev/null +++ b/engines/zvision/search_manager.cpp @@ -0,0 +1,274 @@ +/* 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/search_manager.h" +#include "zvision/zfs_archive.h" +#include "common/fs.h" +#include "common/stream.h" + + +namespace ZVision { + +sManager::sManager(const Common::String &root_path, int depth) { + _root = root_path; + if (_root[_root.size() - 1] == '\\' || _root[_root.size() - 1] == '/') + _root.deleteLastChar(); + + Common::FSNode fs_node(_root); + + list_dir_recursive(dir_list, fs_node, depth); + + for (Common::List<Common::String>::iterator it = dir_list.begin(); it != dir_list.end();) + if (it->size() == _root.size()) + it = dir_list.erase(it); + else if (it->size() > _root.size()) { + *it = Common::String(it->c_str() + _root.size() + 1); + it++; + } else + it++; +} + +sManager::~sManager() { + Common::List<Common::Archive *>::iterator it = archList.begin(); + while (it != archList.end()) { + delete *it; + it++; + } + + archList.clear(); +} + +void sManager::addPatch(const Common::String &src, const Common::String &dst) { + Common::String lw_name = dst; + lw_name.toLowercase(); + + sManager::MatchList::iterator it = files.find(lw_name); + + if (it != files.end()) { + lw_name = src; + lw_name.toLowercase(); + files[lw_name] = it->_value; + } +} + +void sManager::addFile(const Common::String &name, Common::Archive *arch) { + bool addArch = true; + Common::List<Common::Archive *>::iterator it = archList.begin(); + while (it != archList.end()) { + if (*it == arch) { + addArch = false; + break; + } + it++; + } + if (addArch) + archList.push_back(arch); + + Common::String lw_name = name; + lw_name.toLowercase(); + + sManager::Node nod; + nod.name = lw_name; + nod.arch = arch; + + sManager::MatchList::iterator fit = files.find(lw_name); + + if (fit == files.end()) { + files[lw_name] = 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[lw_name] = nod; + } + } +} + +Common::File *sManager::openFile(const Common::String &name) { + Common::String lw_name = name; + lw_name.toLowercase(); + + sManager::MatchList::iterator fit = files.find(lw_name); + + if (fit != files.end()) { + Common::File *tmp = new Common::File(); + tmp->open(fit->_value.name, *fit->_value.arch); + return tmp; + } + return NULL; +} + +bool sManager::openFile(Common::File &file, const Common::String &name) { + Common::String lw_name = name; + lw_name.toLowercase(); + + sManager::MatchList::iterator fit = files.find(lw_name); + + if (fit != files.end()) + return file.open(fit->_value.name, *fit->_value.arch); + return false; +} + +bool sManager::hasFile(const Common::String &name) { + Common::String lw_name = name; + lw_name.toLowercase(); + + sManager::MatchList::iterator fit = files.find(lw_name); + + if (fit != files.end()) + return true; + return false; +} + +void sManager::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<Common::Archive *> 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 n_path[128]; + strcpy(n_path, path.c_str()); + for (uint i = 0; i < path.size(); i++) + if (n_path[i] == '\\') + n_path[i] = '/'; + + path = Common::String(n_path); + 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<Common::String>::iterator it = dir_list.begin(); it != dir_list.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 sManager::addDir(const Common::String &name) { + Common::String path; + for (Common::List<Common::String>::iterator it = dir_list.begin(); it != dir_list.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 z_name = (*ziter)->getName(); + addFile(z_name, 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 sManager::list_dir_recursive(Common::List<Common::String> &_list, const Common::FSNode &fs_node, int depth) { + Common::FSList fs_list; + fs_node.getChildren(fs_list); + + _list.push_back(fs_node.getPath()); + + if (depth > 1) + for (Common::FSList::const_iterator it = fs_list.begin(); it != fs_list.end(); ++it) + list_dir_recursive(_list, *it, depth - 1); +} + +} // End of namespace ZVision diff --git a/engines/zvision/search_manager.h b/engines/zvision/search_manager.h new file mode 100644 index 0000000000..c768cb8b00 --- /dev/null +++ b/engines/zvision/search_manager.h @@ -0,0 +1,51 @@ +#ifndef SEARCH_MANAGER_H_INCLUDED +#define SEARCH_MANAGER_H_INCLUDED + +#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 sManager { +public: + sManager(const Common::String &root_path, int depth); + ~sManager(); + + 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 list_dir_recursive(Common::List<Common::String> &dir_list, const Common::FSNode &fs_node, int depth); + + struct Node { + Common::String name; + Common::Archive *arch; + }; + + Common::List<Common::String> dir_list; + + typedef Common::HashMap<Common::String, Node, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MatchList; + + Common::List<Common::Archive *> archList; + MatchList files; + + Common::String _root; + +private: +}; + +} + +#endif // SEARCH_MANAGER_H_INCLUDED diff --git a/engines/zvision/inventory/inventory_manager.h b/engines/zvision/sidefx.cpp index f9d2ff294a..3874c86485 100644 --- a/engines/zvision/inventory/inventory_manager.h +++ b/engines/zvision/sidefx.cpp @@ -8,21 +8,29 @@ * 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_INVENTORY_MANAGER_H -#define ZVISION_INVENTORY_MANAGER_H +#include "common/scummsys.h" + +#include "zvision/sidefx.h" + +#include "zvision/zvision.h" +#include "zvision/render_manager.h" +#include "zvision/utility.h" + +#include "common/stream.h" + +namespace ZVision { -// TODO: Implement InventoryManager -#endif +} // End of namespace ZVision diff --git a/engines/zvision/sidefx.h b/engines/zvision/sidefx.h new file mode 100644 index 0000000000..2c53467559 --- /dev/null +++ b/engines/zvision/sidefx.h @@ -0,0 +1,124 @@ +/* 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: + +}; + +// 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 // SIDEFX_H_INCLUDED diff --git a/engines/zvision/slot_control.cpp b/engines/zvision/slot_control.cpp new file mode 100644 index 0000000000..111a7c63e8 --- /dev/null +++ b/engines/zvision/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/slot_control.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/render_manager.h" +#include "zvision/utility.h" + +#include "common/stream.h" + + +namespace ZVision { + +SlotControl::SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : Control(engine, key) { + + _rendered_item = 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", &_distance_id); + } else if (param.matchString("venus_id", true)) { + _venus_id = 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); + + _eligible_objects.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)) { + //ctrl_setvenus(ct); + + int item = _engine->getScriptManager()->getStateValue(_key); + int mouse_item = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + if (item != 0) { + if (mouse_item != 0) { + if (eligeblity(mouse_item)) { + _engine->getScriptManager()->invertory_drop(mouse_item); + _engine->getScriptManager()->invertory_add(item); + _engine->getScriptManager()->setStateValue(_key, mouse_item); + } + } else { + _engine->getScriptManager()->invertory_add(item); + _engine->getScriptManager()->setStateValue(_key, 0); + } + } else if (mouse_item == 0) { + if (eligeblity(0)) { + _engine->getScriptManager()->invertory_drop(0); + _engine->getScriptManager()->setStateValue(_key, 0); + } + } else if (eligeblity(mouse_item)) { + _engine->getScriptManager()->setStateValue(_key, mouse_item); + _engine->getScriptManager()->invertory_drop(mouse_item); + } + } + 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 cur_item = _engine->getScriptManager()->getStateValue(_key); + if (cur_item != _rendered_item) { + if (_rendered_item != 0 && cur_item == 0) { + _engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top); + _rendered_item = cur_item; + } else { + if (_rendered_item == 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", cur_item, _distance_id); + else + sprintf(buf, "g0z%cu%2.2x1.tga", _distance_id, cur_item); + + 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; + + _rendered_item = cur_item; + } + } + } + return false; +} + +bool SlotControl::eligeblity(int item_id) { + for (Common::List<int>::iterator it = _eligible_objects.begin(); it != _eligible_objects.end(); it++) + if (*it == item_id) + return true; + return false; +} + +} // End of namespace ZVision diff --git a/engines/zvision/slot_control.h b/engines/zvision/slot_control.h new file mode 100644 index 0000000000..eec2d04518 --- /dev/null +++ b/engines/zvision/slot_control.h @@ -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. + * + */ + +#ifndef ZVISION_SLOT_CONTROL_H +#define ZVISION_SLOT_CONTROL_H + +#include "zvision/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 _distance_id; + int _venus_id; + + int _rendered_item; + + Common::List<int> _eligible_objects; + + bool eligeblity(int item_id); + + Graphics::Surface *_bkg; + + /** The cursor to use when hovering over _hotspot */ + Common::String _hoverCursor; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp index edee1fd16e..a4d091f3c0 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. @@ -22,11 +22,11 @@ #include "common/scummsys.h" -#include "zvision/sound/zork_raw.h" +#include "zvision/zork_raw.h" #include "zvision/zvision.h" #include "zvision/detection.h" -#include "zvision/utility/utility.h" +#include "zvision/utility.h" #include "common/file.h" #include "common/str.h" @@ -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 ef98e3e1ef..a5e346dfbb 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<Common::SeekableReadStream> _stream; // Stream to read data from - bool _endOfData; // Whether the stream end has been reached uint _stereo; /** @@ -75,13 +68,58 @@ private: 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<Common::SeekableReadStream> _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 @@ public: * @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.cpp b/engines/zvision/subtitles.cpp new file mode 100644 index 0000000000..1942e3c979 --- /dev/null +++ b/engines/zvision/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.h" +#include "zvision/render_manager.h" +#include "zvision/search_manager.h" +#include "zvision/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 cur_sub; + cur_sub.start = -1; + cur_sub.stop = -1; + cur_sub.sub = txtline; + + _subs.push_back(cur_sub); + } + 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 776ddd3a97..09e079bba4 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<sub> _subs; +}; + +} #endif diff --git a/engines/zvision/syncsound_node.cpp b/engines/zvision/syncsound_node.cpp new file mode 100644 index 0000000000..5d04c1fb70 --- /dev/null +++ b/engines/zvision/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/syncsound_node.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/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/syncsound_node.h b/engines/zvision/syncsound_node.h new file mode 100644 index 0000000000..7d875f2797 --- /dev/null +++ b/engines/zvision/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/sidefx.h" +#include "zvision/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/text.cpp b/engines/zvision/text.cpp new file mode 100644 index 0000000000..2fdfafcdf6 --- /dev/null +++ b/engines/zvision/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.h" + +#include "zvision/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/render_manager.h" +#include "zvision/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 strt = -1; + int16 endt = -1; + + for (uint16 i = 0; i < txt.size(); i++) { + if (txt[i] == '<') + strt = i; + else if (txt[i] == '>') { + endt = i; + if (strt != -1) + if ((endt - strt - 1) > 0) + parseStyle(Common::String(txt.c_str() + strt + 1), endt - strt - 1); + } + + } +} + +void cTxtStyle::setFontStyle(sTTFont &font) { + uint temp_stl = 0; + + if (bold) + temp_stl |= sTTFont::STTF_BOLD; + + if (italic) + temp_stl |= sTTFont::STTF_ITALIC; + + if (underline) + temp_stl |= sTTFont::STTF_UNDERLINE; + + if (strikeout) + temp_stl |= sTTFont::STTF_STRIKEOUT; + + if (sharp) + temp_stl |= sTTFont::STTF_SHARP; + + font.setStyle(temp_stl); +} + +void cTxtStyle::setFont(sTTFont &font) { + uint temp_stl = 0; + + if (bold) + temp_stl |= sTTFont::STTF_BOLD; + + if (italic) + temp_stl |= sTTFont::STTF_ITALIC; + + if (underline) + temp_stl |= sTTFont::STTF_UNDERLINE; + + if (strikeout) + temp_stl |= sTTFont::STTF_STRIKEOUT; + + if (sharp) + temp_stl |= sTTFont::STTF_SHARP; + + font.loadFont(fontname, size, temp_stl); +} + +Graphics::Surface *textRenderer::render(sTTFont &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, sTTFont &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 &fnt_stl, Graphics::Surface &dst) { + sTTFont font(_engine); + fnt_stl.setFont(font); + + dst.fillRect(Common::Rect(dst.w, dst.h), 0); + + uint32 clr = _engine->_pixelFormat.RGBToColor(fnt_stl.red, fnt_stl.green, fnt_stl.blue); + + int16 w; + + w = font.getStringWidth(txt); + + drawTxtWithJustify(txt, font, clr, dst, fnt_stl.size, fnt_stl.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 strt = -1; + int16 endt = -1; + int16 i = 0; + int16 dx = 0, dy = 0; + int16 txt_w; + int16 txtpos = 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(); + + sTTFont font(_engine); + + style.setFont(font); + + int16 prevbufspace = 0, prevtxtspace = 0; + + while (i < stringlen) { + TxtJustify[currentline] = style.justify; + if (text[i] == '<') { + int16 ret = 0; + + strt = i; + while (i < stringlen && text[i] != '>') + i++; + endt = i; + if (strt != -1) + if ((endt - strt - 1) > 0) { + style2 = style; + ret = style.parseStyle(Common::String(text.c_str() + strt + 1), endt - strt - 1); + } + + if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { + if (buf.size() > 0) { + txt_w = font.getStringWidth(buf); + + TxtSurfaces[currentline][currentlineitm] = render(font, buf, style2); + TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + + currentlineitm++; + + buf.clear(); + prevbufspace = 0; + txtpos = 0; + dx += txt_w; + + } + 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; + txtpos += buf3.size(); + } + + } else { + + buf += text[i]; + txtpos++; + + if (text[i] == ' ') { + prevbufspace = txtpos - 1; + prevtxtspace = i; + } + + if (font.isLoaded()) { + txt_w = font.getStringWidth(buf); + if (txt_w + dx > dst.w) { + if (prevbufspace == 0) { + prevtxtspace = i; + prevbufspace = txtpos - 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; + txtpos = 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 single octet with a simple cast + 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.h b/engines/zvision/text.h new file mode 100644 index 0000000000..c1dc0c167c --- /dev/null +++ b/engines/zvision/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/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(sTTFont &font); + void setFont(sTTFont &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, sTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify); + int32 drawTxt(const Common::String &txt, cTxtStyle &fnt_stl, Graphics::Surface &dst); + Graphics::Surface *render(sTTFont &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/ttytext_node.cpp b/engines/zvision/ttytext_node.cpp new file mode 100644 index 0000000000..4f64800cd7 --- /dev/null +++ b/engines/zvision/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/ttytext_node.h" + +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/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/ttytext_node.h b/engines/zvision/ttytext_node.h new file mode 100644 index 0000000000..6c3e6f46af --- /dev/null +++ b/engines/zvision/ttytext_node.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_TTYTEXT_NODE_H +#define ZVISION_TTYTEXT_NODE_H + +#include "common/rect.h" +#include "graphics/surface.h" + +#include "zvision/sidefx.h" +#include "zvision/text.h" +#include "zvision/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; + //int16 x; + //int16 y; + //uint16 w; + //uint16 h; + cTxtStyle _style; + sTTFont _fnt; + Common::String _txtbuf; + uint32 _txtpos; + //int32 txtsize; + 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/utility/clock.h b/engines/zvision/utility/clock.h index 6ae0f86161..9a50116a8c 100644 --- a/engines/zvision/utility/clock.h +++ b/engines/zvision/utility/clock.h @@ -52,13 +52,17 @@ public: * * @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 e094188ef6..14613a6fb2 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 b51cf3905f..b7ae5ac2cb 100644 --- a/engines/zvision/utility/lzss_read_stream.h +++ b/engines/zvision/utility/lzss_read_stream.h @@ -64,7 +64,7 @@ private: * * @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 951383661a..ac6e99039a 100644 --- a/engines/zvision/utility/single_value_container.h +++ b/engines/zvision/utility/single_value_container.h @@ -60,7 +60,7 @@ public: explicit SingleValueContainer(Common::String value); // Copy constructor - explicit SingleValueContainer(const SingleValueContainer& other); + explicit SingleValueContainer(const SingleValueContainer &other); // Destructor ~SingleValueContainer(); @@ -91,7 +91,7 @@ public: 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 905bc4513a..537f525bd4 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<Common::String, byte> *count, - Common::HashMap<Common::String, bool> *fileAlreadyUsed, - Common::DumpFile &output) { + Common::String &line, + Common::HashMap<Common::String, byte> *count, + Common::HashMap<Common::String, bool> *fileAlreadyUsed, + Common::DumpFile &output) { const byte numberOfExamplesPerType = 8; if ((*count)[key] < numberOfExamplesPerType && !(*fileAlreadyUsed)[key]) { @@ -203,7 +203,7 @@ void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Com return; Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); - + Common::DumpFile output; output.open(outputFile); diff --git a/engines/zvision/utility/utility.h b/engines/zvision/utility/utility.h index 063d4c0663..380034404a 100644 --- a/engines/zvision/utility/utility.h +++ b/engines/zvision/utility/utility.h @@ -71,8 +71,8 @@ void removeDuplicateEntries(Common::Array<T> &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/video/video.cpp b/engines/zvision/video/video.cpp index d1fff30408..0ad9000fcb 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. @@ -24,8 +24,9 @@ #include "zvision/zvision.h" -#include "zvision/utility/clock.h" -#include "zvision/graphics/render_manager.h" +#include "zvision/clock.h" +#include "zvision/render_manager.h" +#include "zvision/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 f22a4203ab..e7624342e8 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,9 +43,14 @@ 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 { + warning("Got %d wave format in AVI\n", _wvInfo.tag); delete stream; } } diff --git a/engines/zvision/video/zork_avi_decoder.h b/engines/zvision/video/zork_avi_decoder.h index c47f007f9b..d8937ac057 100644 --- a/engines/zvision/video/zork_avi_decoder.h +++ b/engines/zvision/video/zork_avi_decoder.h @@ -8,41 +8,52 @@ * 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 "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); + private: + RawChunkStream *decoder; }; Video::AVIDecoder::AVIAudioTrack *createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo); @@ -50,7 +61,7 @@ private: 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 b464f6ee81..b1615aa7b4 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. @@ -24,16 +24,21 @@ #include "zvision/zvision.h" -#include "zvision/core/console.h" -#include "zvision/scripting/script_manager.h" -#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/archives/zfs_archive.h" +#include "zvision/console.h" +#include "zvision/script_manager.h" +#include "zvision/render_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/save_manager.h" +#include "zvision/string_manager.h" +#include "zvision/zfs_archive.h" #include "zvision/detection.h" +#include "zvision/menu.h" +#include "zvision/search_manager.h" +#include "zvision/text.h" +#include "zvision/truetype_font.h" #include "common/config-manager.h" +#include "common/str.h" #include "common/debug.h" #include "common/debug-channels.h" #include "common/textconsole.h" @@ -49,18 +54,22 @@ namespace ZVision { 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), + _aud_id(0), + _rendDelay(2), + _velocity(0) { debug(1, "ZVision::ZVision"); } @@ -83,6 +92,30 @@ ZVision::~ZVision() { void ZVision::initialize() { const Common::FSNode gameDataDir(ConfMan.get("path")); + + _searchManager = new sManager(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"); // 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. @@ -93,6 +126,7 @@ void ZVision::initialize() { SearchMan.addSubDirectoryMatching(gameDataDir, "zassets2", 0, 2, true); SearchMan.addSubDirectoryMatching(gameDataDir, "znemmx", 0, 1, true); SearchMan.addSubDirectoryMatching(gameDataDir, "zgi", 0, 4, true); + SearchMan.addSubDirectoryMatching(gameDataDir, "zgi_mx", 0, 1, true); SearchMan.addSubDirectoryMatching(gameDataDir, "fonts", 0, 1, true); // Find zfs archive files @@ -117,10 +151,16 @@ 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); + + if (_gameDescription->gameId == GID_GRANDINQUISITOR) + _menu = new menuZgi(this); + else + _menu = new menuNem(this); // Initialize the managers _cursorManager->initialize(); @@ -140,18 +180,27 @@ 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); @@ -181,4 +230,54 @@ 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() { + if (_velocity) { + RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 st_pos = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 new_pos = st_pos + _velocity * (1 - 2 * 0); + + int16 zero_point = 0; + if (st_pos >= zero_point && new_pos < zero_point) + _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) - 1); + if (st_pos <= zero_point && new_pos > zero_point) + _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) + 1); + + int16 scr_width = _renderManager->getBkgSize().x; + if (scr_width) + new_pos %= scr_width; + + if (new_pos < 0) + new_pos += scr_width; + + _scriptManager->setStateValue(StateKey_ViewPos, new_pos); + _renderManager->setBackgroundPosition(new_pos); + } else if (renderState == RenderTable::TILT) { + int16 st_pos = _scriptManager->getStateValue(StateKey_ViewPos); + + int16 new_pos = st_pos + _velocity * (1 - 2 * 0); + + int16 scr_height = _renderManager->getBkgSize().y; + int16 tilt_gap = _workingWindow.height() / 2; + + if (new_pos >= (scr_height - tilt_gap)) + new_pos = scr_height - tilt_gap; + if (new_pos <= tilt_gap) + new_pos = tilt_gap; + + _scriptManager->setStateValue(StateKey_ViewPos, new_pos); + _renderManager->setBackgroundPosition(new_pos); + } + } +} + } // End of namespace ZVision diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h index 0b5a86f370..982d3fd98b 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/clock.h" +#include "zvision/search_manager.h" #include "common/random.h" #include "common/events.h" @@ -44,12 +45,16 @@ 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 ZVision : public Engine { public: @@ -62,7 +67,7 @@ public: * 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,12 +76,12 @@ private: 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 @@ -96,27 +101,57 @@ private: CursorManager *_cursorManager; SaveManager *_saveManager; StringManager *_stringManager; + menuHandler *_menu; + sManager *_searchManager; + textRenderer *_textRenderer; // Clock Clock _clock; + // Audio ID + int _aud_id; + // To prevent allocation every time we process events Common::Event _event; + const Common::Rect _workingWindow_ZGI; + const Common::Rect _workingWindow_ZNM; + + int _rendDelay; + int16 _velocity; 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; + } + sManager *getSearchManager() const { + return _searchManager; + } + textRenderer *getTextRenderer() const { + return _textRenderer; + } + 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 +162,14 @@ public: * @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); Common::String generateSaveFileName(uint slot); Common::String generateAutoSaveFileName(); + void setRenderDelay(uint); + bool canRender(); + private: void initialize(); void initFonts(); @@ -144,6 +182,7 @@ private: void onMouseDown(const Common::Point &pos); void onMouseUp(const Common::Point &pos); void onMouseMove(const Common::Point &pos); + void updateRotation(); }; } // End of namespace ZVision |