aboutsummaryrefslogtreecommitdiff
path: root/engines/zvision
diff options
context:
space:
mode:
Diffstat (limited to 'engines/zvision')
-rw-r--r--engines/zvision/animation/rlf_animation.cpp331
-rw-r--r--engines/zvision/animation/rlf_animation.h163
-rw-r--r--engines/zvision/configure.engine2
-rw-r--r--engines/zvision/core/clock.cpp (renamed from engines/zvision/utility/clock.cpp)3
-rw-r--r--engines/zvision/core/clock.h (renamed from engines/zvision/utility/clock.h)11
-rw-r--r--engines/zvision/core/console.cpp278
-rw-r--r--engines/zvision/core/console.h9
-rw-r--r--engines/zvision/core/events.cpp438
-rw-r--r--engines/zvision/core/save_manager.cpp206
-rw-r--r--engines/zvision/cursors/cursor.cpp94
-rw-r--r--engines/zvision/cursors/cursor_manager.cpp152
-rw-r--r--engines/zvision/detection.cpp382
-rw-r--r--engines/zvision/detection.h1
-rw-r--r--engines/zvision/file/lzss_read_stream.cpp (renamed from engines/zvision/utility/lzss_read_stream.cpp)20
-rw-r--r--engines/zvision/file/lzss_read_stream.h (renamed from engines/zvision/utility/lzss_read_stream.h)3
-rw-r--r--engines/zvision/file/save_manager.cpp292
-rw-r--r--engines/zvision/file/save_manager.h (renamed from engines/zvision/core/save_manager.h)31
-rw-r--r--engines/zvision/file/search_manager.cpp285
-rw-r--r--engines/zvision/file/search_manager.h70
-rw-r--r--engines/zvision/file/zfs_archive.cpp (renamed from engines/zvision/archives/zfs_archive.cpp)16
-rw-r--r--engines/zvision/file/zfs_archive.h (renamed from engines/zvision/archives/zfs_archive.h)5
-rw-r--r--engines/zvision/fonts/truetype_font.cpp113
-rw-r--r--engines/zvision/graphics/cursors/cursor.cpp96
-rw-r--r--engines/zvision/graphics/cursors/cursor.h (renamed from engines/zvision/cursors/cursor.h)28
-rw-r--r--engines/zvision/graphics/cursors/cursor_manager.cpp158
-rw-r--r--engines/zvision/graphics/cursors/cursor_manager.h (renamed from engines/zvision/cursors/cursor_manager.h)84
-rw-r--r--engines/zvision/graphics/effects/fog.cpp173
-rw-r--r--engines/zvision/graphics/effects/fog.h53
-rw-r--r--engines/zvision/graphics/effects/light.cpp109
-rw-r--r--engines/zvision/graphics/effects/light.h53
-rw-r--r--engines/zvision/graphics/effects/wave.cpp145
-rw-r--r--engines/zvision/graphics/effects/wave.h51
-rw-r--r--engines/zvision/graphics/graphics_effect.h83
-rw-r--r--engines/zvision/graphics/render_manager.cpp1333
-rw-r--r--engines/zvision/graphics/render_manager.h373
-rw-r--r--engines/zvision/graphics/render_table.cpp96
-rw-r--r--engines/zvision/graphics/render_table.h18
-rw-r--r--engines/zvision/module.mk45
-rw-r--r--engines/zvision/scripting/actions.cpp1011
-rw-r--r--engines/zvision/scripting/actions.h335
-rw-r--r--engines/zvision/scripting/control.cpp64
-rw-r--r--engines/zvision/scripting/control.h111
-rw-r--r--engines/zvision/scripting/controls/animation_control.cpp263
-rw-r--r--engines/zvision/scripting/controls/fist_control.cpp304
-rw-r--r--engines/zvision/scripting/controls/fist_control.h84
-rw-r--r--engines/zvision/scripting/controls/hotmov_control.cpp188
-rw-r--r--engines/zvision/scripting/controls/hotmov_control.h61
-rw-r--r--engines/zvision/scripting/controls/input_control.cpp221
-rw-r--r--engines/zvision/scripting/controls/input_control.h39
-rw-r--r--engines/zvision/scripting/controls/lever_control.cpp227
-rw-r--r--engines/zvision/scripting/controls/lever_control.h24
-rw-r--r--engines/zvision/scripting/controls/paint_control.cpp219
-rw-r--r--engines/zvision/scripting/controls/paint_control.h92
-rw-r--r--engines/zvision/scripting/controls/push_toggle_control.cpp111
-rw-r--r--engines/zvision/scripting/controls/push_toggle_control.h22
-rw-r--r--engines/zvision/scripting/controls/safe_control.cpp180
-rw-r--r--engines/zvision/scripting/controls/safe_control.h65
-rw-r--r--engines/zvision/scripting/controls/save_control.cpp123
-rw-r--r--engines/zvision/scripting/controls/save_control.h (renamed from engines/zvision/core/menu.h)33
-rw-r--r--engines/zvision/scripting/controls/slot_control.cpp219
-rw-r--r--engines/zvision/scripting/controls/slot_control.h84
-rw-r--r--engines/zvision/scripting/controls/titler_control.cpp108
-rw-r--r--engines/zvision/scripting/controls/titler_control.h55
-rw-r--r--engines/zvision/scripting/effects/animation_effect.cpp217
-rw-r--r--engines/zvision/scripting/effects/animation_effect.h (renamed from engines/zvision/scripting/controls/animation_control.h)72
-rw-r--r--engines/zvision/scripting/effects/distort_effect.cpp104
-rw-r--r--engines/zvision/scripting/effects/distort_effect.h63
-rw-r--r--engines/zvision/scripting/effects/music_effect.cpp296
-rw-r--r--engines/zvision/scripting/effects/music_effect.h137
-rw-r--r--engines/zvision/scripting/effects/region_effect.cpp56
-rw-r--r--engines/zvision/scripting/effects/region_effect.h57
-rw-r--r--engines/zvision/scripting/effects/syncsound_effect.cpp85
-rw-r--r--engines/zvision/scripting/effects/syncsound_effect.h56
-rw-r--r--engines/zvision/scripting/effects/timer_effect.cpp (renamed from engines/zvision/scripting/controls/timer_node.cpp)42
-rw-r--r--engines/zvision/scripting/effects/timer_effect.h (renamed from engines/zvision/scripting/controls/timer_node.h)14
-rw-r--r--engines/zvision/scripting/effects/ttytext_effect.cpp172
-rw-r--r--engines/zvision/scripting/effects/ttytext_effect.h73
-rw-r--r--engines/zvision/scripting/inventory.cpp122
-rw-r--r--engines/zvision/scripting/menu.cpp761
-rw-r--r--engines/zvision/scripting/menu.h119
-rw-r--r--engines/zvision/scripting/puzzle.h14
-rw-r--r--engines/zvision/scripting/scr_file_handling.cpp373
-rw-r--r--engines/zvision/scripting/script_manager.cpp958
-rw-r--r--engines/zvision/scripting/script_manager.h238
-rw-r--r--engines/zvision/scripting/scripting_effect.h124
-rw-r--r--engines/zvision/sound/midi.cpp91
-rw-r--r--engines/zvision/sound/midi.h (renamed from engines/zvision/inventory/inventory_manager.h)37
-rw-r--r--engines/zvision/sound/zork_raw.cpp264
-rw-r--r--engines/zvision/sound/zork_raw.h96
-rw-r--r--engines/zvision/strings/string_manager.cpp255
-rw-r--r--engines/zvision/text/string_manager.cpp70
-rw-r--r--engines/zvision/text/string_manager.h (renamed from engines/zvision/strings/string_manager.h)33
-rw-r--r--engines/zvision/text/subtitles.cpp115
-rw-r--r--engines/zvision/text/subtitles.h (renamed from engines/zvision/subtitles/subtitles.h)28
-rw-r--r--engines/zvision/text/text.cpp578
-rw-r--r--engines/zvision/text/text.h93
-rw-r--r--engines/zvision/text/truetype_font.cpp265
-rw-r--r--engines/zvision/text/truetype_font.h (renamed from engines/zvision/fonts/truetype_font.h)73
-rw-r--r--engines/zvision/utility/single_value_container.cpp348
-rw-r--r--engines/zvision/utility/single_value_container.h183
-rw-r--r--engines/zvision/utility/utility.cpp237
-rw-r--r--engines/zvision/utility/utility.h114
-rw-r--r--engines/zvision/video/rlf_decoder.cpp313
-rw-r--r--engines/zvision/video/rlf_decoder.h134
-rw-r--r--engines/zvision/video/video.cpp164
-rw-r--r--engines/zvision/video/zork_avi_decoder.cpp29
-rw-r--r--engines/zvision/video/zork_avi_decoder.h28
-rw-r--r--engines/zvision/zvision.cpp349
-rw-r--r--engines/zvision/zvision.h182
109 files changed, 13553 insertions, 4725 deletions
diff --git a/engines/zvision/animation/rlf_animation.cpp b/engines/zvision/animation/rlf_animation.cpp
deleted file mode 100644
index 945461e7b2..0000000000
--- a/engines/zvision/animation/rlf_animation.cpp
+++ /dev/null
@@ -1,331 +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/animation/rlf_animation.h"
-
-#include "common/str.h"
-#include "common/file.h"
-#include "common/textconsole.h"
-#include "common/debug.h"
-#include "common/endian.h"
-
-#include "graphics/colormasks.h"
-
-
-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)) {
- warning("RLF animation file %s could not be opened", fileName.c_str());
- return;
- }
-
- if (!readHeader()) {
- warning("%s is not a RLF animation file. Wrong magic number", fileName.c_str());
- 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;
- _currentFrameBuffer.free();
-}
-
-bool RlfAnimation::readHeader() {
- if (_file.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
-
- // 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);
-
- //// 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
-
- //// 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
-
- // Read time header
- _file.readUint32BE(); // Magic number EMIT
- _file.readUint32LE(); // Size4
- _file.readUint32LE(); // Unknown11
- _frameTime = _file.readUint32LE() / 10; // Frame time in microseconds
-
- return true;
-}
-
-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
-
- frame.encodedSize = size - headerSize;
- frame.encodedData = new int8[frame.encodedSize];
- _file.read(frame.encodedData, frame.encodedSize);
-
- if (type == MKTAG('E', 'L', 'H', 'D')) {
- frame.type = Masked;
- } else if (type == MKTAG('E', 'L', 'R', 'H')) {
- frame.type = Simple;
- _completeFrames.push_back(_lastFrameRead);
- } else {
- warning("Frame %u doesn't have type that can be decoded", _lastFrameRead);
- }
-
- _lastFrameRead++;
- return frame;
-}
-
-void RlfAnimation::seekToFrame(int frameNumber) {
- assert(!_stream);
- assert(frameNumber < (int)_frameCount || frameNumber >= -1);
-
- if (frameNumber == -1) {
- _currentFrame = -1;
- return;
- }
-
- int closestFrame = _currentFrame;
- int distance = (int)frameNumber - _currentFrame;
- for (uint i = 0; i < _completeFrames.size(); ++i) {
- int newDistance = (int)frameNumber - (int)(_completeFrames[i]);
- if (newDistance > 0 && (closestFrame == -1 || newDistance < distance)) {
- closestFrame = _completeFrames[i];
- distance = newDistance;
- }
- }
-
- for (int i = closestFrame; i <= frameNumber; ++i) {
- applyFrameToCurrent(i);
- }
-
- _currentFrame = frameNumber;
-}
-
-const Graphics::Surface *RlfAnimation::getFrameData(uint frameNumber) {
- assert(!_stream);
- 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) {
- return &_currentFrameBuffer;
- } else if (_currentFrame + 1 == (int)frameNumber) {
- return getNextFrame();
- }
-
- seekToFrame(frameNumber);
- return &_currentFrameBuffer;
-}
-
-const Graphics::Surface *RlfAnimation::getNextFrame() {
- assert(_currentFrame + 1 < (int)_frameCount);
-
- if (_stream) {
- applyFrameToCurrent(readNextFrame());
- } else {
- applyFrameToCurrent(_currentFrame + 1);
- }
-
- _currentFrame++;
- return &_currentFrameBuffer;
-}
-
-void RlfAnimation::applyFrameToCurrent(uint frameNumber) {
- if (_frames[frameNumber].type == Masked) {
- decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
- } else if (_frames[frameNumber].type == Simple) {
- decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
- }
-}
-
-void RlfAnimation::applyFrameToCurrent(const RlfAnimation::Frame &frame) {
- if (frame.type == Masked) {
- decodeMaskedRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize);
- } else if (frame.type == Simple) {
- decodeSimpleRunLengthEncoding(frame.encodedData, (int8 *)_currentFrameBuffer.getPixels(), frame.encodedSize, _frameBufferByteSize);
- }
-}
-
-void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const {
- uint32 sourceOffset = 0;
- uint32 destOffset = 0;
-
- while (sourceOffset < sourceSize) {
- int8 numberOfSamples = source[sourceOffset];
- sourceOffset++;
-
- // If numberOfSamples is negative, the next abs(numberOfSamples) samples should
- // be copied directly from source to dest
- if (numberOfSamples < 0) {
- numberOfSamples = ABS(numberOfSamples);
-
- while (numberOfSamples > 0) {
- if (sourceOffset + 1 >= sourceSize) {
- return;
- } else if (destOffset + 1 >= destSize) {
- debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
- return;
- }
-
- byte r, g, b;
- Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b);
- uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
- WRITE_UINT16(dest + destOffset, destColor);
-
- sourceOffset += 2;
- destOffset += 2;
- numberOfSamples--;
- }
-
- // 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;
- } else if (destOffset + 1 >= destSize) {
- debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
- return;
- }
-
- destOffset += (numberOfSamples * 2) + 2;
- }
- }
-}
-
-void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const {
- uint32 sourceOffset = 0;
- uint32 destOffset = 0;
-
- while (sourceOffset < sourceSize) {
- int8 numberOfSamples = source[sourceOffset];
- sourceOffset++;
-
- // If numberOfSamples is negative, the next abs(numberOfSamples) samples should
- // be copied directly from source to dest
- if (numberOfSamples < 0) {
- numberOfSamples = ABS(numberOfSamples);
-
- while (numberOfSamples > 0) {
- if (sourceOffset + 1 >= sourceSize) {
- return;
- } else if (destOffset + 1 >= destSize) {
- debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
- return;
- }
-
- byte r, g, b;
- Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b);
- uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
- WRITE_UINT16(dest + destOffset, destColor);
-
- sourceOffset += 2;
- destOffset += 2;
- numberOfSamples--;
- }
-
- // If numberOfSamples is >= 0, copy one sample from source to the
- // next (numberOfSamples + 2) dest spots
- } else {
- if (sourceOffset + 1 >= sourceSize) {
- return;
- }
-
- byte r, g, b;
- Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b);
- uint16 sampleColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
- sourceOffset += 2;
-
- numberOfSamples += 2;
- while (numberOfSamples > 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;
- }
-
- WRITE_UINT16(dest + destOffset, sampleColor);
- destOffset += 2;
- numberOfSamples--;
- }
- }
- }
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/animation/rlf_animation.h b/engines/zvision/animation/rlf_animation.h
deleted file mode 100644
index 4bb779301b..0000000000
--- a/engines/zvision/animation/rlf_animation.h
+++ /dev/null
@@ -1,163 +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.
- *
- */
-
-#ifndef ZVISION_RLF_ANIMATION_H
-#define ZVISION_RLF_ANIMATION_H
-
-#include "common/file.h"
-
-#include "graphics/surface.h"
-
-
-namespace Common {
-class String;
-}
-
-namespace ZVision {
-
-class RlfAnimation {
-public:
- RlfAnimation(const Common::String &fileName, bool stream = true);
- ~RlfAnimation();
-
-private:
- enum EncodingType {
- Masked,
- Simple
- };
-
- struct Frame {
- EncodingType type;
- int8 *encodedData;
- uint32 encodedSize;
- };
-
-private:
- Common::File _file;
- bool _stream;
- uint _lastFrameRead;
-
- uint _frameCount;
- uint _width;
- uint _height;
- uint32 _frameTime; // In milliseconds
- Frame *_frames;
- Common::Array<uint> _completeFrames;
-
- int _currentFrame;
- Graphics::Surface _currentFrameBuffer;
- uint32 _frameBufferByteSize;
-
-public:
- 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
- * the new frame data. If frameNumber == -1, it only sets _currentFrame,
- * the internal Surface is unchanged. This function requires _stream = false
- *
- * @param frameNumber The frame number to seek to
- */
- void seekToFrame(int frameNumber);
-
- /**
- * Returns the pixel data of the frame specified. It will try to use
- * getNextFrame() if possible. If not, it uses seekToFrame() to
- * update the internal Surface and then returns a pointer to it.
- * This function requires _stream = false
- *
- * @param frameNumber The frame number to get data for
- * @return A pointer to the pixel data. Do NOT delete this.
- */
- 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.
- * IE. Use endOfAnimation()
- *
- * @return A pointer to the pixel data. Do NOT delete this.
- */
- const Graphics::Surface *getNextFrame();
-
- /**
- * @return Is the currentFrame is the last frame in the animation?
- */
- bool endOfAnimation() { return _currentFrame == (int)_frameCount - 1; }
-
-private:
- /**
- * Reads in the header of the RLF file
- *
- * @return Will return false if the header magic number is wrong
- */
- bool readHeader();
- /**
- * Reads the next frame from the RLF file, stores the data in
- * a Frame object, then returns the object
- *
- * @return A Frame object representing the frame data
- */
- Frame readNextFrame();
-
- /**
- * Applies the frame corresponding to frameNumber on top of _currentFrameBuffer.
- * This function requires _stream = false so it can look up the Frame object
- * referenced by frameNumber.
- *
- * @param frameNumber The frame number to apply to _currentFrameBuffer
- */
- void applyFrameToCurrent(uint frameNumber);
- /**
- * Applies the data from a Frame object on top of a _currentFrameBuffer.
- *
- * @param frame A Frame object to apply to _currentFrameBuffer
- */
- void applyFrameToCurrent(const RlfAnimation::Frame &frame);
-
- /**
- * Decode frame data that uses masked run length encoding. This is the encoding
- * used by P-frames.
- *
- * @param source The source pixel data
- * @param dest The destination buffer
- * @param sourceSize The size of the source pixel data
- * @param destSize The size of the destination buffer
- */
- void decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const;
- /**
- * Decode frame data that uses simple run length encoding. This is the encoding
- * used by I-frames.
- *
- * @param source The source pixel data
- * @param dest The destination buffer
- * @param sourceSize The size of the source pixel data
- * @param destSize The size of the destination buffer
- */
- void decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const;
-};
-
-} // End of namespace ZVision
-
-#endif
diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine
index 02e31943af..38a5959995 100644
--- a/engines/zvision/configure.engine
+++ b/engines/zvision/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine zvision "ZVision" no "" "" "freetype2 16bit"
+add_engine zvision "ZVision" yes "" "" "freetype2 16bit"
diff --git a/engines/zvision/utility/clock.cpp b/engines/zvision/core/clock.cpp
index 45ab23ab65..1425d550b7 100644
--- a/engines/zvision/utility/clock.cpp
+++ b/engines/zvision/core/clock.cpp
@@ -22,11 +22,10 @@
#include "common/scummsys.h"
-#include "zvision/utility/clock.h"
+#include "zvision/core/clock.h"
#include "common/system.h"
-
namespace ZVision {
Clock::Clock(OSystem *system)
diff --git a/engines/zvision/utility/clock.h b/engines/zvision/core/clock.h
index 6ae0f86161..cbf52be560 100644
--- a/engines/zvision/utility/clock.h
+++ b/engines/zvision/core/clock.h
@@ -47,24 +47,31 @@ public:
* when the last update() was called.
*/
void update();
+
/**
* Get the delta time since the last frame. (The time between update() calls)
*
* @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.
* Has no effect if the clock is already paused.
*/
void start();
+
/**
* Un-pause the clock.
* Has no effect if the clock is already un-paused.
diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp
index e610f34474..f5cacb582c 100644
--- a/engines/zvision/core/console.cpp
+++ b/engines/zvision/core/console.cpp
@@ -1,24 +1,24 @@
/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
#include "common/scummsys.h"
@@ -27,11 +27,10 @@
#include "zvision/zvision.h"
#include "zvision/scripting/script_manager.h"
#include "zvision/graphics/render_manager.h"
-#include "zvision/strings/string_manager.h"
+#include "zvision/text/string_manager.h"
#include "zvision/video/zork_avi_decoder.h"
#include "zvision/sound/zork_raw.h"
-#include "zvision/utility/utility.h"
-#include "zvision/cursors/cursor.h"
+#include "zvision/graphics/cursors/cursor.h"
#include "common/system.h"
#include "common/file.h"
@@ -41,11 +40,9 @@
#include "audio/mixer.h"
-
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));
@@ -53,26 +50,17 @@ Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) {
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("location", WRAP_METHOD(Console, cmdLocation));
registerCmd("dumpfile", WRAP_METHOD(Console, cmdDumpFile));
- registerCmd("parseallscrfiles", WRAP_METHOD(Console, cmdParseAllScrFiles));
- registerCmd("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;
- }
-
- return true;
+ registerCmd("dumpfiles", WRAP_METHOD(Console, cmdDumpFiles));
+ registerCmd("dumpimage", WRAP_METHOD(Console, cmdDumpImage));
+ registerCmd("statevalue", WRAP_METHOD(Console, cmdStateValue));
+ registerCmd("stateflag", WRAP_METHOD(Console, cmdStateFlag));
}
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 %s <fileName> to load a video to the screen\n", argv[0]);
return true;
}
@@ -94,18 +82,20 @@ bool Console::cmdLoadSound(int argc, const char **argv) {
Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine);
Audio::SoundHandle handle;
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false);
-
} else if (argc == 4) {
int isStereo = atoi(argv[3]);
Common::File *file = new Common::File();
- file->open(argv[1]);
+ if (!_engine->getSearchManager()->openFile(*file, argv[1])) {
+ warning("File not found: %s", argv[1]);
+ return true;
+ }
Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true);
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 %s <fileName> [<rate> <isStereo: 1 or 0>] to load a sound\n", argv[0]);
return true;
}
@@ -114,17 +104,58 @@ 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 %s <rawFilePath> <wavFileName> to dump a .RAW file to .WAV\n", argv[0]);
return true;
}
- convertRawToWav(argv[1], _engine, argv[2]);
+ Common::File file;
+ if (!_engine->getSearchManager()->openFile(file, argv[1])) {
+ warning("File not found: %s", argv[1]);
+ return true;
+ }
+
+ Audio::AudioStream *audioStream = makeRawZorkStream(argv[1], _engine);
+
+ Common::DumpFile output;
+ output.open(argv[2]);
+
+ output.writeUint32BE(MKTAG('R', 'I', 'F', 'F'));
+ output.writeUint32LE(file.size() * 2 + 36);
+ output.writeUint32BE(MKTAG('W', 'A', 'V', 'E'));
+ output.writeUint32BE(MKTAG('f', 'm', 't', ' '));
+ output.writeUint32LE(16);
+ output.writeUint16LE(1);
+ uint16 numChannels;
+ if (audioStream->isStereo()) {
+ numChannels = 2;
+ output.writeUint16LE(2);
+ } else {
+ numChannels = 1;
+ output.writeUint16LE(1);
+ }
+ output.writeUint32LE(audioStream->getRate());
+ output.writeUint32LE(audioStream->getRate() * numChannels * 2);
+ output.writeUint16LE(numChannels * 2);
+ output.writeUint16LE(16);
+ output.writeUint32BE(MKTAG('d', 'a', 't', 'a'));
+ output.writeUint32LE(file.size() * 2);
+ int16 *buffer = new int16[file.size()];
+ audioStream->readBuffer(buffer, file.size());
+#ifndef SCUMM_LITTLE_ENDIAN
+ for (int i = 0; i < file.size(); ++i)
+ buffer[i] = TO_LE_16(buffer[i]);
+#endif
+ output.write(buffer, file.size() * 2);
+
+ delete[] buffer;
+
+
return true;
}
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 %s <RenderState: panorama, tilt, flat> to change the current render state\n", argv[0]);
return true;
}
@@ -137,7 +168,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 %s <RenderState: panorama, tilt, flat> to change the current render state\n", argv[0]);
return true;
}
@@ -150,7 +181,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 %s <fieldOfView> to change the current panorama field of view\n", argv[0]);
return true;
}
@@ -161,7 +192,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 %s <scale> to change the current panorama scale\n", argv[0]);
return true;
}
@@ -170,9 +201,14 @@ bool Console::cmdSetPanoramaScale(int argc, const char **argv) {
return true;
}
-bool Console::cmdChangeLocation(int argc, const char **argv) {
+bool Console::cmdLocation(int argc, const char **argv) {
+ Location curLocation = _engine->getScriptManager()->getCurrentLocation();
+ Common::String scrFile = Common::String::format("%c%c%c%c.scr", curLocation.world, curLocation.room, curLocation.node, curLocation.view);
+ debugPrintf("Current location: world '%c', room '%c', node '%c', view '%c', offset %d, script %s\n",
+ curLocation.world, curLocation.room, curLocation.node, curLocation.view, curLocation.offset, scrFile.c_str());
+
if (argc != 6) {
- debugPrintf("Use changelocation <char: world> <char: room> <char:node> <char:view> <int: x position> to change your location\n");
+ debugPrintf("Use %s <char: world> <char: room> <char:node> <char:view> <int: x offset> to change your location\n", argv[0]);
return true;
}
@@ -181,36 +217,152 @@ bool Console::cmdChangeLocation(int argc, const char **argv) {
return true;
}
+void dumpFile(Common::SeekableReadStream *s, const char *outName) {
+ byte *buffer = new byte[s->size()];
+ s->read(buffer, s->size());
+
+ Common::DumpFile dumpFile;
+ dumpFile.open(outName);
+
+ dumpFile.write(buffer, s->size());
+ dumpFile.flush();
+ dumpFile.close();
+
+ delete[] buffer;
+}
+
bool Console::cmdDumpFile(int argc, const char **argv) {
if (argc != 2) {
- debugPrintf("Use dumpfile <fileName> to dump a file\n");
+ debugPrintf("Use %s <fileName> to dump a file\n", argv[0]);
return true;
}
- writeFileContentsToFile(argv[1], argv[1]);
+ Common::File f;
+ if (!_engine->getSearchManager()->openFile(f, argv[1])) {
+ warning("File not found: %s", argv[1]);
+ return true;
+ }
+
+ dumpFile(&f, argv[1]);
return true;
}
-bool Console::cmdParseAllScrFiles(int argc, const char **argv) {
- Common::ArchiveMemberList list;
- SearchMan.listMatchingMembers(list, "*.scr");
+bool Console::cmdDumpFiles(int argc, const char **argv) {
+ Common::String fileName;
+ Common::SeekableReadStream *in;
+
+ if (argc != 2) {
+ debugPrintf("Use %s <file extension> to dump all files with a specific extension\n", argv[0]);
+ return true;
+ }
+
+ SearchManager::MatchList fileList;
+ _engine->getSearchManager()->listMembersWithExtension(fileList, argv[1]);
+
+ for (SearchManager::MatchList::iterator iter = fileList.begin(); iter != fileList.end(); ++iter) {
+ fileName = iter->_value.name;
+ debugPrintf("Dumping %s\n", fileName.c_str());
- for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
- _engine->getScriptManager()->parseScrFile((*iter)->getName());
+ in = iter->_value.arch->createReadStreamForMember(iter->_value.name);
+ if (in)
+ dumpFile(in, fileName.c_str());
+ delete in;
}
return true;
}
-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");
+bool Console::cmdDumpImage(int argc, const char **argv) {
+ if (argc != 2) {
+ debugPrintf("Use %s <TGA/TGZ name> to dump a ZVision TGA/TGZ image into a regular BMP image\n", argv[0]);
+ return true;
+ }
+
+ Common::String fileName = argv[1];
+ if (!fileName.hasSuffix(".tga")) {
+ debugPrintf("%s is not an image file", argv[1]);
+ }
+
+ Common::File f;
+ if (!_engine->getSearchManager()->openFile(f, argv[1])) {
+ warning("File not found: %s", argv[1]);
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);
+ Graphics::Surface surface;
+ _engine->getRenderManager()->readImageToSurface(argv[1], surface, false);
+
+ // Open file
+ Common::DumpFile out;
+
+ fileName.setChar('b', fileName.size() - 3);
+ fileName.setChar('m', fileName.size() - 2);
+ fileName.setChar('p', fileName.size() - 1);
+
+ out.open(fileName);
+
+ // Write BMP header
+ out.writeByte('B');
+ out.writeByte('M');
+ out.writeUint32LE(surface.h * surface.pitch + 54);
+ out.writeUint32LE(0);
+ out.writeUint32LE(54);
+ out.writeUint32LE(40);
+ out.writeUint32LE(surface.w);
+ out.writeUint32LE(surface.h);
+ out.writeUint16LE(1);
+ out.writeUint16LE(16);
+ out.writeUint32LE(0);
+ out.writeUint32LE(0);
+ out.writeUint32LE(0);
+ out.writeUint32LE(0);
+ out.writeUint32LE(0);
+ out.writeUint32LE(0);
+
+ // Write pixel data to BMP
+ out.write(surface.getPixels(), surface.pitch * surface.h);
+
+ out.flush();
+ out.close();
+
+ surface.free();
+
+ return true;
+}
+
+bool Console::cmdStateValue(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Use %s <valuenum> to show the value of a state variable\n", argv[0]);
+ debugPrintf("Use %s <valuenum> <newvalue> to set the value of a state variable\n", argv[0]);
+ return true;
+ }
+
+ int valueNum = atoi(argv[1]);
+ int newValue = (argc > 2) ? atoi(argv[2]) : -1;
+
+ if (argc == 2)
+ debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateValue(valueNum));
+ else if (argc == 3)
+ _engine->getScriptManager()->setStateValue(valueNum, newValue);
+
+ return true;
+}
+
+bool Console::cmdStateFlag(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Use %s <flagnum> to show the value of a state flag\n", argv[0]);
+ debugPrintf("Use %s <flagnum> <newvalue> to set the value of a state flag\n", argv[0]);
+ return true;
+ }
+
+ int valueNum = atoi(argv[1]);
+ int newValue = (argc > 2) ? atoi(argv[2]) : -1;
+
+ if (argc == 2)
+ debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateFlag(valueNum));
+ else if (argc == 3)
+ _engine->getScriptManager()->setStateFlag(valueNum, newValue);
return true;
}
diff --git a/engines/zvision/core/console.h b/engines/zvision/core/console.h
index 29523c57ee..ac834185a0 100644
--- a/engines/zvision/core/console.h
+++ b/engines/zvision/core/console.h
@@ -37,7 +37,6 @@ public:
private:
ZVision *_engine;
- bool cmdLoadImage(int argc, const char **argv);
bool cmdLoadVideo(int argc, const char **argv);
bool cmdLoadSound(int argc, const char **argv);
bool cmdRawToWav(int argc, const char **argv);
@@ -45,10 +44,12 @@ private:
bool cmdGenerateRenderTable(int argc, const char **argv);
bool cmdSetPanoramaFoV(int argc, const char **argv);
bool cmdSetPanoramaScale(int argc, const char **argv);
- bool cmdChangeLocation(int argc, const char **argv);
+ bool cmdLocation(int argc, const char **argv);
bool cmdDumpFile(int argc, const char **argv);
- bool cmdParseAllScrFiles(int argc, const char **argv);
- bool cmdRenderText(int argc, const char **argv);
+ bool cmdDumpFiles(int argc, const char **argv);
+ bool cmdDumpImage(int argc, const char **argv);
+ bool cmdStateValue(int argc, const char **argv);
+ bool cmdStateFlag(int argc, const char **argv);
};
} // End of namespace ZVision
diff --git a/engines/zvision/core/events.cpp b/engines/zvision/core/events.cpp
index 83d6c17dec..cc1c00b6d0 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.
@@ -25,10 +25,12 @@
#include "zvision/zvision.h"
#include "zvision/core/console.h"
-#include "zvision/cursors/cursor_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
#include "zvision/graphics/render_manager.h"
#include "zvision/scripting/script_manager.h"
-#include "zvision/animation/rlf_animation.h"
+#include "zvision/scripting/menu.h"
+#include "zvision/sound/zork_raw.h"
+#include "zvision/text/string_manager.h"
#include "common/events.h"
#include "common/system.h"
@@ -36,29 +38,190 @@
#include "engines/util.h"
-
namespace ZVision {
+void ZVision::pushKeyToCheatBuf(uint8 key) {
+ for (int i = 0; i < KEYBUF_SIZE - 1; i++)
+ _cheatBuffer[i] = _cheatBuffer[i + 1];
+
+ _cheatBuffer[KEYBUF_SIZE - 1] = key;
+}
+
+bool ZVision::checkCode(const char *code) {
+ int codeLen = strlen(code);
+
+ if (codeLen > KEYBUF_SIZE)
+ return false;
+
+ for (int i = 0; i < codeLen; i++)
+ if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?')
+ return false;
+
+ return true;
+}
+
+uint8 ZVision::getBufferedKey(uint8 pos) {
+ if (pos >= KEYBUF_SIZE)
+ return 0;
+ else
+ return _cheatBuffer[KEYBUF_SIZE - pos - 1];
+}
+
+void ZVision::shortKeys(Common::Event event) {
+ if (event.kbd.hasFlags(Common::KBD_CTRL)) {
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_s:
+ if (_menu->getEnable() & kMenubarSave)
+ _scriptManager->changeLocation('g', 'j', 's', 'e', 0);
+ break;
+ case Common::KEYCODE_r:
+ if (_menu->getEnable() & kMenubarRestore)
+ _scriptManager->changeLocation('g', 'j', 'r', 'e', 0);
+ break;
+ case Common::KEYCODE_p:
+ if (_menu->getEnable() & kMenubarSettings)
+ _scriptManager->changeLocation('g', 'j', 'p', 'e', 0);
+ break;
+ case Common::KEYCODE_q:
+ if (_menu->getEnable() & kMenubarExit)
+ ifQuit();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ZVision::cheatCodes(uint8 key) {
+ Location loc = _scriptManager->getCurrentLocation();
+ // Do not process cheat codes while in the game menus
+ if (loc.world == 'g' && loc.room == 'j')
+ return;
+
+ pushKeyToCheatBuf(key);
+
+ if (getGameId() == GID_GRANDINQUISITOR) {
+ if (checkCode("IMNOTDEAF")) {
+ // Unknown cheat
+ _renderManager->showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented"));
+ }
+
+ if (checkCode("3100OPB")) {
+ _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
+ _scriptManager->getStateValue(StateKey_World),
+ _scriptManager->getStateValue(StateKey_Room),
+ _scriptManager->getStateValue(StateKey_Node),
+ _scriptManager->getStateValue(StateKey_View)));
+ }
+
+ if (checkCode("KILLMENOW")) {
+ _scriptManager->changeLocation('g', 'j', 'd', 'e', 0);
+ _scriptManager->setStateValue(2201, 35);
+ }
+
+ if (checkCode("MIKESPANTS")) {
+ _scriptManager->changeLocation('g', 'j', 't', 'm', 0);
+ }
+
+ // There are 3 more cheats in script files:
+ // - "WHOAMI": gjcr.scr
+ // - "HUISOK": hp1e.scr
+ // - "EAT ME": uh1f.scr
+ } else if (getGameId() == GID_NEMESIS) {
+ if (checkCode("CHLOE")) {
+ _scriptManager->changeLocation('t', 'm', '2', 'g', 0);
+ _scriptManager->setStateValue(224, 1);
+ }
+
+ if (checkCode("77MASSAVE")) {
+ _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
+ _scriptManager->getStateValue(StateKey_World),
+ _scriptManager->getStateValue(StateKey_Room),
+ _scriptManager->getStateValue(StateKey_Node),
+ _scriptManager->getStateValue(StateKey_View)));
+ }
+
+ if (checkCode("IDKFA")) {
+ _scriptManager->changeLocation('t', 'w', '3', 'f', 0);
+ _scriptManager->setStateValue(249, 1);
+ }
+
+ if (checkCode("309NEWDORMA")) {
+ _scriptManager->changeLocation('g', 'j', 'g', 'j', 0);
+ }
+
+ if (checkCode("HELLOSAILOR")) {
+ Audio::AudioStream *soundStream;
+ if (loc == "vb10") {
+ soundStream = makeRawZorkStream("v000hpta.raw", this);
+ } else {
+ soundStream = makeRawZorkStream("v000hnta.raw", this);
+ }
+ Audio::SoundHandle handle;
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream);
+ }
+ }
+
+ if (checkCode("FRAME")) {
+ Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
+ _renderManager->showDebugMsg(fpsStr);
+ }
+
+ if (checkCode("COMPUTERARCH"))
+ _renderManager->showDebugMsg("COMPUTERARCH: var-viewer not implemented");
+
+ // This cheat essentially toggles the GOxxxx cheat below
+ if (checkCode("XYZZY"))
+ _scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats));
+
+ if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1)
+ if (checkCode("GO????"))
+ _scriptManager->changeLocation(getBufferedKey(3),
+ getBufferedKey(2),
+ getBufferedKey(1),
+ getBufferedKey(0), 0);
+
+ // Show the Venus screen when "?" or "/" is pressed while inside the temple world
+ if (_scriptManager->getStateValue(StateKey_VenusEnable) == 1)
+ if (getBufferedKey(0) == 0xBF && _scriptManager->getStateValue(StateKey_World) == 't')
+ _scriptManager->changeLocation('g', 'j', 'h', 'e', 0);
+}
+
void ZVision::processEvents() {
while (_eventMan->pollEvent(_event)) {
switch (_event.type) {
case Common::EVENT_LBUTTONDOWN:
- onMouseDown(_event.mouse);
+ _cursorManager->cursorDown(true);
+ _scriptManager->setStateValue(StateKey_LMouse, 1);
+ _menu->onMouseDown(_event.mouse);
+ _scriptManager->addEvent(_event);
break;
case Common::EVENT_LBUTTONUP:
- onMouseUp(_event.mouse);
+ _cursorManager->cursorDown(false);
+ _scriptManager->setStateValue(StateKey_LMouse, 0);
+ _menu->onMouseUp(_event.mouse);
+ _scriptManager->addEvent(_event);
break;
case Common::EVENT_RBUTTONDOWN:
- // TODO: Inventory logic
+ _cursorManager->cursorDown(true);
+ _scriptManager->setStateValue(StateKey_RMouse, 1);
+
+ if (getGameId() == GID_NEMESIS)
+ _scriptManager->inventoryCycle();
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ _cursorManager->cursorDown(false);
+ _scriptManager->setStateValue(StateKey_RMouse, 0);
break;
case Common::EVENT_MOUSEMOVE:
onMouseMove(_event.mouse);
break;
- case Common::EVENT_KEYDOWN:
+ case Common::EVENT_KEYDOWN: {
switch (_event.kbd.keycode) {
case Common::KEYCODE_d:
if (_event.kbd.hasFlags(Common::KBD_CTRL)) {
@@ -67,18 +230,57 @@ void ZVision::processEvents() {
_console->onFrame();
}
break;
- case Common::KEYCODE_q:
- if (_event.kbd.hasFlags(Common::KBD_CTRL))
- quitGame();
+
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_RIGHT:
+ if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
+ _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_LEFT ?
+ -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
+ _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_DOWN:
+ if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
+ _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_UP ?
+ -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
+ _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
+ break;
+
+ case Common::KEYCODE_F10: {
+ Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
+ _renderManager->showDebugMsg(fpsStr);
+ }
break;
default:
break;
}
- _scriptManager->onKeyDown(_event.kbd);
- break;
+ uint8 vkKey = getZvisionKey(_event.kbd.keycode);
+
+ _scriptManager->setStateValue(StateKey_KeyPress, vkKey);
+
+ _scriptManager->addEvent(_event);
+ shortKeys(_event);
+ cheatCodes(vkKey);
+ }
+ break;
case Common::EVENT_KEYUP:
- _scriptManager->onKeyUp(_event.kbd);
+ _scriptManager->addEvent(_event);
+ switch (_event.kbd.keycode) {
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_RIGHT:
+ if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
+ _keyboardVelocity = 0;
+ break;
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_DOWN:
+ if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
+ _keyboardVelocity = 0;
+ break;
+ default:
+ break;
+ }
break;
default:
break;
@@ -86,24 +288,11 @@ void ZVision::processEvents() {
}
}
-void ZVision::onMouseDown(const Common::Point &pos) {
- _cursorManager->cursorDown(true);
-
- Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos));
- _scriptManager->onMouseDown(pos, imageCoord);
-}
-
-void ZVision::onMouseUp(const Common::Point &pos) {
- _cursorManager->cursorDown(false);
-
- Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos));
- _scriptManager->onMouseUp(pos, imageCoord);
-}
-
void ZVision::onMouseMove(const Common::Point &pos) {
+ _menu->onMouseMove(pos);
Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos));
- bool cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord);
+ bool cursorWasChanged = false;
// Graph of the function governing rotation velocity:
//
@@ -135,52 +324,183 @@ void ZVision::onMouseMove(const Common::Point &pos) {
// |
// ^
- if (_workingWindow.contains(pos)) {
+ // Clip the horizontal mouse position to the working window
+ Common::Point clippedPos = pos;
+ clippedPos.x = CLIP<int16>(pos.x, _workingWindow.left + 1, _workingWindow.right - 1);
+
+ if (_workingWindow.contains(clippedPos)) {
+ cursorWasChanged = _scriptManager->onMouseMove(clippedPos, 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();
+ if (clippedPos.x >= _workingWindow.left && clippedPos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) {
+
+ int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
+ if (mspeed <= 0) {
+ mspeed = 25;
+ }
+ _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.left)) - mspeed).toInt(), -1);
+
+
+ _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();
+ } else if (clippedPos.x <= _workingWindow.right && clippedPos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) {
+
+ int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
+ if (mspeed <= 0) {
+ mspeed = 25;
+ }
+ _mouseVelocity = MAX((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
+
+ _cursorManager->changeCursor(CursorIndex_Right);
cursorWasChanged = true;
} else {
- _renderManager->setBackgroundVelocity(0);
+ _mouseVelocity = 0;
}
} else if (renderState == RenderTable::TILT) {
- if (pos.y >= _workingWindow.top && pos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) {
- // Linear function of distance to top edge
- // We use fixed point math to get better accuracy
- Common::Rational velocity = (Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - MAX_ROTATION_SPEED;
- _renderManager->setBackgroundVelocity(velocity.toInt());
- _cursorManager->setUpCursor();
+ if (clippedPos.y >= _workingWindow.top && clippedPos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) {
+
+ int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
+ if (mspeed <= 0) {
+ mspeed = 25;
+ }
+ _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - mspeed).toInt(), -1);
+
+ _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();
+ } else if (clippedPos.y <= _workingWindow.bottom && clippedPos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) {
+
+ int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
+ if (mspeed <= 0) {
+ mspeed = 25;
+ }
+ _mouseVelocity = MAX((Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
+
+ _cursorManager->changeCursor(CursorIndex_DownArr);
cursorWasChanged = true;
} else {
- _renderManager->setBackgroundVelocity(0);
+ _mouseVelocity = 0;
}
+ } else {
+ _mouseVelocity = 0;
}
} else {
- _renderManager->setBackgroundVelocity(0);
+ _mouseVelocity = 0;
}
if (!cursorWasChanged) {
- _cursorManager->revertToIdle();
+ _cursorManager->changeCursor(CursorIndex_Idle);
+ }
+}
+
+uint8 ZVision::getZvisionKey(Common::KeyCode scummKeyCode) {
+ if (scummKeyCode >= Common::KEYCODE_a && scummKeyCode <= Common::KEYCODE_z)
+ return 0x41 + scummKeyCode - Common::KEYCODE_a;
+ if (scummKeyCode >= Common::KEYCODE_0 && scummKeyCode <= Common::KEYCODE_9)
+ return 0x30 + scummKeyCode - Common::KEYCODE_0;
+ if (scummKeyCode >= Common::KEYCODE_F1 && scummKeyCode <= Common::KEYCODE_F15)
+ return 0x70 + scummKeyCode - Common::KEYCODE_F1;
+ if (scummKeyCode >= Common::KEYCODE_KP0 && scummKeyCode <= Common::KEYCODE_KP9)
+ return 0x60 + scummKeyCode - Common::KEYCODE_KP0;
+
+ switch (scummKeyCode) {
+ case Common::KEYCODE_BACKSPACE:
+ return 0x8;
+ case Common::KEYCODE_TAB:
+ return 0x9;
+ case Common::KEYCODE_CLEAR:
+ return 0xC;
+ case Common::KEYCODE_RETURN:
+ return 0xD;
+ case Common::KEYCODE_CAPSLOCK:
+ return 0x14;
+ case Common::KEYCODE_ESCAPE:
+ return 0x1B;
+ case Common::KEYCODE_SPACE:
+ return 0x20;
+ case Common::KEYCODE_PAGEUP:
+ return 0x21;
+ case Common::KEYCODE_PAGEDOWN:
+ return 0x22;
+ case Common::KEYCODE_END:
+ return 0x23;
+ case Common::KEYCODE_HOME:
+ return 0x24;
+ case Common::KEYCODE_LEFT:
+ return 0x25;
+ case Common::KEYCODE_UP:
+ return 0x26;
+ case Common::KEYCODE_RIGHT:
+ return 0x27;
+ case Common::KEYCODE_DOWN:
+ return 0x28;
+ case Common::KEYCODE_PRINT:
+ return 0x2A;
+ case Common::KEYCODE_INSERT:
+ return 0x2D;
+ case Common::KEYCODE_DELETE:
+ return 0x2E;
+ case Common::KEYCODE_HELP:
+ return 0x2F;
+ case Common::KEYCODE_KP_MULTIPLY:
+ return 0x6A;
+ case Common::KEYCODE_KP_PLUS:
+ return 0x6B;
+ case Common::KEYCODE_KP_MINUS:
+ return 0x6D;
+ case Common::KEYCODE_KP_PERIOD:
+ return 0x6E;
+ case Common::KEYCODE_KP_DIVIDE:
+ return 0x6F;
+ case Common::KEYCODE_NUMLOCK:
+ return 0x90;
+ case Common::KEYCODE_SCROLLOCK:
+ return 0x91;
+ case Common::KEYCODE_LSHIFT:
+ return 0xA0;
+ case Common::KEYCODE_RSHIFT:
+ return 0xA1;
+ case Common::KEYCODE_LCTRL:
+ return 0xA2;
+ case Common::KEYCODE_RCTRL:
+ return 0xA3;
+ case Common::KEYCODE_MENU:
+ return 0xA5;
+ case Common::KEYCODE_LEFTBRACKET:
+ return 0xDB;
+ case Common::KEYCODE_RIGHTBRACKET:
+ return 0xDD;
+ case Common::KEYCODE_SEMICOLON:
+ return 0xBA;
+ case Common::KEYCODE_BACKSLASH:
+ return 0xDC;
+ case Common::KEYCODE_QUOTE:
+ return 0xDE;
+ case Common::KEYCODE_SLASH:
+ return 0xBF;
+ case Common::KEYCODE_TILDE:
+ return 0xC0;
+ case Common::KEYCODE_COMMA:
+ return 0xBC;
+ case Common::KEYCODE_PERIOD:
+ return 0xBE;
+ case Common::KEYCODE_MINUS:
+ return 0xBD;
+ case Common::KEYCODE_PLUS:
+ return 0xBB;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+bool ZVision::ifQuit() {
+ if (_renderManager->askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) {
+ quitGame();
+ return true;
}
+ return false;
}
} // End of namespace ZVision
diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/core/save_manager.cpp
deleted file mode 100644
index 07fb7637e7..0000000000
--- a/engines/zvision/core/save_manager.cpp
+++ /dev/null
@@ -1,206 +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/core/save_manager.h"
-
-#include "zvision/zvision.h"
-#include "zvision/scripting/script_manager.h"
-#include "zvision/graphics/render_manager.h"
-
-#include "common/system.h"
-
-#include "graphics/surface.h"
-#include "graphics/thumbnail.h"
-
-#include "gui/message.h"
-
-
-namespace ZVision {
-
-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);
-
- 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);
-
- // Write savegame name
- file->writeString(saveName);
- file->writeByte(0);
-
- // 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());
-
- // Skip over the header info
- autoSaveFile->readSint32BE(); // SAVEGAME_ID
- autoSaveFile->readByte(); // Version
- autoSaveFile->seek(5, SEEK_CUR); // The string "auto" with terminating NULL
-
- // Read the rest to a buffer
- uint32 size = autoSaveFile->size() - autoSaveFile->pos();
- byte *buffer = new byte[size];
- autoSaveFile->read(buffer, size);
-
- // Then write the buffer to the new file
- file->write(buffer, size);
-
- // Cleanup
- delete[] buffer;
- file->finalize();
- delete file;
-}
-
-void SaveManager::autoSave() {
- Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName());
-
- // Write out the savegame header
- file->writeUint32BE(SAVEGAME_ID);
-
- // Version
- file->writeByte(SAVE_VERSION);
-
- file->writeString("auto");
- file->writeByte(0);
-
- writeSaveGameData(file);
-
- // Cleanup
- file->finalize();
- delete file;
-}
-
-void SaveManager::writeSaveGameData(Common::OutSaveFile *file) {
- // Create a thumbnail and save it
- Graphics::saveThumbnail(*file);
-
- // Write out the save date/time
- TimeDate td;
- g_system->getTimeAndDate(td);
- file->writeSint16LE(td.tm_year + 1900);
- file->writeSint16LE(td.tm_mon + 1);
- 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);
-
- Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot));
- if (saveFile == 0) {
- return Common::kPathDoesNotExist;
- }
-
- // Read the header
- SaveGameHeader header;
- if (!readSaveGameHeader(saveFile, header)) {
- 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);
-
- // Load the room
- scriptManager->changeLocation(world, room, node, view, offset);
-
- // Update the controls
- scriptManager->deserializeControls(saveFile);
-
- return Common::kNoError;
-}
-
-bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) {
- if (in->readUint32BE() != SAVEGAME_ID) {
- warning("File is not a ZVision save file. Aborting load");
- return false;
- }
-
- // Read in the version
- header.version = in->readByte();
-
- // Check that the save version isn't newer than this binary
- if (header.version > SAVE_VERSION) {
- uint tempVersion = header.version;
- GUI::MessageDialog dialog(Common::String::format("This save file uses version %u, but this engine only supports up to version %d. You will need an updated version of the engine to use this save file.", tempVersion, SAVE_VERSION), "OK");
- dialog.runModal();
- }
-
- // Read in the save name
- header.saveName.clear();
- char ch;
- while ((ch = (char)in->readByte()) != '\0')
- header.saveName += ch;
-
- // Get the thumbnail
- header.thumbnail = Graphics::loadThumbnail(*in);
- if (!header.thumbnail)
- return false;
-
- // Read in save date/time
- header.saveYear = in->readSint16LE();
- header.saveMonth = in->readSint16LE();
- header.saveDay = in->readSint16LE();
- header.saveHour = in->readSint16LE();
- header.saveMinutes = in->readSint16LE();
-
- return true;
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/cursors/cursor.cpp b/engines/zvision/cursors/cursor.cpp
deleted file mode 100644
index 9b9b9a3f71..0000000000
--- a/engines/zvision/cursors/cursor.cpp
+++ /dev/null
@@ -1,94 +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/cursors/cursor.h"
-
-#include "common/str.h"
-#include "common/file.h"
-
-
-namespace ZVision {
-
-ZorkCursor::ZorkCursor()
- : _width(0),
- _height(0),
- _hotspotX(0),
- _hotspotY(0) {
-}
-
-ZorkCursor::ZorkCursor(const Common::String &fileName)
- : _width(0),
- _height(0),
- _hotspotX(0),
- _hotspotY(0) {
- Common::File file;
- if (!file.open(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;
- _hotspotX = other._hotspotX;
- _hotspotY = other._hotspotY;
-
- _surface.copyFrom(other._surface);
-}
-
-ZorkCursor &ZorkCursor::operator=(const ZorkCursor &other) {
- _width = other._width;
- _height = other._height;
- _hotspotX = other._hotspotX;
- _hotspotY = other._hotspotY;
-
- _surface.free();
- _surface.copyFrom(other._surface);
-
- return *this;
-}
-
-ZorkCursor::~ZorkCursor() {
- _surface.free();
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/cursors/cursor_manager.cpp b/engines/zvision/cursors/cursor_manager.cpp
deleted file mode 100644
index 7f70c8b4e3..0000000000
--- a/engines/zvision/cursors/cursor_manager.cpp
+++ /dev/null
@@ -1,152 +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/cursors/cursor_manager.h"
-
-#include "zvision/zvision.h"
-
-#include "common/system.h"
-
-#include "graphics/pixelformat.h"
-#include "graphics/cursorman.h"
-
-
-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" };
-
-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" };
-
-const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft",
- "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]);
- }
-}
-
-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));
- 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;
- }
- }
- }
-
- // If we get here, something went wrong
- warning("No cursor found for identifier %s", cursorName.c_str());
-}
-
-void CursorManager::changeCursor(const ZorkCursor &cursor) {
- CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, _pixelFormat);
-}
-
-void CursorManager::cursorDown(bool pushed) {
- if (_cursorIsPushed == pushed)
- return;
-
- _cursorIsPushed = pushed;
- changeCursor(_currentCursor, pushed);
-}
-
-void CursorManager::setLeftCursor() {
- changeCursor("leftarrow");
-}
-
-void CursorManager::setRightCursor() {
- changeCursor("rightarrow");
-}
-
-void CursorManager::setUpCursor() {
- changeCursor("zuparrow");
-}
-
-void CursorManager::setDownCursor() {
- changeCursor("downarrow");
-}
-
-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);
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp
index 9961db1215..c817cbf3e9 100644
--- a/engines/zvision/detection.cpp
+++ b/engines/zvision/detection.cpp
@@ -26,13 +26,14 @@
#include "zvision/zvision.h"
#include "zvision/detection.h"
+#include "zvision/file/save_manager.h"
+#include "zvision/scripting/script_manager.h"
#include "common/translation.h"
#include "common/savefile.h"
#include "common/str-array.h"
#include "common/system.h"
-
namespace ZVision {
uint32 ZVision::getFeatures() const {
@@ -45,7 +46,6 @@ Common::Language ZVision::getLanguage() const {
} // End of namespace ZVision
-
static const PlainGameDescriptor zVisionGames[] = {
{"zvision", "ZVision Game"},
{"znemesis", "Zork Nemesis: The Forbidden Lands"},
@@ -53,9 +53,14 @@ static const PlainGameDescriptor zVisionGames[] = {
{0, 0}
};
-
namespace ZVision {
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_DOUBLE_FPS GUIO_GAMEOPTIONS2
+#define GAMEOPTION_ENABLE_VENUS GUIO_GAMEOPTIONS3
+#define GAMEOPTION_DISABLE_ANIM_WHILE_TURNING GUIO_GAMEOPTIONS4
+#define GAMEOPTION_USE_HIRES_MPEG_MOVIES GUIO_GAMEOPTIONS5
+
static const ZVisionGameDescription gameDescriptions[] = {
{
@@ -67,21 +72,156 @@ static const ZVisionGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO1(GUIO_NONE)
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
},
GID_NEMESIS
},
{
- // Zork Grand Inquisitor English version
+ // Zork Nemesis French version
{
- "zgi",
+ "znemesis",
+ 0,
+ {{"CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873},
+ {"NEMESIS.STR", 0, "333bcb17bbb7f57cae742fbbe44f56f3", 9219},
+ AD_LISTEND
+ },
+ Common::FR_FRA,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_NEMESIS
+ },
+
+ {
+ // Zork Nemesis German version
+ {
+ "znemesis",
+ 0,
+ {{"CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873},
+ {"NEMESIS.STR", 0, "3d1a12b907751653866cffc6d4dfb331", 9505},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_NEMESIS
+ },
+
+ {
+ // Zork Nemesis Italian version
+ {
+ "znemesis",
0,
+ {{"CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873},
+ {"NEMESIS.STR", 0, "7c568feca8d9f9ae855c47183612c305", 9061},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_NEMESIS
+ },
+
+ {
+ // Zork Nemesis English demo version
+ {
+ "znemesis",
+ "Demo",
+ AD_ENTRY1s("SCRIPTS.ZFS", "64f1e881394e9462305104f99513c833", 380539),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_NEMESIS
+ },
+
+ {
+ // Zork Grand Inquisitor English CD version
+ {
+ "zgi",
+ "CD",
AD_ENTRY1s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
- GUIO1(GUIO_NONE)
+ GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_GRANDINQUISITOR
+ },
+
+ {
+ // Zork Grand Inquisitor French CD version, reported by ulrichh on IRC
+ {
+ "zgi",
+ "CD",
+ AD_ENTRY1s("SCRIPTS.ZFS", "4d1ec4ade7ecc9ee9ec591d43ca3d213", 8338133),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_GRANDINQUISITOR
+ },
+
+ {
+ // Zork Grand Inquisitor German CD version, reported by breit in bug #6760
+ {
+ "zgi",
+ "CD",
+ AD_ENTRY1s("SCRIPTS.ZFS", "b7ac7e331b9b7f884590b0b325b560c8", 8338133),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_GRANDINQUISITOR
+ },
+
+ {
+ // Zork Grand Inquisitor Spanish CD version, reported by dianiu in bug #6764
+ {
+ "zgi",
+ "CD",
+ AD_ENTRY1s("SCRIPTS.ZFS", "5cdc4b99c1134053af135aae71326fd1", 8338141),
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
+ },
+ GID_GRANDINQUISITOR
+ },
+
+ {
+ // Zork Grand Inquisitor English DVD version
+ {
+ "zgi",
+ "DVD",
+ AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_USE_HIRES_MPEG_MOVIES)
+ },
+ GID_GRANDINQUISITOR
+ },
+
+ {
+ // Zork Grand Inquisitor English demo version
+ {
+ "zgi",
+ "Demo",
+ AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING)
},
GID_GRANDINQUISITOR
},
@@ -99,16 +239,63 @@ static const char *directoryGlobs[] = {
0
};
-static const ExtraGuiOption ZVisionExtraGuiOption = {
- _s("Use original save/load screens"),
- _s("Use the original save/load screens, instead of the ScummVM ones"),
- "originalsaveload",
- false
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_ORIGINAL_SAVELOAD,
+ {
+ _s("Use original save/load screens"),
+ _s("Use the original save/load screens, instead of the ScummVM ones"),
+ "originalsaveload",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_DOUBLE_FPS,
+ {
+ _s("Double FPS"),
+ _s("Increase game FPS from 30 to 60"),
+ "doublefps",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_ENABLE_VENUS,
+ {
+ _s("Enable Venus"),
+ _s("Enable the Venus help system"),
+ "venusenabled",
+ true
+ }
+ },
+
+ {
+ GAMEOPTION_DISABLE_ANIM_WHILE_TURNING,
+ {
+ _s("Disable animation while turning"),
+ _s("Disable animation while turning in panoramic mode"),
+ "noanimwhileturning",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_USE_HIRES_MPEG_MOVIES,
+ {
+ _s("Use the hires MPEG movies"),
+ _s("Use the hires MPEG movies of the DVD version, instead of the lowres AVI ones"),
+ "mpegmovies",
+ true
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class ZVisionMetaEngine : public AdvancedMetaEngine {
public:
- ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames) {
+ ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames, optionsList) {
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
_singleid = "zvision";
@@ -124,7 +311,6 @@ public:
virtual bool hasFeature(MetaEngineFeature f) const;
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
- virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
SaveStateList listSaves(const char *target) const;
virtual int getMaximumSaveSlot() const;
void removeSaveState(const char *target, int slot) const;
@@ -132,24 +318,40 @@ public:
};
bool ZVisionMetaEngine::hasFeature(MetaEngineFeature f) const {
- return false;
- /*
- (f == kSupportsListSaves) ||
- (f == kSupportsLoadingDuringStartup) ||
- (f == kSupportsDeleteSave) ||
- (f == kSavesSupportMetaInfo) ||
- (f == kSavesSupportThumbnail) ||
- (f == kSavesSupportCreationDate) ||
- (f == kSavesSupportPlayTime);
- */
+ return
+ (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);
-}*/
+bool ZVision::ZVision::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+Common::Error ZVision::ZVision::loadGameState(int slot) {
+ return _saveManager->loadGame(slot);
+}
+
+Common::Error ZVision::ZVision::saveGameState(int slot, const Common::String &desc) {
+ _saveManager->saveGame(slot, desc, false);
+ return Common::kNoError;
+}
+
+bool ZVision::ZVision::canLoadGameStateCurrently() {
+ return !_videoIsPlaying;
+}
+
+bool ZVision::ZVision::canSaveGameStateCurrently() {
+ Location currentLocation = _scriptManager->getCurrentLocation();
+ return !_videoIsPlaying && currentLocation.world != 'g' && !(currentLocation.room == 'j' || currentLocation.room == 'a');
+}
bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
const ZVision::ZVisionGameDescription *gd = (const ZVision::ZVisionGameDescription *)desc;
@@ -159,37 +361,36 @@ bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG
return gd != 0;
}
-const ExtraGuiOptions ZVisionMetaEngine::getExtraGuiOptions(const Common::String &target) const {
- ExtraGuiOptions options;
- options.push_back(ZVisionExtraGuiOption);
- return options;
-}
-
SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
- //Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
- /*ZVision::ZVision::SaveHeader header;
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ ZVision::SaveGameHeader header;
Common::String pattern = target;
pattern += ".???";
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;
- }
- }
- }*/
+ // We only use readSaveGameHeader() here, which doesn't need an engine callback
+ ZVision::SaveManager *zvisionSaveMan = new ZVision::SaveManager(NULL);
+
+ 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 (zvisionSaveMan->readSaveGameHeader(in, header)) {
+ saveList.push_back(SaveStateDescriptor(slotNum, header.saveName));
+ }
+ delete in;
+ }
+ }
+ }
+
+ delete zvisionSaveMan;
return saveList;
}
@@ -199,9 +400,8 @@ int ZVisionMetaEngine::getMaximumSaveSlot() const {
}
void ZVisionMetaEngine::removeSaveState(const char *target, int slot) const {
- /*
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
- Common::String filename = ZVision::ZVision::getSavegameFilename(target, slot);
+ Common::String filename = Common::String::format("%s.%03u", target, slot);
saveFileMan->removeSavefile(filename.c_str());
@@ -209,63 +409,67 @@ 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 = Common::String::format("%s.%03u", target, ++slot);
+ }
}
- */
}
SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
- /*
- Common::String filename = ZVision::ZVision::getSavegameFilename(target, slot);
+ Common::String filename = Common::String::format("%s.%03u", target, slot);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
if (in) {
- ZVision::ZVision::SaveHeader header;
- ZVision::ZVision::kReadSaveHeaderError error;
+ ZVision::SaveGameHeader header;
- error = ZVision::ZVision::readSaveHeader(in, true, header);
- delete in;
+ // We only use readSaveGameHeader() here, which doesn't need an engine callback
+ ZVision::SaveManager *zvisionSaveMan = new ZVision::SaveManager(NULL);
+ bool successfulRead = zvisionSaveMan->readSaveGameHeader(in, header);
+ delete zvisionSaveMan;
+ delete in;
- if (error == ZVision::ZVision::kRSHENoError) {
- SaveStateDescriptor desc(slot, header.description);
+ if (successfulRead) {
+ SaveStateDescriptor desc(slot, header.saveName);
- desc.setThumbnail(header.thumbnail);
+ // Do not allow save slot 0 (used for auto-saving) to be deleted or
+ // overwritten.
+ desc.setDeletableFlag(slot != 0);
+ desc.setWriteProtectedFlag(slot == 0);
- if (header.version > 0) {
- int day = (header.saveDate >> 24) & 0xFF;
- int month = (header.saveDate >> 16) & 0xFF;
- int year = header.saveDate & 0xFFFF;
+ desc.setThumbnail(header.thumbnail);
- desc.setSaveDate(year, month, day);
+ if (header.version > 0) {
+ int day = header.saveDay;
+ int month = header.saveMonth;
+ int year = header.saveYear;
- int hour = (header.saveTime >> 16) & 0xFF;
- int minutes = (header.saveTime >> 8) & 0xFF;
+ desc.setSaveDate(year, month, day);
- desc.setSaveTime(hour, minutes);
+ int hour = header.saveHour;
+ int minutes = header.saveMinutes;
- desc.setPlayTime(header.playTime * 1000);
- }
+ desc.setSaveTime(hour, minutes);
- return desc;
- }
+ //desc.setPlayTime(header.playTime * 1000);
+ }
+
+ return desc;
+ }
}
- */
return SaveStateDescriptor();
}
#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/detection.h b/engines/zvision/detection.h
index a788e710b7..f80cac79ec 100644
--- a/engines/zvision/detection.h
+++ b/engines/zvision/detection.h
@@ -25,7 +25,6 @@
#include "engines/advancedDetector.h"
-
namespace ZVision {
enum ZVisionGameId {
diff --git a/engines/zvision/utility/lzss_read_stream.cpp b/engines/zvision/file/lzss_read_stream.cpp
index e094188ef6..ca10af7d72 100644
--- a/engines/zvision/utility/lzss_read_stream.cpp
+++ b/engines/zvision/file/lzss_read_stream.cpp
@@ -22,18 +22,18 @@
#include "common/scummsys.h"
-#include "zvision/utility/lzss_read_stream.h"
-
+#include "zvision/file/lzss_read_stream.h"
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) {
- // Clear the window to null
- memset(_window, 0, BLOCK_SIZE);
+ : _source(source),
+ // It's convention to set the starting cursor position to blockSize - 16
+ _windowCursor(0x0FEE),
+ _eosFlag(false) {
+ // All values up to _windowCursor inits by 0x20
+ memset(_window, 0x20, _windowCursor);
+ memset(_window + _windowCursor, 0, BLOCK_SIZE - _windowCursor);
}
uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) {
@@ -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/file/lzss_read_stream.h
index b51cf3905f..1420621f13 100644
--- a/engines/zvision/utility/lzss_read_stream.h
+++ b/engines/zvision/file/lzss_read_stream.h
@@ -26,7 +26,6 @@
#include "common/stream.h"
#include "common/array.h"
-
namespace Common {
class SeekableReadStream;
}
@@ -64,7 +63,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/file/save_manager.cpp b/engines/zvision/file/save_manager.cpp
new file mode 100644
index 0000000000..63b54269de
--- /dev/null
+++ b/engines/zvision/file/save_manager.cpp
@@ -0,0 +1,292 @@
+/* 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/zvision.h"
+#include "zvision/file/save_manager.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+#include "common/system.h"
+#include "common/translation.h"
+
+#include "graphics/surface.h"
+#include "graphics/thumbnail.h"
+
+#include "gui/message.h"
+#include "gui/saveload.h"
+
+namespace ZVision {
+
+const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G');
+
+bool SaveManager::scummVMSaveLoadDialog(bool isSave) {
+ GUI::SaveLoadChooser *dialog;
+ Common::String desc;
+ int slot;
+
+ if (isSave) {
+ dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+ slot = dialog->runModalWithCurrentTarget();
+ desc = dialog->getResultString();
+
+ if (desc.empty()) {
+ // create our own description for the saved game, the user didnt enter it
+ desc = dialog->createDefaultSaveDescription(slot);
+ }
+
+ if (desc.size() > 28)
+ desc = Common::String(desc.c_str(), 28);
+ } else {
+ dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+ slot = dialog->runModalWithCurrentTarget();
+ }
+
+ delete dialog;
+
+ if (slot < 0)
+ return false;
+
+ if (isSave) {
+ saveGame(slot, desc, false);
+ return true;
+ } else {
+ Common::ErrorCode result = loadGame(slot).getCode();
+ return (result == Common::kNoError);
+ }
+}
+
+void SaveManager::saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer) {
+ if (!_tempSave && useSaveBuffer)
+ return;
+
+ Common::SaveFileManager *saveFileManager = g_system->getSavefileManager();
+ Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot));
+
+ writeSaveGameHeader(file, saveName, useSaveBuffer);
+
+ if (useSaveBuffer)
+ file->write(_tempSave->getData(), _tempSave->size());
+ else
+ _engine->getScriptManager()->serialize(file);
+
+ file->finalize();
+ delete file;
+
+ if (useSaveBuffer)
+ flushSaveBuffer();
+
+ _lastSaveTime = g_system->getMillis();
+}
+
+void SaveManager::autoSave() {
+ saveGame(0, "Auto save", false);
+}
+
+void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer) {
+ file->writeUint32BE(SAVEGAME_ID);
+
+ // Write version
+ file->writeByte(SAVE_VERSION);
+
+ // Write savegame name
+ file->writeString(saveName);
+ file->writeByte(0);
+
+ // Save the game thumbnail
+ if (useSaveBuffer)
+ file->write(_tempThumbnail->getData(), _tempThumbnail->size());
+ else
+ Graphics::saveThumbnail(*file);
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ file->writeSint16LE(td.tm_year + 1900);
+ file->writeSint16LE(td.tm_mon + 1);
+ file->writeSint16LE(td.tm_mday);
+ file->writeSint16LE(td.tm_hour);
+ file->writeSint16LE(td.tm_min);
+}
+
+Common::Error SaveManager::loadGame(int slot) {
+ Common::SeekableReadStream *saveFile = NULL;
+
+ if (slot >= 0) {
+ saveFile = getSlotFile(slot);
+ } else {
+ saveFile = _engine->getSearchManager()->openFile("r.svr");
+ if (!saveFile) {
+ Common::File *restoreFile = new Common::File();
+ if (!restoreFile->open("r.svr")) {
+ delete restoreFile;
+ return Common::kPathDoesNotExist;
+ }
+
+ saveFile = restoreFile;
+ }
+ }
+
+ if (!saveFile)
+ return Common::kPathDoesNotExist;
+
+ // Read the header
+ SaveGameHeader header;
+ if (!readSaveGameHeader(saveFile, header)) {
+ return Common::kUnknownError;
+ }
+
+ ScriptManager *scriptManager = _engine->getScriptManager();
+ // Update the state table values
+ scriptManager->deserialize(saveFile);
+
+ delete saveFile;
+ if (header.thumbnail)
+ delete header.thumbnail;
+
+ if (_engine->getGameId() == GID_NEMESIS && scriptManager->getCurrentLocation() == "tv2f") {
+ // WORKAROUND for script bug #6793: location tv2f (stairs) has two states:
+ // one at the top of the stairs, and one at the bottom. When the player
+ // goes to the bottom of the stairs, the screen changes, and hotspot
+ // 4652 (exit opposite the stairs) is enabled. However, the variable that
+ // controls the state (2408) is reset when the player goes down the stairs.
+ // Furthermore, the room's initialization script disables the stair exit
+ // control (4652). This leads to an impossible situation, where all the
+ // exit controls are disabled, and the player can't more anywhere. Thus,
+ // when loading a game in that room, we check for that impossible
+ // situation, which only occurs after the player has moved down the stairs,
+ // and fix it here by setting the correct background, and enabling the
+ // stair exit hotspot.
+ if ((scriptManager->getStateFlag(2411) & Puzzle::DISABLED) &&
+ (scriptManager->getStateFlag(2408) & Puzzle::DISABLED) &&
+ (scriptManager->getStateFlag(4652) & Puzzle::DISABLED)) {
+ _engine->getRenderManager()->setBackgroundImage("tv2fb21c.tga");
+ scriptManager->unsetStateFlag(4652, Puzzle::DISABLED);
+ }
+ }
+
+ return Common::kNoError;
+}
+
+bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) {
+ uint32 tag = in->readUint32BE();
+ // Check if it's original savegame than fill header structure
+ if (tag == MKTAG('Z', 'N', 'S', 'G')) {
+ header.saveYear = 0;
+ header.saveMonth = 0;
+ header.saveDay = 0;
+ header.saveHour = 0;
+ header.saveMinutes = 0;
+ header.saveName = "Original Save";
+ header.thumbnail = NULL;
+ header.version = SAVE_ORIGINAL;
+ in->seek(-4, SEEK_CUR);
+ return true;
+ }
+ if (tag != SAVEGAME_ID) {
+ warning("File is not a ZVision save file. Aborting load");
+ return false;
+ }
+
+ // Read in the version
+ header.version = in->readByte();
+
+ // Check that the save version isn't newer than this binary
+ if (header.version > SAVE_VERSION) {
+ uint tempVersion = header.version;
+ GUI::MessageDialog dialog(
+ Common::String::format(
+ "This save file uses version %u, but this engine only "
+ "supports up to version %d. You will need an updated version "
+ "of the engine to use this save file.", tempVersion, SAVE_VERSION
+ ),
+ "OK");
+ dialog.runModal();
+ }
+
+ // Read in the save name
+ header.saveName.clear();
+ char ch;
+ while ((ch = (char)in->readByte()) != '\0')
+ header.saveName += ch;
+
+ // Get the thumbnail
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ if (!header.thumbnail)
+ return false;
+
+ // Read in save date/time
+ header.saveYear = in->readSint16LE();
+ header.saveMonth = in->readSint16LE();
+ header.saveDay = in->readSint16LE();
+ header.saveHour = in->readSint16LE();
+ header.saveMinutes = in->readSint16LE();
+
+ return true;
+}
+
+Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) {
+ Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot));
+ if (saveFile == NULL) {
+ // Try to load standard save file
+ Common::String filename;
+ if (_engine->getGameId() == GID_GRANDINQUISITOR)
+ filename = Common::String::format("inqsav%u.sav", slot);
+ else if (_engine->getGameId() == GID_NEMESIS)
+ filename = Common::String::format("nemsav%u.sav", slot);
+
+ saveFile = _engine->getSearchManager()->openFile(filename);
+ if (saveFile == NULL) {
+ Common::File *tmpFile = new Common::File;
+ if (!tmpFile->open(filename)) {
+ delete tmpFile;
+ } else {
+ saveFile = tmpFile;
+ }
+ }
+
+ }
+
+ return saveFile;
+}
+
+void SaveManager::prepareSaveBuffer() {
+ delete _tempThumbnail;
+ _tempThumbnail = new Common::MemoryWriteStreamDynamic;
+ Graphics::saveThumbnail(*_tempThumbnail);
+
+ delete _tempSave;
+ _tempSave = new Common::MemoryWriteStreamDynamic;
+ _engine->getScriptManager()->serialize(_tempSave);
+}
+
+void SaveManager::flushSaveBuffer() {
+ delete _tempThumbnail;
+ _tempThumbnail = NULL;
+
+ delete _tempSave;
+ _tempSave = NULL;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/file/save_manager.h
index 43fb0c0faf..9e816373ea 100644
--- a/engines/zvision/core/save_manager.h
+++ b/engines/zvision/file/save_manager.h
@@ -24,6 +24,7 @@
#define ZVISION_SAVE_MANAGER_H
#include "common/savefile.h"
+#include "common/memstream.h"
namespace Common {
class String;
@@ -47,21 +48,32 @@ struct SaveGameHeader {
class SaveManager {
public:
- SaveManager(ZVision *engine) : _engine(engine) {}
+ SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _tempThumbnail(NULL), _lastSaveTime(0) {}
+ ~SaveManager() {
+ flushSaveBuffer();
+ }
+
+ uint32 getLastSaveTime() const {
+ return _lastSaveTime;
+ }
private:
ZVision *_engine;
+ uint32 _lastSaveTime;
static const uint32 SAVEGAME_ID;
enum {
+ SAVE_ORIGINAL = 0,
SAVE_VERSION = 1
};
+ Common::MemoryWriteStreamDynamic *_tempThumbnail;
+ Common::MemoryWriteStreamDynamic *_tempSave;
+
public:
/**
* Called every room change. Saves the state of the room just before
- * we switched rooms. Uses ZVision::generateAutoSaveFileName() to
- * create the save file name.
+ * the room changes.
*/
void autoSave();
/**
@@ -72,18 +84,23 @@ public:
* @param slot The save slot this save pertains to. Must be [1, 20]
* @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, bool useSaveBuffer);
/**
* Loads the state data from the save file that slot references. Uses
* ZVision::generateSaveFileName(slot) to get the save file name.
*
* @param slot The save slot to load. Must be [1, 20]
*/
- Common::Error loadGame(uint slot);
+ Common::Error loadGame(int slot);
+
+ Common::SeekableReadStream *getSlotFile(uint slot);
+ bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header);
+ void prepareSaveBuffer();
+ void flushSaveBuffer();
+ bool scummVMSaveLoadDialog(bool isSave);
private:
- void writeSaveGameData(Common::OutSaveFile *file);
- bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header);
+ void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer);
};
} // End of namespace ZVision
diff --git a/engines/zvision/file/search_manager.cpp b/engines/zvision/file/search_manager.cpp
new file mode 100644
index 0000000000..821b85b053
--- /dev/null
+++ b/engines/zvision/file/search_manager.cpp
@@ -0,0 +1,285 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/debug.h"
+#include "common/fs.h"
+#include "common/stream.h"
+
+#include "zvision/file/search_manager.h"
+#include "zvision/file/zfs_archive.h"
+
+namespace ZVision {
+
+SearchManager::SearchManager(const Common::String &rootPath, int depth) {
+ _root = rootPath;
+ if (_root[_root.size() - 1] == '\\' || _root[_root.size() - 1] == '/')
+ _root.deleteLastChar();
+
+ Common::FSNode fsNode(_root);
+
+ listDirRecursive(_dirList, fsNode, depth);
+
+ for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end();) {
+ if ((*it).hasSuffix("\\") || (*it).hasSuffix("/"))
+ (*it).deleteLastChar();
+
+ if (it->size() == _root.size())
+ it = _dirList.erase(it);
+ else if (it->size() > _root.size()) {
+ *it = Common::String(it->c_str() + _root.size() + 1);
+ it++;
+ } else
+ it++;
+ }
+}
+
+SearchManager::~SearchManager() {
+ Common::List<Common::Archive *>::iterator it = _archList.begin();
+ while (it != _archList.end()) {
+ delete *it;
+ it++;
+ }
+
+ _archList.clear();
+}
+
+void SearchManager::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 lowerCaseName = name;
+ lowerCaseName.toLowercase();
+
+ SearchManager::Node nod;
+ nod.name = lowerCaseName;
+ nod.arch = arch;
+
+ SearchManager::MatchList::iterator fit = _files.find(lowerCaseName);
+
+ if (fit == _files.end()) {
+ _files[lowerCaseName] = nod;
+ } else {
+ Common::SeekableReadStream *stream = fit->_value.arch->createReadStreamForMember(fit->_value.name);
+ if (stream) {
+ if (stream->size() < 10)
+ fit->_value.arch = arch;
+ delete stream;
+ } else {
+ _files[lowerCaseName] = nod;
+ }
+ }
+}
+
+Common::File *SearchManager::openFile(const Common::String &name) {
+ Common::String lowerCaseName = name;
+ lowerCaseName.toLowercase();
+
+ SearchManager::MatchList::iterator fit = _files.find(lowerCaseName);
+
+ if (fit != _files.end()) {
+ Common::File *tmp = new Common::File();
+ tmp->open(fit->_value.name, *fit->_value.arch);
+ return tmp;
+ }
+ return NULL;
+}
+
+bool SearchManager::openFile(Common::File &file, const Common::String &name) {
+ Common::String lowerCaseName = name;
+ lowerCaseName.toLowercase();
+
+ SearchManager::MatchList::iterator fit = _files.find(lowerCaseName);
+
+ if (fit != _files.end())
+ return file.open(fit->_value.name, *fit->_value.arch);
+ return false;
+}
+
+bool SearchManager::hasFile(const Common::String &name) {
+ Common::String lowerCaseName = name;
+ lowerCaseName.toLowercase();
+
+ SearchManager::MatchList::iterator fit = _files.find(lowerCaseName);
+
+ if (fit != _files.end())
+ return true;
+ return false;
+}
+
+bool SearchManager::loadZix(const Common::String &name) {
+ Common::File file;
+ if (!file.open(name))
+ return false;
+
+ Common::String line;
+
+ while (!file.eos()) {
+ line = file.readLine();
+ if (line.matchString("----------*", true))
+ break;
+ }
+
+ if (file.eos())
+ error("Corrupt ZIX file: %s", name.c_str());
+
+ Common::Array<Common::Archive *> archives;
+
+ while (!file.eos()) {
+ line = file.readLine();
+ line.trim();
+ if (line.matchString("----------*", true))
+ break;
+ else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true) || line.matchString("CD2:*", true)) {
+ Common::Archive *arc;
+
+ Common::String path(line.c_str() + 5);
+ for (uint i = 0; i < path.size(); i++)
+ if (path[i] == '\\')
+ path.setChar('/', i);
+
+ // Check if NEMESIS.ZIX/MEDIUM.ZIX refers to the znemesis folder, and
+ // check the game root folder instead
+ if (path.hasPrefix("znemesis/"))
+ path = Common::String(path.c_str() + 9);
+
+ // Check if INQUIS.ZIX refers to the ZGI folder, and check the game
+ // root folder instead
+ if (path.hasPrefix("zgi/"))
+ path = Common::String(path.c_str() + 4);
+ if (path.hasPrefix("zgi_e/"))
+ path = Common::String(path.c_str() + 6);
+
+ if (path.size() && path[0] == '.')
+ path.deleteChar(0);
+ if (path.size() && path[0] == '/')
+ path.deleteChar(0);
+ if (path.size() && path.hasSuffix("/"))
+ path.deleteLastChar();
+
+ // Handle paths in case-sensitive file systems (bug #6775)
+ if (path.size()) {
+ for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) {
+ if (path.equalsIgnoreCase(*it)) {
+ path = *it;
+ break;
+ }
+ }
+ }
+
+ if (path.matchString("*.zfs", true)) {
+ arc = new ZfsArchive(path);
+ } else {
+ path = Common::String::format("%s/%s", _root.c_str(), path.c_str());
+ arc = new Common::FSDirectory(path);
+ }
+ archives.push_back(arc);
+ }
+ }
+
+ if (file.eos())
+ error("Corrupt ZIX file: %s", name.c_str());
+
+ 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]);
+ }
+ }
+ }
+
+ return true;
+}
+
+void SearchManager::addDir(const Common::String &name) {
+ Common::String path;
+ for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it)
+ if (name.equalsIgnoreCase(*it)) {
+ path = *it;
+ break;
+ }
+
+ if (path.size() == 0)
+ return;
+
+ path = Common::String::format("%s/%s", _root.c_str(), path.c_str());
+
+ Common::FSDirectory *dir = new Common::FSDirectory(path);
+
+ Common::ArchiveMemberList list;
+ dir->listMatchingMembers(list, "*.zfs");
+
+ for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
+ Common::String flname = (*iter)->getName();
+
+ ZfsArchive *zfs = new ZfsArchive(Common::String::format("%s/%s", name.c_str(), flname.c_str()));
+
+ Common::ArchiveMemberList zfslist;
+ zfs->listMembers(zfslist);
+
+ for (Common::ArchiveMemberList::iterator ziter = zfslist.begin(); ziter != zfslist.end(); ++ziter) {
+ Common::String zfsFileName = (*ziter)->getName();
+ addFile(zfsFileName, zfs);
+ }
+ }
+
+ list.clear();
+ dir->listMembers(list);
+
+ for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
+ Common::String flname = (*iter)->getName();
+ addFile(flname, dir);
+ }
+}
+
+void SearchManager::listDirRecursive(Common::List<Common::String> &_list, const Common::FSNode &fsNode, int depth) {
+ Common::FSList fsList;
+ if (fsNode.getChildren(fsList)) {
+
+ _list.push_back(fsNode.getPath());
+
+ if (depth > 1)
+ for (Common::FSList::const_iterator it = fsList.begin(); it != fsList.end(); ++it)
+ listDirRecursive(_list, *it, depth - 1);
+ }
+}
+
+void SearchManager::listMembersWithExtension(MatchList &fileList, Common::String extension) {
+ for (SearchManager::MatchList::iterator it = _files.begin(); it != _files.end(); ++it) {
+ if (it->_key.hasSuffix(extension))
+ fileList[it->_key] = it->_value;
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/file/search_manager.h b/engines/zvision/file/search_manager.h
new file mode 100644
index 0000000000..0d0ab14d31
--- /dev/null
+++ b/engines/zvision/file/search_manager.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_SEARCH_MANAGER_H
+#define ZVISION_SEARCH_MANAGER_H
+
+#include "common/str.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/archive.h"
+#include "common/file.h"
+#include "common/list.h"
+
+namespace ZVision {
+
+class SearchManager {
+public:
+ SearchManager(const Common::String &rootPath, int depth);
+ ~SearchManager();
+
+ void addFile(const Common::String &name, Common::Archive *arch);
+ void addDir(const Common::String &name);
+
+ Common::File *openFile(const Common::String &name);
+ bool openFile(Common::File &file, const Common::String &name);
+ bool hasFile(const Common::String &name);
+
+ bool loadZix(const Common::String &name);
+
+ struct Node {
+ Common::String name;
+ Common::Archive *arch;
+ };
+
+ typedef Common::HashMap<Common::String, Node, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MatchList;
+
+ void listMembersWithExtension(MatchList &fileList, Common::String extension);
+
+private:
+
+ void listDirRecursive(Common::List<Common::String> &dirList, const Common::FSNode &fsNode, int depth);
+
+ Common::List<Common::String> _dirList;
+ Common::List<Common::Archive *> _archList;
+ Common::String _root;
+ MatchList _files;
+};
+
+}
+
+#endif // ZVISION_SEARCH_MANAGER_H
diff --git a/engines/zvision/archives/zfs_archive.cpp b/engines/zvision/file/zfs_archive.cpp
index f5fa6fc9bf..3a385cd8fd 100644
--- a/engines/zvision/archives/zfs_archive.cpp
+++ b/engines/zvision/file/zfs_archive.cpp
@@ -21,17 +21,17 @@
*/
#include "common/scummsys.h"
-
-#include "zvision/archives/zfs_archive.h"
-
#include "common/memstream.h"
#include "common/debug.h"
#include "common/file.h"
+#include "zvision/file/zfs_archive.h"
+
namespace ZVision {
ZfsArchive::ZfsArchive(const Common::String &fileName) : _fileName(fileName) {
Common::File zfsFile;
+ memset(&_header, 0, sizeof(_header));
if (!zfsFile.open(_fileName)) {
warning("ZFSArchive::ZFSArchive(): Could not find the archive file");
@@ -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,10 +138,10 @@ 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)
+ if (_header.xorKey[0] + _header.xorKey[1] + _header.xorKey[2] + _header.xorKey[3] != 0)
unXor(buffer, entryHeader->size, _header.xorKey);
return new Common::MemoryReadStream(buffer, entryHeader->size, DisposeAfterUse::YES);
@@ -153,5 +153,3 @@ void ZfsArchive::unXor(byte *buffer, uint32 length, const byte *xorKey) const {
}
} // End of namespace ZVision
-
-
diff --git a/engines/zvision/archives/zfs_archive.h b/engines/zvision/file/zfs_archive.h
index 3509cfee26..fe0221416d 100644
--- a/engines/zvision/archives/zfs_archive.h
+++ b/engines/zvision/file/zfs_archive.h
@@ -27,7 +27,6 @@
#include "common/hashmap.h"
#include "common/hash-str.h"
-
namespace Common {
class String;
}
@@ -40,7 +39,7 @@ struct ZfsHeader {
uint32 maxNameLength;
uint32 filesPerBlock;
uint32 fileCount;
- byte xorKey[4];
+ uint8 xorKey[4];
uint32 fileSectionOffset;
};
@@ -53,7 +52,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/fonts/truetype_font.cpp b/engines/zvision/fonts/truetype_font.cpp
deleted file mode 100644
index ba4d72bde8..0000000000
--- a/engines/zvision/fonts/truetype_font.cpp
+++ /dev/null
@@ -1,113 +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/fonts/truetype_font.h"
-
-#include "zvision/zvision.h"
-#include "zvision/graphics/render_manager.h"
-
-#include "common/debug.h"
-#include "common/file.h"
-#include "common/system.h"
-
-#include "graphics/font.h"
-#include "graphics/fonts/ttf.h"
-#include "graphics/surface.h"
-
-
-namespace ZVision {
-
-TruetypeFont::TruetypeFont(ZVision *engine, int32 fontHeight)
- : _fontHeight(fontHeight),
- _font(0),
- _lineHeight(0) {
-}
-
-TruetypeFont::~TruetypeFont(void) {
- delete _font;
-}
-
-bool TruetypeFont::loadFile(const Common::String &filename) {
- Common::File file;
-
- bool fileOpened = false;
- if (!Common::File::exists(filename)) {
- debug("TTF font file %s was not found. Reverting to arial.ttf", filename.c_str());
- fileOpened = file.open("arial.ttf");
- } else {
- fileOpened = file.open(filename);
- }
-
- if (!fileOpened) {
- debug("TTF file could not be opened");
- return false;
- }
-
- _font = Graphics::loadTTFFont(file, _fontHeight);
- _lineHeight = _font->getFontHeight();
-
- return true;
-}
-
-Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) {
- if (text.equals("")) {
- return nullptr;
- }
-
- Graphics::Surface *surface = new Graphics::Surface();
-
- if (!wrap) {
- int width = MIN(_font->getStringWidth(text), maxWidth);
- surface->create(width, _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
- // TODO: Add better alpha support by getting the pixels from the backbuffer.
- // However doing that requires some kind of caching system so future text doesn't try to use this text as it's alpha background.
- surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0);
-
- _font->drawString(surface, text, 0, 0, maxWidth, textColor, align);
- return surface;
- }
-
- Common::Array<Common::String> lines;
- _font->wordWrapText(text, maxWidth, lines);
-
- while (maxHeight > 0 && (int)lines.size() * _lineHeight > maxHeight) {
- lines.pop_back();
- }
- if (lines.size() == 0) {
- return nullptr;
- }
-
- surface->create(maxWidth, lines.size() * _lineHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
- surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0);
-
- int heightOffset = 0;
- for (Common::Array<Common::String>::iterator it = lines.begin(); it != lines.end(); it++) {
- _font->drawString(surface, *it, 0, 0 + heightOffset, maxWidth, textColor, align);
- heightOffset += _lineHeight;
- }
-
- return surface;
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/graphics/cursors/cursor.cpp b/engines/zvision/graphics/cursors/cursor.cpp
new file mode 100644
index 0000000000..2c011668ac
--- /dev/null
+++ b/engines/zvision/graphics/cursors/cursor.cpp
@@ -0,0 +1,96 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/scummsys.h"
+
+#include "zvision/graphics/cursors/cursor.h"
+
+#include "common/str.h"
+#include "common/file.h"
+
+namespace ZVision {
+
+ZorkCursor::ZorkCursor()
+ : _width(0),
+ _height(0),
+ _hotspotX(0),
+ _hotspotY(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))
+ error("Cursor file %s does not exist", fileName.c_str());
+
+ 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, engine->_resourcePixelFormat);
+ uint32 bytesRead = file.read(_surface.getPixels(), dataSize);
+ assert(bytesRead == dataSize);
+
+#ifndef SCUMM_LITTLE_ENDIAN
+ int16 *buffer = (int16 *)_surface.getPixels();
+ for (uint32 i = 0; i < dataSize / 2; ++i)
+ buffer[i] = FROM_LE_16(buffer[i]);
+#endif
+}
+
+ZorkCursor::ZorkCursor(const ZorkCursor &other) {
+ _width = other._width;
+ _height = other._height;
+ _hotspotX = other._hotspotX;
+ _hotspotY = other._hotspotY;
+
+ _surface.copyFrom(other._surface);
+}
+
+ZorkCursor &ZorkCursor::operator=(const ZorkCursor &other) {
+ _width = other._width;
+ _height = other._height;
+ _hotspotX = other._hotspotX;
+ _hotspotY = other._hotspotY;
+
+ _surface.free();
+ _surface.copyFrom(other._surface);
+
+ return *this;
+}
+
+ZorkCursor::~ZorkCursor() {
+ _surface.free();
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/cursors/cursor.h b/engines/zvision/graphics/cursors/cursor.h
index be9fae64da..6e0083520a 100644
--- a/engines/zvision/cursors/cursor.h
+++ b/engines/zvision/graphics/cursors/cursor.h
@@ -24,7 +24,7 @@
#define ZVISION_CURSOR_H
#include "graphics/surface.h"
-
+#include "zvision/zvision.h"
namespace Common {
class String;
@@ -39,7 +39,7 @@ namespace ZVision {
class ZorkCursor {
public:
ZorkCursor();
- ZorkCursor(const Common::String &fileName);
+ ZorkCursor(ZVision *engine, const Common::String &fileName);
ZorkCursor(const ZorkCursor &other);
~ZorkCursor();
@@ -53,12 +53,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/graphics/cursors/cursor_manager.cpp b/engines/zvision/graphics/cursors/cursor_manager.cpp
new file mode 100644
index 0000000000..eeab18f4ba
--- /dev/null
+++ b/engines/zvision/graphics/cursors/cursor_manager.cpp
@@ -0,0 +1,158 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/scummsys.h"
+
+#include "zvision/graphics/cursors/cursor_manager.h"
+
+#include "zvision/zvision.h"
+
+#include "common/system.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/cursorman.h"
+
+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"
+ };
+
+const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac011.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"
+ };
+
+const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow", "back", "down", "forw", "handpt", "handpu", "hdown", "hleft",
+ "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up"
+ };
+
+CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat pixelFormat)
+ : _engine(engine),
+ _pixelFormat(pixelFormat),
+ _cursorIsPushed(false),
+ _item(0),
+ _lastitem(0),
+ _currentCursor(CursorIndex_Idle) {
+ for (int i = 0; i < NUM_CURSORS; i++) {
+ if (_engine->getGameId() == GID_NEMESIS) {
+ Common::String name;
+ if (i == 1) {
+ // Cursors "arrowa.zcr" and "arrowb.zcr" are missing
+ _cursors[i][0] = _cursors[i][1] = ZorkCursor();
+ continue;
+ }
+ 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];
+ memset(buffer, 0, 25);
+ strncpy(buffer, _zgiCursorFileNames[i], 24);
+ buffer[3] += 2;
+ _cursors[i][1] = ZorkCursor(_engine, buffer); // Down cursor
+ }
+ }
+}
+
+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;
+ }
+ _item = id;
+ changeCursor(CursorIndex_Idle);
+ }
+}
+
+void CursorManager::initialize() {
+ changeCursor(_cursors[CursorIndex_Idle][_cursorIsPushed]);
+ showMouse(true);
+}
+
+void CursorManager::changeCursor(const ZorkCursor &cursor) {
+ CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, &_pixelFormat);
+}
+
+void CursorManager::cursorDown(bool pushed) {
+ if (_cursorIsPushed == pushed)
+ return;
+
+ _cursorIsPushed = pushed;
+
+ changeCursor(_cursors[_currentCursor][_cursorIsPushed]);
+}
+
+void CursorManager::changeCursor(int id) {
+ if (_item && (id == CursorIndex_Active ||
+ id == CursorIndex_Idle ||
+ id == CursorIndex_HandPu)) {
+ 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]);
+ }
+}
+
+int CursorManager::getCursorId(const Common::String &name) {
+ for (int i = 0; i < NUM_CURSORS; i++) {
+ if (name.equals(_cursorNames[i])) {
+ return i;
+ }
+ }
+
+ return CursorIndex_Idle;
+}
+
+void CursorManager::showMouse(bool vis) {
+ CursorMan.showMouse(vis);
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/cursors/cursor_manager.h b/engines/zvision/graphics/cursors/cursor_manager.h
index 0576517f58..35c605baf8 100644
--- a/engines/zvision/cursors/cursor_manager.h
+++ b/engines/zvision/graphics/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,11 +23,10 @@
#ifndef ZVISION_CURSOR_MANAGER_H
#define ZVISION_CURSOR_MANAGER_H
-#include "zvision/cursors/cursor.h"
+#include "zvision/graphics/cursors/cursor.h"
#include "common/str.h"
-
namespace Graphics {
struct PixelFormat;
}
@@ -37,6 +36,21 @@ namespace ZVision {
class ZVision;
/**
+ * Mostly usable cursors
+ */
+enum CursorIndex {
+ CursorIndex_Active = 0,
+ CursorIndex_DownArr = 3,
+ CursorIndex_HandPu = 6,
+ CursorIndex_Idle = 11,
+ CursorIndex_Left = 12,
+ CursorIndex_Right = 13,
+ CursorIndex_UpArr = 17,
+ CursorIndex_ItemIdle = 18,
+ CursorIndex_ItemAct = 19
+};
+
+/**
* Class to manage cursor changes. The actual changes have to be done
* through CursorMan. Otherwise the cursor will disappear after GMM
* or debug console.
@@ -44,20 +58,20 @@ class ZVision;
*/
class CursorManager {
public:
- CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat);
+ 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;
+ const Graphics::PixelFormat _pixelFormat;
bool _cursorIsPushed;
+ int _item;
+ int _lastitem;
+ int _currentCursor;
static const char *_cursorNames[];
static const char *_zgiCursorFileNames[];
@@ -68,19 +82,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 id Wanted cursor id.
+ */
+
+ void changeCursor(int id);
+
+ /**
+ * 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 name Cursor name
+ * @return Id of cursor or idle cursor id if not found
*/
- void changeCursor(const Common::String &cursorName);
+
+ int getCursorId(const Common::String &name);
+
/**
- * Parses a cursor name into a cursor file then creates and shows that cursor.
+ * Load cursor for item by id, and try to change cursor to item cursor if it's not 0
*
- * @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 id Item id or 0 for no item cursor
*/
- void changeCursor(const Common::String &cursorName, bool pushed);
+
+ 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 +113,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/graphics/effects/fog.cpp b/engines/zvision/graphics/effects/fog.cpp
new file mode 100644
index 0000000000..7b65f60f24
--- /dev/null
+++ b/engines/zvision/graphics/effects/fog.cpp
@@ -0,0 +1,173 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/graphics/effects/fog.h"
+
+#include "zvision/zvision.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/scripting/script_manager.h"
+
+namespace ZVision {
+
+FogFx::FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds):
+ GraphicsEffect(engine, key, region, ported) {
+
+ _map = Map;
+
+ _r = 0;
+ _g = 0;
+ _b = 0;
+
+ _pos = 0;
+
+ if (_engine->getSearchManager()->hasFile(clouds))
+ _engine->getRenderManager()->readImageToSurface(clouds, _fog);
+ else
+ _engine->getRenderManager()->readImageToSurface("cloud.tga", _fog);
+
+ _mp.resize(_fog.h);
+ for (int16 i = 0; i < _fog.h; i++) {
+ _mp[i].resize(_fog.w);
+ for (int16 j = 0; j < _fog.w; j++)
+ _mp[i][j] = true;
+ }
+
+ for (uint8 i = 0; i < 32; i++)
+ _colorMap[i] = 0;
+}
+
+FogFx::~FogFx() {
+ if (_map)
+ delete _map;
+
+ for (uint16 i = 0; i < _mp.size(); i++)
+ _mp[i].clear();
+ _mp.clear();
+}
+
+const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) {
+ _surface.copyFrom(srcSubRect);
+ EffectMap::iterator it = _map->begin();
+
+ uint32 cnt = 0;
+
+ for (uint16 j = 0; j < _surface.h; j++) {
+ uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j);
+
+ for (uint16 i = 0; i < _surface.w; i++) {
+ if (it->inEffect) {
+ // Not 100% equivalent, but looks nice and not buggy
+ uint8 sr, sg, sb;
+ _engine->_resourcePixelFormat.colorToRGB(lineBuf[i], sr, sg, sb);
+ uint16 fogColor = *(uint16 *)_fog.getBasePtr((i + _pos) % _fog.w, j);
+ uint8 dr, dg, db;
+ _engine->_resourcePixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db);
+ uint16 fr = dr + sr;
+ if (fr > 255)
+ fr = 255;
+ uint16 fg = dg + sg;
+ if (fg > 255)
+ fg = 255;
+ uint16 fb = db + sb;
+ if (fb > 255)
+ fb = 255;
+ lineBuf[i] = _engine->_resourcePixelFormat.RGBToColor(fr, fg, fb);
+ }
+ cnt++;
+ if (cnt >= it->count) {
+ it++;
+ cnt = 0;
+ }
+ if (it == _map->end())
+ break;
+ }
+ if (it == _map->end())
+ break;
+ }
+
+ return &_surface;
+}
+
+void FogFx::update() {
+ _pos += _engine->getScriptManager()->getStateValue(StateKey_EF9_Speed);
+ _pos %= _fog.w;
+
+ uint8 dr = _engine->getScriptManager()->getStateValue(StateKey_EF9_R);
+ uint8 dg = _engine->getScriptManager()->getStateValue(StateKey_EF9_G);
+ uint8 db = _engine->getScriptManager()->getStateValue(StateKey_EF9_B);
+ dr = CLIP((int)dr, 0, 31);
+ dg = CLIP((int)dg, 0, 31);
+ db = CLIP((int)db, 0, 31);
+
+ if (dr != _r || dg != _g || db != _b) {
+ if (_r > dr)
+ _r--;
+ else if (_r < dr)
+ _r++;
+
+ if (_g > dg)
+ _g--;
+ else if (_g < dg)
+ _g++;
+
+ if (_b > db)
+ _b--;
+ else if (_b < db)
+ _b++;
+
+ // Not 100% equivalent, but looks nice and not buggy
+
+ _colorMap[31] = _engine->_resourcePixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3);
+
+ for (uint8 i = 0; i < 31; i++) {
+ float perc = (float)i / 31.0;
+ uint8 cr = (uint8)((float)_r * perc);
+ uint8 cg = (uint8)((float)_g * perc);
+ uint8 cb = (uint8)((float)_b * perc);
+ _colorMap[i] = _engine->_resourcePixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3);
+ }
+ }
+
+ for (uint16 j = 0; j < _fog.h; j++) {
+ uint16 *pix = (uint16 *)_fog.getBasePtr(0, j);
+
+ for (uint16 i = 0; i < _fog.w; i++) {
+ if (_mp[j][i]) {
+ if ((pix[i] & 0x1F) == 0x1F) {
+ pix[i]--;
+ _mp[j][i] = false;
+ } else
+ pix[i]++;
+ } else {
+ if ((pix[i] & 0x1F) == 0) {
+ pix[i]++;
+ _mp[j][i] = true;
+ } else
+ pix[i]--;
+ }
+ }
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/graphics/effects/fog.h b/engines/zvision/graphics/effects/fog.h
new file mode 100644
index 0000000000..498347609e
--- /dev/null
+++ b/engines/zvision/graphics/effects/fog.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_FOG_H
+#define ZVISION_FOG_H
+
+#include "zvision/graphics/graphics_effect.h"
+
+namespace ZVision {
+
+class ZVision;
+
+// Used by Zork: Nemesis for the mixing chamber gas effect in the gas puzzle (location tt5e, when the blinds are down)
+class FogFx : public GraphicsEffect {
+public:
+
+ FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds);
+ ~FogFx();
+
+ const Graphics::Surface *draw(const Graphics::Surface &srcSubRect);
+
+ void update();
+
+private:
+ EffectMap *_map;
+ Graphics::Surface _fog;
+ uint8 _r, _g, _b;
+ int32 _pos;
+ Common::Array< Common::Array< bool > > _mp;
+ uint16 _colorMap[32];
+};
+} // End of namespace ZVision
+
+#endif // ZVISION_FOG_H
diff --git a/engines/zvision/graphics/effects/light.cpp b/engines/zvision/graphics/effects/light.cpp
new file mode 100644
index 0000000000..39341687f8
--- /dev/null
+++ b/engines/zvision/graphics/effects/light.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/graphics/effects/light.h"
+
+#include "zvision/zvision.h"
+#include "zvision/graphics/render_manager.h"
+
+namespace ZVision {
+
+LightFx::LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD, int8 maxD):
+ GraphicsEffect(engine, key, region, ported) {
+ _map = Map;
+ _delta = delta;
+ _up = true;
+ _pos = 0;
+
+ _minD = minD;
+ if (_minD < -delta)
+ _minD = -delta;
+
+ _maxD = maxD;
+ if (_maxD > delta)
+ _maxD = delta;
+}
+
+LightFx::~LightFx() {
+ if (_map)
+ delete _map;
+}
+
+const Graphics::Surface *LightFx::draw(const Graphics::Surface &srcSubRect) {
+ _surface.copyFrom(srcSubRect);
+ EffectMap::iterator it = _map->begin();
+ uint32 cnt = 0;
+
+ uint32 dcolor = 0;
+
+ if (_pos < 0) {
+ uint8 cc = ((-_pos) & 0x1F) << 3;
+ dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc);
+ } else {
+ uint8 cc = (_pos & 0x1F) << 3;
+ dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc);
+ }
+
+ for (uint16 j = 0; j < _surface.h; j++) {
+ uint16 *lineBuf = (uint16 *)_surface.getBasePtr(0, j);
+
+ for (uint16 i = 0; i < _surface.w; i++) {
+ if (it->inEffect) {
+ if (_pos < 0) {
+ lineBuf[i] -= dcolor;
+ } else {
+ lineBuf[i] += dcolor;
+ }
+ }
+ cnt++;
+ if (cnt >= it->count) {
+ it++;
+ cnt = 0;
+ }
+ if (it == _map->end())
+ break;
+ }
+ if (it == _map->end())
+ break;
+ }
+
+ return &_surface;
+}
+
+void LightFx::update() {
+ if (_up)
+ _pos++;
+ else
+ _pos--;
+
+ if (_pos <= _minD) {
+ _up = !_up;
+ _pos = _minD;
+ } else if (_pos >= _maxD) {
+ _up = !_up;
+ _pos = _maxD;
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/graphics/effects/light.h b/engines/zvision/graphics/effects/light.h
new file mode 100644
index 0000000000..cd73a585ec
--- /dev/null
+++ b/engines/zvision/graphics/effects/light.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef LIGHTFX_H_INCLUDED
+#define LIGHTFX_H_INCLUDED
+
+#include "zvision/graphics/graphics_effect.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class LightFx : public GraphicsEffect {
+public:
+
+ LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD = -127, int8 maxD = 127);
+ ~LightFx();
+
+ const Graphics::Surface *draw(const Graphics::Surface &srcSubRect);
+
+ void update();
+
+private:
+ EffectMap *_map;
+ int32 _delta;
+ bool _up;
+ int32 _pos;
+
+ int8 _minD;
+ int8 _maxD;
+};
+} // End of namespace ZVision
+
+#endif // LIGHTFX_H_INCLUDED
diff --git a/engines/zvision/graphics/effects/wave.cpp b/engines/zvision/graphics/effects/wave.cpp
new file mode 100644
index 0000000000..d2887b3112
--- /dev/null
+++ b/engines/zvision/graphics/effects/wave.cpp
@@ -0,0 +1,145 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/graphics/effects/wave.h"
+
+#include "zvision/zvision.h"
+#include "zvision/graphics/render_manager.h"
+
+namespace ZVision {
+
+WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd):
+ GraphicsEffect(engine, key, region, ported) {
+
+ _frame = 0;
+ _frameCount = frames;
+
+ _ampls.resize(_frameCount);
+ _halfWidth = _region.width() / 2;
+ _halfHeight = _region.height() / 2;
+
+ int32 frmsize = _halfWidth * _halfHeight;
+
+ float phase = 0;
+
+ int16 quarterWidth = _halfWidth / 2;
+ int16 quarterHeight = _halfHeight / 2;
+
+ for (int16 i = 0; i < _frameCount; i++) {
+ _ampls[i].resize(frmsize);
+
+ for (int16 y = 0; y < _halfHeight; y++)
+ for (int16 x = 0; x < _halfWidth; x++) {
+ int16 dx = (x - quarterWidth);
+ int16 dy = (y - quarterHeight);
+
+ _ampls[i][x + y * _halfWidth] = (int8)(ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase));
+ }
+ phase += spd;
+ }
+}
+
+WaveFx::~WaveFx() {
+ for (uint16 i = 0; i < _ampls.size(); i++)
+ _ampls[i].clear();
+ _ampls.clear();
+}
+
+const Graphics::Surface *WaveFx::draw(const Graphics::Surface &srcSubRect) {
+ for (int16 y = 0; y < _halfHeight; y++) {
+ uint16 *abc = (uint16 *)_surface.getBasePtr(0, y);
+ uint16 *abc2 = (uint16 *)_surface.getBasePtr(0, _halfHeight + y);
+ uint16 *abc3 = (uint16 *)_surface.getBasePtr(_halfWidth, y);
+ uint16 *abc4 = (uint16 *)_surface.getBasePtr(_halfWidth, _halfHeight + y);
+
+ for (int16 x = 0; x < _halfWidth; x++) {
+ int8 amnt = _ampls[_frame][x + _halfWidth * y];
+
+ int16 nX = x + amnt;
+ int16 nY = y + amnt;
+
+ if (nX < 0)
+ nX = 0;
+ if (nX >= _region.width())
+ nX = _region.width() - 1;
+ if (nY < 0)
+ nY = 0;
+ if (nY >= _region.height())
+ nY = _region.height() - 1;
+ *abc = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
+
+ nX = x + amnt + _halfWidth;
+ nY = y + amnt;
+
+ if (nX < 0)
+ nX = 0;
+ if (nX >= _region.width())
+ nX = _region.width() - 1;
+ if (nY < 0)
+ nY = 0;
+ if (nY >= _region.height())
+ nY = _region.height() - 1;
+ *abc3 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
+
+ nX = x + amnt;
+ nY = y + amnt + _halfHeight;
+
+ if (nX < 0)
+ nX = 0;
+ if (nX >= _region.width())
+ nX = _region.width() - 1;
+ if (nY < 0)
+ nY = 0;
+ if (nY >= _region.height())
+ nY = _region.height() - 1;
+ *abc2 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
+
+ nX = x + amnt + _halfWidth;
+ nY = y + amnt + _halfHeight;
+
+ if (nX < 0)
+ nX = 0;
+ if (nX >= _region.width())
+ nX = _region.width() - 1;
+ if (nY < 0)
+ nY = 0;
+ if (nY >= _region.height())
+ nY = _region.height() - 1;
+ *abc4 = *(const uint16 *)srcSubRect.getBasePtr(nX, nY);
+
+ abc++;
+ abc2++;
+ abc3++;
+ abc4++;
+ }
+ }
+
+ return &_surface;
+}
+
+void WaveFx::update() {
+ _frame = (_frame + 1) % _frameCount;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/graphics/effects/wave.h b/engines/zvision/graphics/effects/wave.h
new file mode 100644
index 0000000000..8e912372d7
--- /dev/null
+++ b/engines/zvision/graphics/effects/wave.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WAVEFX_H_INCLUDED
+#define WAVEFX_H_INCLUDED
+
+#include "common/array.h"
+#include "zvision/graphics/graphics_effect.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class WaveFx : public GraphicsEffect {
+public:
+
+ WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd);
+ ~WaveFx();
+
+ const Graphics::Surface *draw(const Graphics::Surface &srcSubRect);
+
+ void update();
+
+private:
+ int16 _frame;
+ int16 _frameCount;
+ int16 _halfWidth, _halfHeight;
+ Common::Array< Common::Array< int8 > > _ampls;
+};
+} // End of namespace ZVision
+
+#endif // WAVEFX_H_INCLUDED
diff --git a/engines/zvision/graphics/graphics_effect.h b/engines/zvision/graphics/graphics_effect.h
new file mode 100644
index 0000000000..bfa266b11d
--- /dev/null
+++ b/engines/zvision/graphics/graphics_effect.h
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GRAPHICS_EFFECT_H_INCLUDED
+#define GRAPHICS_EFFECT_H_INCLUDED
+
+#include "common/rect.h"
+#include "common/list.h"
+#include "graphics/surface.h"
+
+#include "zvision/zvision.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class GraphicsEffect {
+public:
+
+ GraphicsEffect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) {
+ _surface.create(_region.width(), _region.height(), _engine->_resourcePixelFormat);
+ }
+ virtual ~GraphicsEffect() {}
+
+ uint32 getKey() {
+ return _key;
+ }
+
+ Common::Rect getRegion() {
+ return _region;
+ }
+
+ bool isPort() {
+ return _ported;
+ }
+
+ virtual const Graphics::Surface *draw(const Graphics::Surface &srcSubRect) {
+ return &_surface;
+ }
+
+ virtual void update() {}
+
+protected:
+ ZVision *_engine;
+ uint32 _key;
+ Common::Rect _region;
+ bool _ported;
+ Graphics::Surface _surface;
+
+// Static member functions
+public:
+
+};
+
+struct EffectMapUnit {
+ uint32 count;
+ bool inEffect;
+};
+
+typedef Common::List<EffectMapUnit> EffectMap;
+
+} // End of namespace ZVision
+
+#endif // GRAPHICS_EFFECT_H_INCLUDED
diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp
index aed30ea12c..ce0a02a1ad 100644
--- a/engines/zvision/graphics/render_manager.cpp
+++ b/engines/zvision/graphics/render_manager.cpp
@@ -1,30 +1,33 @@
/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
#include "common/scummsys.h"
+#include "zvision/zvision.h"
#include "zvision/graphics/render_manager.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/text/text.h"
-#include "zvision/utility/lzss_read_stream.h"
+#include "zvision/file/lzss_read_stream.h"
#include "common/file.h"
#include "common/system.h"
@@ -34,200 +37,153 @@
#include "image/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, bool doubleFPS)
+ : _engine(engine),
+ _system(engine->_system),
+ _screenCenterX(_workingWindow.width() / 2),
+ _screenCenterY(_workingWindow.height() / 2),
+ _workingWindow(workingWindow),
+ _pixelFormat(pixelFormat),
+ _backgroundWidth(0),
+ _backgroundHeight(0),
+ _backgroundOffset(0),
+ _renderTable(_workingWindow.width(), _workingWindow.height()),
+ _doubleFPS(doubleFPS),
+ _subid(0) {
+
+ _backgroundSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
+ _effectSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
+ _warpedSceneSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat);
+ _menuSurface.create(windowWidth, workingWindow.top, _pixelFormat);
+
+ _menuArea = Common::Rect(0, 0, windowWidth, workingWindow.top);
+
+ initSubArea(windowWidth, windowHeight, workingWindow);
}
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);
- }
+ _currentBackgroundImage.free();
+ _backgroundSurface.free();
+ _effectSurface.free();
+ _warpedSceneSurface.free();
+ _menuSurface.free();
+ _subtitleSurface.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());
- }
-
- // Translate the working window dirty rect to screen coords
- _workingWindowDirtyRect.translate(_workingWindow.left, _workingWindow.top);
- // Then extend the backbuffer dirty rect to contain it
- if (_backBufferDirtyRect.isEmpty()) {
- _backBufferDirtyRect = _workingWindowDirtyRect;
- } else {
- _backBufferDirtyRect.extend(_workingWindowDirtyRect);
- }
-
- // Clear the dirty rect
- _workingWindowDirtyRect = Common::Rect();
- }
-
- // TODO: Add menu rendering
+void RenderManager::renderSceneToScreen() {
+ Graphics::Surface *out = &_warpedSceneSurface;
+ Graphics::Surface *in = &_backgroundSurface;
+ Common::Rect outWndDirtyRect;
- // Render alpha entries
- processAlphaEntries();
+ // If we have graphical effects, we apply them using a temporary buffer
+ if (!_effects.empty()) {
+ bool copied = false;
+ Common::Rect windowRect(_workingWindow.width(), _workingWindow.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();
- }
-}
+ for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) {
+ Common::Rect rect = (*it)->getRegion();
+ Common::Rect screenSpaceLocation = 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();
+ if ((*it)->isPort()) {
+ screenSpaceLocation = transformBackgroundSpaceRectToScreenSpace(screenSpaceLocation);
+ }
- 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;
+ if (windowRect.intersects(screenSpaceLocation)) {
+ if (!copied) {
+ copied = true;
+ _effectSurface.copyFrom(_backgroundSurface);
+ in = &_effectSurface;
+ }
+ const Graphics::Surface *post;
+ if ((*it)->isPort())
+ post = (*it)->draw(_currentBackgroundImage.getSubArea(rect));
+ else
+ post = (*it)->draw(_effectSurface.getSubArea(rect));
+ Common::Rect empty;
+ blitSurfaceToSurface(*post, empty, _effectSurface, screenSpaceLocation.left, screenSpaceLocation.top);
+ screenSpaceLocation.clip(windowRect);
+ if (_backgroundSurfaceDirtyRect .isEmpty()) {
+ _backgroundSurfaceDirtyRect = screenSpaceLocation;
+ } else {
+ _backgroundSurfaceDirtyRect.extend(screenSpaceLocation);
}
}
-
- destOffset += _backBuffer.w;
- sourceOffset += iter->_value.width;
}
+ }
- 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));
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
+ if (!_backgroundSurfaceDirtyRect.isEmpty()) {
+ _renderTable.mutateImage(&_warpedSceneSurface, in);
+ out = &_warpedSceneSurface;
+ outWndDirtyRect = Common::Rect(_workingWindow.width(), _workingWindow.height());
}
+ } else {
+ out = in;
+ outWndDirtyRect = _backgroundSurfaceDirtyRect;
}
-}
-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();
-
- for (uint32 i = 0; i < workingWindowSize; ++i) {
- bufferPtr[i] = colorIn565;
+ if (!outWndDirtyRect.isEmpty()) {
+ Common::Rect rect(
+ outWndDirtyRect.left + _workingWindow.left,
+ outWndDirtyRect.top + _workingWindow.top,
+ outWndDirtyRect.left + _workingWindow.left + outWndDirtyRect.width(),
+ outWndDirtyRect.top + _workingWindow.top + outWndDirtyRect.height()
+ );
+ copyToScreen(*out, rect, outWndDirtyRect.left, outWndDirtyRect.top);
}
}
-void RenderManager::renderSubRectToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) {
- int16 subRectX = 0;
- int16 subRectY = 0;
-
- // 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;
- }
-
- // 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;
- }
-
- if (wrap) {
- _backgroundWidth = surface.w;
- _backgroundHeight = surface.h;
-
- if (destinationX > 0) {
- // Move destinationX to 0
- subRectX = surface.w - destinationX;
- destinationX = 0;
- }
-
- if (destinationY > 0) {
- // Move destinationY to 0
- subRectY = surface.h - destinationY;
- destinationY = 0;
- }
- }
+void RenderManager::copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop) {
+ // Convert the surface to RGB565, if needed
+ Graphics::Surface *outSurface = surface.convertTo(_engine->_screenPixelFormat);
+ _system->copyRectToScreen(outSurface->getBasePtr(srcLeft, srcTop),
+ outSurface->pitch,
+ rect.left,
+ rect.top,
+ rect.width(),
+ rect.height());
+ outSurface->free();
+ delete outSurface;
+}
- // Clip subRect to working window bounds
- Common::Rect subRect(subRectX, subRectY, subRectX + _workingWidth, subRectY + _workingHeight);
+void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) {
+ Graphics::Surface surface;
+ readImageToSurface(fileName, surface);
- if (!wrap) {
- // Clip to image bounds
- subRect.clip(surface.w, surface.h);
- }
+ blitSurfaceToBkg(surface, destX, destY);
+ surface.free();
+}
- // Check destRect for validity
- if (!subRect.isValidRect() || subRect.isEmpty())
- return;
+void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, uint32 keycolor) {
+ Graphics::Surface surface;
+ readImageToSurface(fileName, surface);
- copyRectToWorkingWindow((const uint16 *)surface.getBasePtr(subRect.left, subRect.top), destinationX, destinationY, surface.w, subRect.width(), subRect.height());
+ blitSurfaceToBkg(surface, destX, destY, keycolor);
+ surface.free();
}
-void RenderManager::renderImageToScreen(const Common::String &fileName, int16 destinationX, int16 destinationY, bool wrap) {
+void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, int16 keyX, int16 keyY) {
Graphics::Surface surface;
readImageToSurface(fileName, surface);
- renderSubRectToScreen(surface, destinationX, destinationY, wrap);
-}
+ uint16 keycolor = *(uint16 *)surface.getBasePtr(keyX, keyY);
-void RenderManager::renderImageToScreen(Graphics::Surface &surface, int16 destinationX, int16 destinationY, bool wrap) {
- renderSubRectToScreen(surface, destinationX, destinationY, wrap);
+ blitSurfaceToBkg(surface, destX, destY, keycolor);
+ surface.free();
}
void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) {
+ bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA;
+ readImageToSurface(fileName, destination, isTransposed);
+}
+
+void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) {
Common::File file;
- if (!file.open(fileName)) {
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
warning("Could not open file %s", fileName.c_str());
return;
}
@@ -240,10 +196,8 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics:
uint32 imageHeight;
Image::TGADecoder tga;
uint16 *buffer;
- bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA;
// All ZVision images are in RGB 555
- Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
- destination.format = pixelFormat555;
+ destination.format = _engine->_resourcePixelFormat;
bool isTGZ;
@@ -252,13 +206,17 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics:
isTGZ = true;
// TGZ files have a header and then Bitmap data that is compressed with LZSS
- uint32 decompressedSize = file.readSint32LE();
+ uint32 decompressedSize = file.readSint32LE() / 2;
imageWidth = file.readSint32LE();
imageHeight = file.readSint32LE();
LzssReadStream lzssStream(&file);
buffer = (uint16 *)(new uint16[decompressedSize]);
- lzssStream.read(buffer, decompressedSize);
+ lzssStream.read(buffer, 2 * decompressedSize);
+#ifndef SCUMM_LITTLE_ENDIAN
+ for (uint32 i = 0; i < decompressedSize; ++i)
+ buffer[i] = FROM_LE_16(buffer[i]);
+#endif
} else {
isTGZ = false;
@@ -279,7 +237,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;
@@ -288,12 +246,12 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics:
// 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);
+ destination.create(imageWidth, imageHeight, _engine->_resourcePixelFormat);
}
// 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) {
@@ -304,7 +262,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics:
}
}
} else {
- memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel);
+ memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * destination.format.bytesPerPixel);
}
// Cleanup
@@ -313,214 +271,947 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics:
} else {
tga.destroy();
}
-
- // Convert in place to RGB 565 from RGB 555
- 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(_backgroundOffset - _screenCenterX, 0));
+ } else if (state == RenderTable::TILT) {
+ newPoint += (Common::Point(0, _backgroundOffset - _screenCenterY));
}
- destOffset += _workingWidth;
- sourceOffset += imageWidth;
+ if (_backgroundWidth)
+ newPoint.x %= _backgroundWidth;
+ if (_backgroundHeight)
+ newPoint.y %= _backgroundHeight;
+
+ if (newPoint.x < 0)
+ newPoint.x += _backgroundWidth;
+ if (newPoint.y < 0)
+ newPoint.y += _backgroundHeight;
+
+ return newPoint;
+ } else {
+ return Common::Point(0, 0);
}
+}
+
+RenderTable *RenderManager::getRenderTable() {
+ return &_renderTable;
+}
+
+void RenderManager::setBackgroundImage(const Common::String &fileName) {
+ readImageToSurface(fileName, _currentBackgroundImage);
+ _backgroundWidth = _currentBackgroundImage.w;
+ _backgroundHeight = _currentBackgroundImage.h;
+ _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
+}
+
+void RenderManager::setBackgroundPosition(int offset) {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+ if (state == RenderTable::TILT || state == RenderTable::PANORAMA)
+ if (_backgroundOffset != offset)
+ _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
+ _backgroundOffset = offset;
- if (_workingWindowDirtyRect.isEmpty()) {
- _workingWindowDirtyRect = Common::Rect(destX, destY, destX + width, destY + height);
+ _engine->getScriptManager()->setStateValue(StateKey_ViewPos, offset);
+}
+
+uint32 RenderManager::getCurrentBackgroundOffset() {
+ RenderTable::RenderState state = _renderTable.getRenderState();
+
+ if (state == RenderTable::PANORAMA) {
+ return _backgroundOffset;
+ } else if (state == RenderTable::TILT) {
+ return _backgroundOffset;
} else {
- _workingWindowDirtyRect.extend(Common::Rect(destX, destY, destX + width, destY + height));
+ return 0;
+ }
+}
+
+Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) {
+ Graphics::Surface *tranposedSurface = new Graphics::Surface();
+ tranposedSurface->create(surface->h, surface->w, surface->format);
+
+ const uint16 *source = (const uint16 *)surface->getPixels();
+ uint16 *dest = (uint16 *)tranposedSurface->getPixels();
+
+ for (uint32 y = 0; y < tranposedSurface->h; ++y) {
+ uint32 columnIndex = y * tranposedSurface->w;
+
+ for (uint32 x = 0; x < tranposedSurface->w; ++x) {
+ dest[columnIndex + x] = source[x * surface->w + y];
+ }
}
- // 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);
+ return tranposedSurface;
}
-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::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight) {
+ assert(bytesPerPixel == 1 || bytesPerPixel == 2);
- uint32 sourceOffset = 0;
- uint32 destOffset = 0;
- uint16 *surfacePtr = (uint16 *)entry.data->getPixels();
+ const float xscale = (float)srcWidth / (float)dstWidth;
+ const float yscale = (float)srcHeight / (float)dstHeight;
- for (int32 y = 0; y < height; ++y) {
- for (int32 x = 0; x < width; ++x) {
- surfacePtr[destOffset + x] = buffer[sourceOffset + x];
+ if (bytesPerPixel == 1) {
+ const byte *srcPtr = (const byte *)src;
+ byte *dstPtr = (byte *)dst;
+ for (uint32 y = 0; y < dstHeight; ++y) {
+ for (uint32 x = 0; x < dstWidth; ++x) {
+ *dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth];
+ dstPtr++;
+ }
}
+ } else if (bytesPerPixel == 2) {
+ const uint16 *srcPtr = (const uint16 *)src;
+ uint16 *dstPtr = (uint16 *)dst;
+ for (uint32 y = 0; y < dstHeight; ++y) {
+ for (uint32 x = 0; x < dstWidth; ++x) {
+ *dstPtr = srcPtr[(int)(x * xscale) + (int)(y * yscale) * srcWidth];
+ dstPtr++;
+ }
+ }
+ }
+}
+
+void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) {
+ 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;
+
+ Graphics::Surface *srcAdapted = src.convertTo(dst.format);
+
+ // Copy srcRect from src surface to dst surface
+ const byte *srcBuffer = (const byte *)srcAdapted->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) {
+ srcAdapted->free();
+ delete srcAdapted;
+ return;
+ }
+
+ byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy);
- destOffset += width;
- sourceOffset += imageWidth;
+ int32 w = srcRect.width();
+ int32 h = srcRect.height();
+
+ for (int32 y = 0; y < h; y++) {
+ memcpy(dstBuffer, srcBuffer, w * srcAdapted->format.bytesPerPixel);
+ srcBuffer += srcAdapted->pitch;
+ dstBuffer += dst.pitch;
}
- _alphaDataEntries[idNumber] = entry;
+ srcAdapted->free();
+ delete srcAdapted;
}
-Common::Rect RenderManager::renderTextToWorkingWindow(uint32 idNumber, const Common::String &text, TruetypeFont *font, int destX, int destY, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap) {
- AlphaDataEntry entry;
- entry.alphaColor = 0;
- entry.destX = destX;
- entry.destY = destY;
+void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) {
+ 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;
- // 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;
+ Graphics::Surface *srcAdapted = src.convertTo(dst.format);
+ uint32 keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1);
- _alphaDataEntries[idNumber] = entry;
+ // Copy srcRect from src surface to dst surface
+ const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top);
- return Common::Rect(destX, destY, destX + entry.width, destY + entry.height);
-}
+ int xx = _x;
+ int yy = _y;
-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));
+ if (xx < 0)
+ xx = 0;
+ if (yy < 0)
+ yy = 0;
- RenderTable::RenderState state = _renderTable.getRenderState();
- if (state == RenderTable::PANORAMA || state == RenderTable::TILT) {
- newPoint = _renderTable.convertWarpedCoordToFlatCoord(newPoint);
+ if (_x >= dst.w || _y >= dst.h) {
+ srcAdapted->free();
+ delete srcAdapted;
+ return;
}
- if (state == RenderTable::PANORAMA) {
- newPoint -= (Common::Point(_screenCenterX, 0) - _backgroundOffset);
- } else if (state == RenderTable::TILT) {
- newPoint -= (Common::Point(0, _screenCenterY) - _backgroundOffset);
+ byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy);
+
+ int32 w = srcRect.width();
+ int32 h = srcRect.height();
+
+ for (int32 y = 0; y < h; y++) {
+ switch (srcAdapted->format.bytesPerPixel) {
+ case 1: {
+ const uint *srcTemp = (const uint *)srcBuffer;
+ uint *dstTemp = (uint *)dstBuffer;
+ for (int32 x = 0; x < w; x++) {
+ if (*srcTemp != keycolor)
+ *dstTemp = *srcTemp;
+ srcTemp++;
+ dstTemp++;
+ }
+ }
+ break;
+
+ case 2: {
+ const uint16 *srcTemp = (const uint16 *)srcBuffer;
+ uint16 *dstTemp = (uint16 *)dstBuffer;
+ for (int32 x = 0; x < w; x++) {
+ if (*srcTemp != keycolor)
+ *dstTemp = *srcTemp;
+ srcTemp++;
+ dstTemp++;
+ }
+ }
+ break;
+
+ case 4: {
+ const uint32 *srcTemp = (const uint32 *)srcBuffer;
+ uint32 *dstTemp = (uint32 *)dstBuffer;
+ for (int32 x = 0; x < w; x++) {
+ if (*srcTemp != keycolor)
+ *dstTemp = *srcTemp;
+ srcTemp++;
+ dstTemp++;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ srcBuffer += srcAdapted->pitch;
+ dstBuffer += dst.pitch;
}
- 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;
+ srcAdapted->free();
+ delete srcAdapted;
+}
+
+void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey) {
+ Common::Rect empt;
+ if (colorkey >= 0)
+ blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y, colorkey);
+ else
+ blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y);
+ Common::Rect dirty(src.w, src.h);
+ dirty.translate(x, y);
+ if (_backgroundDirtyRect.isEmpty())
+ _backgroundDirtyRect = dirty;
+ else
+ _backgroundDirtyRect.extend(dirty);
+}
+
+void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey) {
+ if (src.w == _dstRect.width() && src.h == _dstRect.height()) {
+ blitSurfaceToBkg(src, _dstRect.left, _dstRect.top, colorkey);
+ } else {
+ Graphics::Surface *tmp = new Graphics::Surface;
+ tmp->create(_dstRect.width(), _dstRect.height(), src.format);
+ scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height());
+ blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top, colorkey);
+ tmp->free();
+ delete tmp;
+ }
+}
- return newPoint;
+void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey) {
+ Common::Rect empt;
+ if (colorkey >= 0)
+ blitSurfaceToSurface(src, empt, _menuSurface, x, y, colorkey);
+ else
+ blitSurfaceToSurface(src, empt, _menuSurface, x, y);
+ Common::Rect dirty(src.w, src.h);
+ dirty.translate(x, y);
+ if (_menuSurfaceDirtyRect.isEmpty())
+ _menuSurfaceDirtyRect = dirty;
+ else
+ _menuSurfaceDirtyRect.extend(dirty);
}
-const Common::Point RenderManager::imageSpaceToWorkingWindowSpace(const Common::Point &point) {
- Common::Point newPoint(point);
+Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) {
+ Common::Rect dst = rect;
+ dst.clip(_backgroundWidth, _backgroundHeight);
+
+ if (dst.isEmpty() || !dst.isValidRect())
+ return NULL;
+ Graphics::Surface *srf = new Graphics::Surface;
+ srf->create(dst.width(), dst.height(), _currentBackgroundImage.format);
+
+ srf->copyRectToSurface(_currentBackgroundImage, 0, 0, Common::Rect(dst));
+
+ return srf;
+}
+
+Graphics::Surface *RenderManager::loadImage(Common::String file) {
+ Graphics::Surface *tmp = new Graphics::Surface;
+ readImageToSurface(file, *tmp);
+ return tmp;
+}
+
+Graphics::Surface *RenderManager::loadImage(Common::String file, bool transposed) {
+ Graphics::Surface *tmp = new Graphics::Surface;
+ readImageToSurface(file, *tmp, transposed);
+ return tmp;
+}
+
+void RenderManager::prepareBackground() {
+ _backgroundDirtyRect.clip(_backgroundWidth, _backgroundHeight);
RenderTable::RenderState state = _renderTable.getRenderState();
+
if (state == RenderTable::PANORAMA) {
- newPoint += (Common::Point(_screenCenterX, 0) - _backgroundOffset);
+ // Calculate the visible portion of the background
+ Common::Rect viewPort(_workingWindow.width(), _workingWindow.height());
+ viewPort.translate(-(_screenCenterX - _backgroundOffset), 0);
+ Common::Rect drawRect = _backgroundDirtyRect;
+ drawRect.clip(viewPort);
+
+ // Render the visible portion
+ if (!drawRect.isEmpty()) {
+ blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - _backgroundOffset + drawRect.left, drawRect.top);
+ }
+
+ // Mark the dirty portion of the surface
+ _backgroundSurfaceDirtyRect = _backgroundDirtyRect;
+ _backgroundSurfaceDirtyRect.translate(_screenCenterX - _backgroundOffset, 0);
+
+ // Panorama mode allows the user to spin in circles. Therefore, we need to render
+ // the portion of the image that wrapped to the other side of the screen
+ if (_backgroundOffset < _screenCenterX) {
+ viewPort.moveTo(-(_screenCenterX - (_backgroundOffset + _backgroundWidth)), 0);
+ drawRect = _backgroundDirtyRect;
+ drawRect.clip(viewPort);
+
+ if (!drawRect.isEmpty())
+ blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - (_backgroundOffset + _backgroundWidth) + drawRect.left, drawRect.top);
+
+ Common::Rect tmp = _backgroundDirtyRect;
+ tmp.translate(_screenCenterX - (_backgroundOffset + _backgroundWidth), 0);
+ if (!tmp.isEmpty())
+ _backgroundSurfaceDirtyRect.extend(tmp);
+
+ } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) {
+ viewPort.moveTo(-(_screenCenterX + _backgroundWidth - _backgroundOffset), 0);
+ drawRect = _backgroundDirtyRect;
+ drawRect.clip(viewPort);
+
+ if (!drawRect.isEmpty())
+ blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX + _backgroundWidth - _backgroundOffset + drawRect.left, drawRect.top);
+
+ Common::Rect tmp = _backgroundDirtyRect;
+ tmp.translate(_screenCenterX + _backgroundWidth - _backgroundOffset, 0);
+ if (!tmp.isEmpty())
+ _backgroundSurfaceDirtyRect.extend(tmp);
+
+ }
} else if (state == RenderTable::TILT) {
- newPoint += (Common::Point(0, _screenCenterY) - _backgroundOffset);
+ // Tilt doesn't allow wrapping, so we just do a simple clip
+ Common::Rect viewPort(_workingWindow.width(), _workingWindow.height());
+ viewPort.translate(0, -(_screenCenterY - _backgroundOffset));
+ Common::Rect drawRect = _backgroundDirtyRect;
+ drawRect.clip(viewPort);
+ if (!drawRect.isEmpty())
+ blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, drawRect.left, _screenCenterY - _backgroundOffset + drawRect.top);
+
+ // Mark the dirty portion of the surface
+ _backgroundSurfaceDirtyRect = _backgroundDirtyRect;
+ _backgroundSurfaceDirtyRect.translate(0, _screenCenterY - _backgroundOffset);
+
+ } else {
+ if (!_backgroundDirtyRect.isEmpty())
+ blitSurfaceToSurface(_currentBackgroundImage, _backgroundDirtyRect, _backgroundSurface, _backgroundDirtyRect.left, _backgroundDirtyRect.top);
+ _backgroundSurfaceDirtyRect = _backgroundDirtyRect;
}
- return newPoint;
+ // Clear the dirty rect since everything is clean now
+ _backgroundDirtyRect = Common::Rect();
+
+ _backgroundSurfaceDirtyRect.clip(_workingWindow.width(), _workingWindow.height());
+}
+
+void RenderManager::clearMenuSurface() {
+ _menuSurfaceDirtyRect = Common::Rect(0, 0, _menuSurface.w, _menuSurface.h);
+ _menuSurface.fillRect(_menuSurfaceDirtyRect, 0);
}
-bool RenderManager::clipRectToWorkingWindow(Common::Rect &rect) {
- if (!_workingWindow.contains(rect)) {
- return false;
+void RenderManager::clearMenuSurface(const Common::Rect &r) {
+ if (_menuSurfaceDirtyRect.isEmpty())
+ _menuSurfaceDirtyRect = r;
+ else
+ _menuSurfaceDirtyRect.extend(r);
+ _menuSurface.fillRect(r, 0);
+}
+
+void RenderManager::renderMenuToScreen() {
+ if (!_menuSurfaceDirtyRect.isEmpty()) {
+ _menuSurfaceDirtyRect.clip(Common::Rect(_menuSurface.w, _menuSurface.h));
+ if (!_menuSurfaceDirtyRect.isEmpty()) {
+ Common::Rect rect(
+ _menuSurfaceDirtyRect.left + _menuArea.left,
+ _menuSurfaceDirtyRect.top + _menuArea.top,
+ _menuSurfaceDirtyRect.left + _menuArea.left + _menuSurfaceDirtyRect.width(),
+ _menuSurfaceDirtyRect.top + _menuArea.top + _menuSurfaceDirtyRect.height()
+ );
+ copyToScreen(_menuSurface, rect, _menuSurfaceDirtyRect.left, _menuSurfaceDirtyRect.top);
+ }
+ _menuSurfaceDirtyRect = Common::Rect();
}
+}
- // 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::initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow) {
+ _workingWindow = workingWindow;
+
+ _subtitleSurface.free();
+
+ _subtitleSurface.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat);
+ _subtitleArea = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight);
}
-RenderTable *RenderManager::getRenderTable() {
- return &_renderTable;
+uint16 RenderManager::createSubArea(const Common::Rect &area) {
+ _subid++;
+
+ OneSubtitle sub;
+ sub.redraw = false;
+ sub.timer = -1;
+ sub.todelete = false;
+ sub.r = area;
+
+ _subsList[_subid] = sub;
+
+ return _subid;
}
-void RenderManager::setBackgroundImage(const Common::String &fileName) {
- readImageToSurface(fileName, _currentBackground);
+uint16 RenderManager::createSubArea() {
+ Common::Rect r(_subtitleArea.left, _subtitleArea.top, _subtitleArea.right, _subtitleArea.bottom);
+ r.translate(-_workingWindow.left, -_workingWindow.top);
+ return createSubArea(r);
+}
- moveBackground(0);
+void RenderManager::deleteSubArea(uint16 id) {
+ if (_subsList.contains(id))
+ _subsList[id].todelete = true;
}
-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::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)) {
+ OneSubtitle *sub = &_subsList[id];
+ sub->txt = txt;
+ sub->redraw = true;
}
}
-void RenderManager::setBackgroundVelocity(int velocity) {
- // setBackgroundVelocity(0) will be called quite often, so make sure
- // _backgroundInverseVelocity isn't already 0 to prevent an extraneous assignment
- if (velocity == 0) {
- if (_backgroundInverseVelocity != 0) {
- _backgroundInverseVelocity = 0;
+void RenderManager::processSubs(uint16 deltatime) {
+ bool redraw = false;
+ for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) {
+ if (it->_value.timer != -1) {
+ it->_value.timer -= deltatime;
+ if (it->_value.timer <= 0)
+ it->_value.todelete = true;
+ }
+ if (it->_value.todelete) {
+ _subsList.erase(it);
+ redraw = true;
+ } else if (it->_value.redraw) {
+ redraw = true;
}
- } else {
- _backgroundInverseVelocity = 1000 / velocity;
}
-}
-void RenderManager::moveBackground(int offset) {
- RenderTable::RenderState state = _renderTable.getRenderState();
- if (state == RenderTable::TILT) {
- _backgroundOffset += Common::Point(0, offset);
+ if (redraw) {
+ _subtitleSurface.fillRect(Common::Rect(_subtitleSurface.w, _subtitleSurface.h), 0);
+
+ for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) {
+ OneSubtitle *sub = &it->_value;
+ if (sub->txt.size()) {
+ Graphics::Surface subtitleSurface;
+ subtitleSurface.create(sub->r.width(), sub->r.height(), _engine->_resourcePixelFormat);
+ _engine->getTextRenderer()->drawTextWithWordWrapping(sub->txt, subtitleSurface);
+ Common::Rect empty;
+ blitSurfaceToSurface(subtitleSurface, empty, _subtitleSurface, sub->r.left - _subtitleArea.left + _workingWindow.left, sub->r.top - _subtitleArea.top + _workingWindow.top);
+ subtitleSurface.free();
+ }
+ sub->redraw = false;
+ }
- _backgroundOffset.y = CLIP<int16>(_backgroundOffset.y, _screenCenterY, (int16)_backgroundHeight - _screenCenterY);
+ Common::Rect rect(
+ _subtitleArea.left,
+ _subtitleArea.top,
+ _subtitleArea.left + _subtitleSurface.w,
+ _subtitleArea.top + _subtitleSurface.h
+ );
+ copyToScreen(_subtitleSurface, rect, 0, 0);
+ }
+}
- renderImageToScreen(_currentBackground, 0, _screenCenterY - _backgroundOffset.y, true);
- } else if (state == RenderTable::PANORAMA) {
- _backgroundOffset += Common::Point(offset, 0);
+Common::Point RenderManager::getBkgSize() {
+ return Common::Point(_backgroundWidth, _backgroundHeight);
+}
- if (_backgroundOffset.x <= -_backgroundWidth)
- _backgroundOffset.x += _backgroundWidth;
- else if (_backgroundOffset.x >= _backgroundWidth)
- _backgroundOffset.x -= _backgroundWidth;
+void RenderManager::addEffect(GraphicsEffect *_effect) {
+ _effects.push_back(_effect);
+}
- renderImageToScreen(_currentBackground, _screenCenterX - _backgroundOffset.x, 0, true);
- } else {
- renderImageToScreen(_currentBackground, 0, 0);
+void RenderManager::deleteEffect(uint32 ID) {
+ for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) {
+ if ((*it)->getKey() == ID) {
+ delete *it;
+ it = _effects.erase(it);
+ }
}
}
-uint32 RenderManager::getCurrentBackgroundOffset() {
+Common::Rect RenderManager::transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src) {
+ Common::Rect tmp = src;
RenderTable::RenderState state = _renderTable.getRenderState();
if (state == RenderTable::PANORAMA) {
- return _backgroundOffset.x;
+ if (_backgroundOffset < _screenCenterX) {
+ Common::Rect rScreen(_screenCenterX + _backgroundOffset, _workingWindow.height());
+ Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height());
+ lScreen.translate(_backgroundWidth - lScreen.width(), 0);
+ lScreen.clip(src);
+ rScreen.clip(src);
+ if (rScreen.width() < lScreen.width()) {
+ tmp.translate(_screenCenterX - _backgroundOffset - _backgroundWidth, 0);
+ } else {
+ tmp.translate(_screenCenterX - _backgroundOffset, 0);
+ }
+ } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) {
+ Common::Rect rScreen(_screenCenterX - (_backgroundWidth - _backgroundOffset), _workingWindow.height());
+ Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height());
+ lScreen.translate(_backgroundWidth - lScreen.width(), 0);
+ lScreen.clip(src);
+ rScreen.clip(src);
+ if (lScreen.width() < rScreen.width()) {
+ tmp.translate(_screenCenterX + (_backgroundWidth - _backgroundOffset), 0);
+ } else {
+ tmp.translate(_screenCenterX - _backgroundOffset, 0);
+ }
+ } else {
+ tmp.translate(_screenCenterX - _backgroundOffset, 0);
+ }
} else if (state == RenderTable::TILT) {
- return _backgroundOffset.y;
- } else {
- return 0;
+ tmp.translate(0, (_screenCenterY - _backgroundOffset));
}
+
+ return tmp;
}
-Graphics::Surface *RenderManager::tranposeSurface(const Graphics::Surface *surface) {
- Graphics::Surface *tranposedSurface = new Graphics::Surface();
- tranposedSurface->create(surface->h, surface->w, surface->format);
+EffectMap *RenderManager::makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *_minComp, int8 *_maxComp) {
+ Common::Rect bkgRect(_backgroundWidth, _backgroundHeight);
+ if (!bkgRect.contains(xy))
+ return NULL;
+
+ if (!bkgRect.intersects(rect))
+ return NULL;
+
+ uint16 color = *(uint16 *)_currentBackgroundImage.getBasePtr(xy.x, xy.y);
+ uint8 stC1, stC2, stC3;
+ _currentBackgroundImage.format.colorToRGB(color, stC1, stC2, stC3);
+ EffectMap *newMap = new EffectMap;
+
+ EffectMapUnit unit;
+ unit.count = 0;
+ unit.inEffect = false;
+
+ int16 w = rect.width();
+ int16 h = rect.height();
+
+ bool first = true;
+
+ uint8 minComp = MIN(MIN(stC1, stC2), stC3);
+ uint8 maxComp = MAX(MAX(stC1, stC2), stC3);
+
+ uint8 depth8 = depth << 3;
+
+ for (int16 j = 0; j < h; j++) {
+ uint16 *pix = (uint16 *)_currentBackgroundImage.getBasePtr(rect.left, rect.top + j);
+ for (int16 i = 0; i < w; i++) {
+ uint16 curClr = pix[i];
+ uint8 cC1, cC2, cC3;
+ _currentBackgroundImage.format.colorToRGB(curClr, cC1, cC2, cC3);
+
+ bool use = false;
+
+ if (curClr == color)
+ use = true;
+ else if (curClr > color) {
+ if ((cC1 - stC1 < depth8) &&
+ (cC2 - stC2 < depth8) &&
+ (cC3 - stC3 < depth8))
+ use = true;
+ } else { /* if (curClr < color) */
+ if ((stC1 - cC1 < depth8) &&
+ (stC2 - cC2 < depth8) &&
+ (stC3 - cC3 < depth8))
+ use = true;
+ }
- const uint16 *source = (const uint16 *)surface->getPixels();
- uint16 *dest = (uint16 *)tranposedSurface->getPixels();
+ if (first) {
+ unit.inEffect = use;
+ first = false;
+ }
- for (uint32 y = 0; y < tranposedSurface->h; ++y) {
- uint32 columnIndex = y * tranposedSurface->w;
+ if (use) {
+ uint8 cMinComp = MIN(MIN(cC1, cC2), cC3);
+ uint8 cMaxComp = MAX(MAX(cC1, cC2), cC3);
+ if (cMinComp < minComp)
+ minComp = cMinComp;
+ if (cMaxComp > maxComp)
+ maxComp = cMaxComp;
+ }
- for (uint32 x = 0; x < tranposedSurface->w; ++x) {
- dest[columnIndex + x] = source[x * surface->w + y];
+ if (unit.inEffect == use)
+ unit.count++;
+ else {
+ newMap->push_back(unit);
+ unit.count = 1;
+ unit.inEffect = use;
+ }
}
}
+ newMap->push_back(unit);
- return tranposedSurface;
+ if (_minComp) {
+ if (minComp - depth8 < 0)
+ *_minComp = -(minComp >> 3);
+ else
+ *_minComp = -depth;
+ }
+ if (_maxComp) {
+ if ((int16)maxComp + (int16)depth8 > 255)
+ *_maxComp = (255 - maxComp) >> 3;
+ else
+ *_maxComp = depth;
+ }
+
+ return newMap;
+}
+
+EffectMap *RenderManager::makeEffectMap(const Graphics::Surface &surf, uint16 transp) {
+ EffectMapUnit unit;
+ unit.count = 0;
+ unit.inEffect = false;
+
+ int16 w = surf.w;
+ int16 h = surf.h;
+
+ EffectMap *newMap = new EffectMap;
+
+ bool first = true;
+
+ for (int16 j = 0; j < h; j++) {
+ const uint16 *pix = (const uint16 *)surf.getBasePtr(0, j);
+ for (int16 i = 0; i < w; i++) {
+ bool use = false;
+ if (pix[i] != transp)
+ use = true;
+
+ if (first) {
+ unit.inEffect = use;
+ first = false;
+ }
+
+ if (unit.inEffect == use)
+ unit.count++;
+ else {
+ newMap->push_back(unit);
+ unit.count = 1;
+ unit.inEffect = use;
+ }
+ }
+ }
+ newMap->push_back(unit);
+
+ return newMap;
+}
+
+void RenderManager::markDirty() {
+ _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight);
+}
+
+#if 0
+void RenderManager::bkgFill(uint8 r, uint8 g, uint8 b) {
+ _currentBackgroundImage.fillRect(Common::Rect(_currentBackgroundImage.w, _currentBackgroundImage.h), _currentBackgroundImage.format.RGBToColor(r, g, b));
+ markDirty();
+}
+#endif
+
+void RenderManager::timedMessage(const Common::String &str, uint16 milsecs) {
+ uint16 msgid = createSubArea();
+ updateSubArea(msgid, str);
+ deleteSubArea(msgid, milsecs);
+}
+
+bool RenderManager::askQuestion(const Common::String &str) {
+ Graphics::Surface textSurface;
+ textSurface.create(_subtitleArea.width(), _subtitleArea.height(), _engine->_resourcePixelFormat);
+ _engine->getTextRenderer()->drawTextWithWordWrapping(str, textSurface);
+ copyToScreen(textSurface, _subtitleArea, 0, 0);
+
+ _engine->stopClock();
+
+ int result = 0;
+
+ while (result == 0) {
+ Common::Event evnt;
+ while (_engine->getEventManager()->pollEvent(evnt)) {
+ if (evnt.type == Common::EVENT_KEYDOWN) {
+ // English: yes/no
+ // German: ja/nein
+ // Spanish: si/no
+ // French Nemesis: F4/any other key
+ // French ZGI: oui/non
+ switch (evnt.kbd.keycode) {
+ case Common::KEYCODE_y:
+ if (_engine->getLanguage() == Common::EN_ANY)
+ result = 2;
+ break;
+ case Common::KEYCODE_j:
+ if (_engine->getLanguage() == Common::DE_DEU)
+ result = 2;
+ break;
+ case Common::KEYCODE_s:
+ if (_engine->getLanguage() == Common::ES_ESP)
+ result = 2;
+ break;
+ case Common::KEYCODE_o:
+ if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_GRANDINQUISITOR)
+ result = 2;
+ break;
+ case Common::KEYCODE_F4:
+ if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
+ result = 2;
+ break;
+ case Common::KEYCODE_n:
+ result = 1;
+ break;
+ default:
+ if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS)
+ result = 1;
+ break;
+ }
+ }
+ }
+ _system->updateScreen();
+ if (_doubleFPS)
+ _system->delayMillis(33);
+ else
+ _system->delayMillis(66);
+ }
+
+ // Draw over the text in order to clear it
+ textSurface.fillRect(Common::Rect(_subtitleArea.width(), _subtitleArea.height()), 0);
+ copyToScreen(textSurface, _subtitleArea, 0, 0);
+
+ // Free the surface
+ textSurface.free();
+
+ _engine->startClock();
+ return result == 2;
+}
+
+void RenderManager::delayedMessage(const Common::String &str, uint16 milsecs) {
+ uint16 msgid = createSubArea();
+ updateSubArea(msgid, str);
+ processSubs(0);
+ renderSceneToScreen();
+ _engine->stopClock();
+
+ uint32 stopTime = _system->getMillis() + milsecs;
+ while (_system->getMillis() < stopTime) {
+ Common::Event evnt;
+ while (_engine->getEventManager()->pollEvent(evnt)) {
+ if (evnt.type == Common::EVENT_KEYDOWN &&
+ (evnt.kbd.keycode == Common::KEYCODE_SPACE ||
+ evnt.kbd.keycode == Common::KEYCODE_RETURN ||
+ evnt.kbd.keycode == Common::KEYCODE_ESCAPE))
+ break;
+ }
+ _system->updateScreen();
+ if (_doubleFPS)
+ _system->delayMillis(33);
+ else
+ _system->delayMillis(66);
+ }
+ deleteSubArea(msgid);
+ _engine->startClock();
+}
+
+void RenderManager::showDebugMsg(const Common::String &msg, int16 delay) {
+ uint16 msgid = createSubArea();
+ updateSubArea(msgid, msg);
+ deleteSubArea(msgid, delay);
+}
+
+void RenderManager::updateRotation() {
+ int16 _velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
+ ScriptManager *scriptManager = _engine->getScriptManager();
+
+ if (_doubleFPS)
+ _velocity /= 2;
+
+ if (_velocity) {
+ RenderTable::RenderState renderState = _renderTable.getRenderState();
+ if (renderState == RenderTable::PANORAMA) {
+ int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos);
+
+ int16 newPosition = startPosition + (_renderTable.getPanoramaReverse() ? -_velocity : _velocity);
+
+ int16 zeroPoint = _renderTable.getPanoramaZeroPoint();
+ if (startPosition >= zeroPoint && newPosition < zeroPoint)
+ scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) - 1);
+ if (startPosition <= zeroPoint && newPosition > zeroPoint)
+ scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) + 1);
+
+ int16 screenWidth = getBkgSize().x;
+ if (screenWidth)
+ newPosition %= screenWidth;
+
+ if (newPosition < 0)
+ newPosition += screenWidth;
+
+ setBackgroundPosition(newPosition);
+ } else if (renderState == RenderTable::TILT) {
+ int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos);
+
+ int16 newPosition = startPosition + _velocity;
+
+ int16 screenHeight = getBkgSize().y;
+ int16 tiltGap = (int16)_renderTable.getTiltGap();
+
+ if (newPosition >= (screenHeight - tiltGap))
+ newPosition = screenHeight - tiltGap;
+ if (newPosition <= tiltGap)
+ newPosition = tiltGap;
+
+ setBackgroundPosition(newPosition);
+ }
+ }
+}
+
+void RenderManager::checkBorders() {
+ RenderTable::RenderState renderState = _renderTable.getRenderState();
+ if (renderState == RenderTable::PANORAMA) {
+ int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos);
+
+ int16 newPosition = startPosition;
+
+ int16 screenWidth = getBkgSize().x;
+
+ if (screenWidth)
+ newPosition %= screenWidth;
+
+ if (newPosition < 0)
+ newPosition += screenWidth;
+
+ if (startPosition != newPosition)
+ setBackgroundPosition(newPosition);
+ } else if (renderState == RenderTable::TILT) {
+ int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos);
+
+ int16 newPosition = startPosition;
+
+ int16 screenHeight = getBkgSize().y;
+ int16 tiltGap = (int16)_renderTable.getTiltGap();
+
+ if (newPosition >= (screenHeight - tiltGap))
+ newPosition = screenHeight - tiltGap;
+ if (newPosition <= tiltGap)
+ newPosition = tiltGap;
+
+ if (startPosition != newPosition)
+ setBackgroundPosition(newPosition);
+ }
+}
+
+void RenderManager::rotateTo(int16 _toPos, int16 _time) {
+ if (_renderTable.getRenderState() != RenderTable::PANORAMA)
+ return;
+
+ if (_time == 0)
+ _time = 1;
+
+ int32 maxX = getBkgSize().x;
+ int32 curX = getCurrentBackgroundOffset();
+ int32 dx = 0;
+
+ if (curX == _toPos)
+ return;
+
+ if (curX > _toPos) {
+ if (curX - _toPos > maxX / 2)
+ dx = (_toPos + (maxX - curX)) / _time;
+ else
+ dx = -(curX - _toPos) / _time;
+ } else {
+ if (_toPos - curX > maxX / 2)
+ dx = -((maxX - _toPos) + curX) / _time;
+ else
+ dx = (_toPos - curX) / _time;
+ }
+
+ _engine->stopClock();
+
+ for (int16 i = 0; i <= _time; i++) {
+ if (i == _time)
+ curX = _toPos;
+ else
+ curX += dx;
+
+ if (curX < 0)
+ curX = maxX - curX;
+ else if (curX >= maxX)
+ curX %= maxX;
+
+ setBackgroundPosition(curX);
+
+ prepareBackground();
+ renderSceneToScreen();
+
+ _system->updateScreen();
+
+ _system->delayMillis(500 / _time);
+ }
+
+ _engine->startClock();
+}
+
+void RenderManager::upscaleRect(Common::Rect &rect) {
+ rect.top = rect.top * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT;
+ rect.left = rect.left * HIRES_WINDOW_WIDTH / WINDOW_WIDTH;
+ rect.bottom = rect.bottom * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT;
+ rect.right = rect.right * HIRES_WINDOW_WIDTH / WINDOW_WIDTH;
}
} // End of namespace ZVision
diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h
index 9feff4c030..33d8a88e78 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.
@@ -24,13 +24,14 @@
#define ZVISION_RENDER_MANAGER_H
#include "zvision/graphics/render_table.h"
-#include "zvision/fonts/truetype_font.h"
+#include "zvision/text/truetype_font.h"
#include "common/rect.h"
#include "common/hashmap.h"
#include "graphics/surface.h"
+#include "graphics_effect.h"
class OSystem;
@@ -47,171 +48,131 @@ 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, bool doubleFPS);
~RenderManager();
private:
- struct AlphaDataEntry {
- Graphics::Surface *data;
- uint16 alphaColor;
- uint16 destX;
- uint16 destY;
- uint16 width;
- uint16 height;
+ struct OneSubtitle {
+ Common::Rect r;
+ Common::String txt;
+ int16 timer;
+ bool todelete;
+ bool redraw;
};
- typedef Common::HashMap<uint32, AlphaDataEntry> AlphaEntryMap;
+ typedef Common::HashMap<uint16, OneSubtitle> SubtitleMap;
+ typedef Common::List<GraphicsEffect *> EffectsList;
private:
+ ZVision *_engine;
OSystem *_system;
const Graphics::PixelFormat _pixelFormat;
- // A buffer the exact same size as the workingWindow
- // This buffer stores everything un-warped, then does a warp at the end of the frame
- Graphics::Surface _workingWindowBuffer;
- // A buffer representing the entire screen. Any graphical updates are first done with this buffer
- // before actually being blitted to the screen
- Graphics::Surface _backBuffer;
- // A list of Alpha Entries that need to be blitted to the backbuffer
- AlphaEntryMap _alphaDataEntries;
-
- // A rectangle representing the portion of the working window where the pixels have been changed since last frame
- Common::Rect _workingWindowDirtyRect;
- // A rectangle representing the portion of the backbuffer where the pixels have been changed since last frame
- Common::Rect _backBufferDirtyRect;
-
- /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */
- const int _workingWidth;
- /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */
- const int _workingHeight;
- /** Center of the screen in the x direction */
- const int _screenCenterX;
- /** Center of the screen in the y direction */
- const int _screenCenterY;
-
/**
* A Rectangle centered inside the actual window. All in-game coordinates
* are given in this coordinate space. Also, all images are clipped to the
* edges of this Rectangle
*/
- const Common::Rect _workingWindow;
- /** Used to warp the background image */
- RenderTable _renderTable;
+ Common::Rect _workingWindow;
- Graphics::Surface _currentBackground;
- /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */
- Common::Point _backgroundOffset;
+ // Center of the screen in the x direction
+ const int _screenCenterX;
+ // Center of the screen in the y direction
+ const int _screenCenterY;
+
+ /** A buffer for background image that's being used to create the background */
+ Graphics::Surface _currentBackgroundImage;
+ Common::Rect _backgroundDirtyRect;
+
+ /**
+ * The x1 or y1 offset of the subRectangle of the background that is currently displayed on the screen
+ * It will be x1 if PANORAMA, or y1 if TILT
+ */
+ int16 _backgroundOffset;
/** The width of the current background image */
uint16 _backgroundWidth;
/** The height of the current background image */
uint16 _backgroundHeight;
- /**
- * The "velocity" at which the background image is panning. We actually store the inverse of velocity (ms/pixel instead of pixels/ms)
- * because it allows you to accumulate whole pixels 'steps' instead of rounding pixels every frame
- */
- int _backgroundInverseVelocity;
- /** Holds any 'leftover' milliseconds between frames */
- uint _accumulatedVelocityMilliseconds;
+ // A buffer that holds the portion of the background that is used to render the final image
+ // If it's a normal scene, the pixels will be blitted directly to the screen
+ // If it's a panorma / tilt scene, the pixels will be first warped to _warpedSceneSurface
+ Graphics::Surface _backgroundSurface;
+ Common::Rect _backgroundSurfaceDirtyRect;
-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);
+ // A buffer for subtitles
+ Graphics::Surface _subtitleSurface;
- /**
- * Renders the current state of the backbuffer to the screen
- */
- void renderBackbufferToScreen();
+ // Rectangle for subtitles area
+ Common::Rect _subtitleArea;
- /**
- * 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); }
+ // A buffer for menu drawing
+ Graphics::Surface _menuSurface;
+ Common::Rect _menuSurfaceDirtyRect;
- /**
- * 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);
+ // Rectangle for menu area
+ Common::Rect _menuArea;
+
+ // A buffer used for apply graphics effects
+ Graphics::Surface _effectSurface;
+
+ // A buffer to store the result of the panorama / tilt warps
+ Graphics::Surface _warpedSceneSurface;
- /**
- * 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);
+
+ /** Used to warp the background image */
+ RenderTable _renderTable;
+
+ // Internal subtitles counter
+ uint16 _subid;
+
+ // Subtitle list
+ SubtitleMap _subsList;
+
+ // Visual effects list
+ EffectsList _effects;
+
+ bool _doubleFPS;
+
+public:
+ void initialize();
/**
- * 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)
+ * Renders the scene to the screen
*/
- void clearWorkingWindowTo555Color(uint16 color);
+ void renderSceneToScreen();
+
+ void copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop);
/**
- * 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 destinationX X position where the image should be put. Coords are in working window space, not screen space!
- * @param destinationY Y position where the image should be put. Coords are in working window space, not screen space!
+ * @param fileName Name of the image file
+ * @param destX X position where the image should be put. Coords are in working window space, not screen space!
+ * @param destY Y position where the image should be put. Coords are in working window space, not screen space!
+ * @param 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);
+
+ /**
+ * Blits the image or a portion of the image to the background.
+ *
+ * @param fileName Name of the image file
+ * @param destX X position where the image should be put. Coords are in working window space, not screen space!
+ * @param destY Y position where the image should be put. Coords are in working window space, not screen space!
+ * @param keyX X position of transparent color
+ * @param keyY Y position of transparent color
+ */
+ void renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY, int16 keyX, int16 keyY);
/**
* Sets the current background image to be used by the RenderManager and immediately
@@ -234,41 +195,18 @@ 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);
+ // Return pointer of RenderTable object
RenderTable *getRenderTable();
+
+ // Return current background offset
uint32 getCurrentBackgroundOffset();
- const Graphics::Surface *getBackBuffer() { return &_backBuffer; }
/**
* Creates a copy of surface and transposes the data.
@@ -281,25 +219,62 @@ 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);
+ // Scale buffer (nearest)
+ void scaleBuffer(const void *src, void *dst, uint32 srcWidth, uint32 srcHeight, byte bytesPerPixel, uint32 dstWidth, uint32 dstHeight);
+
+ // Blitting surface-to-surface methods
+ void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int x, int y);
+ void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey);
+
+ // Blitting surface-to-background methods
+ void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey = -1);
+
+ // Blitting surface-to-background methods with scale
+ void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey = -1);
+
+ // Blitting surface-to-menu methods
+ void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey = -1);
+
+ // Subtitles methods
+
+ void initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow);
+
+ // Create subtitle area and return ID
+ uint16 createSubArea(const Common::Rect &area);
+ uint16 createSubArea();
+
+ // Delete subtitle by ID
+ void deleteSubArea(uint16 id);
+ void deleteSubArea(uint16 id, int16 delay);
+
+ // Update subtitle area
+ void updateSubArea(uint16 id, const Common::String &txt);
+
+ // Processing subtitles
+ void processSubs(uint16 deltatime);
+
+ // Return background size
+ Common::Point getBkgSize();
+
+ // Return portion of background as new surface
+ Graphics::Surface *getBkgRect(Common::Rect &rect);
+
+ // Load image into new surface
+ Graphics::Surface *loadImage(Common::String file);
+ Graphics::Surface *loadImage(Common::String file, bool transposed);
+
+ // Clear whole/area of menu surface
+ void clearMenuSurface();
+ void clearMenuSurface(const Common::Rect &r);
+
+ // Copy menu buffer to screen
+ void renderMenuToScreen();
+
+ // Copy needed portion of background surface to workingWindow surface
+ void prepareBackground();
/**
- * Reads an image file pixel data into a Surface buffer. In the process
- * it converts the pixel data from RGB 555 to RGB 565. Also, if the image
+ * Reads an image file pixel data into a Surface buffer. Also, if the image
* is transposed, it will un-transpose the pixel data. The function will
* call destination::create() if the dimensions of destination do not match
* up with the dimensions of the image.
@@ -310,17 +285,55 @@ private:
void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination);
/**
- * Move the background image by an offset. If we are currently in Panorama mode,
- * the offset will correspond to a horizontal motion. If we are currently in Tilt mode,
- * the offset will correspond to a vertical motion. This function should not be called
- * if we are in Flat mode.
- *
- * The RenderManager will take care of wrapping the image.
- * Ex: If the image has width 1400px, it is legal to offset 1500px.
+ * Reads an image file pixel data into a Surface buffer. Also, if the image
+ * is transposed, it will un-transpose the pixel data. The function will
+ * call destination::create() if the dimensions of destination do not match
+ * up with the dimensions of the image.
*
- * @param offset The amount to move the background
+ * @param fileName The name of a .tga file
+ * @param destination A reference to the Surface to store the pixel data in
+ * @param transposed Transpose flag
*/
- void moveBackground(int offset);
+ void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed);
+
+ // Add visual effect to effects list
+ void addEffect(GraphicsEffect *_effect);
+
+ // Delete effect(s) by ID (ID equal to slot of action:region that create this effect)
+ void deleteEffect(uint32 ID);
+
+ // Create "mask" for effects - (color +/- depth) will be selected as not transparent. Like color selection
+ // xy - base color
+ // depth - +/- of base color
+ // rect - rectangle where select pixels
+ // minD - if not NULL will recieve real bottom border of depth
+ // maxD - if not NULL will recieve real top border of depth
+ EffectMap *makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *minD, int8 *maxD);
+
+ // Create "mask" for effects by simple transparent color
+ EffectMap *makeEffectMap(const Graphics::Surface &surf, uint16 transp);
+
+ // Return background rectangle in screen coordinates
+ Common::Rect transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src);
+
+ // Mark whole background surface as dirty
+ void markDirty();
+
+#if 0
+ // Fill background surface by color
+ void bkgFill(uint8 r, uint8 g, uint8 b);
+#endif
+
+ bool askQuestion(const Common::String &str);
+ void delayedMessage(const Common::String &str, uint16 milsecs);
+ void timedMessage(const Common::String &str, uint16 milsecs);
+ void showDebugMsg(const Common::String &msg, int16 delay = 3000);
+
+ void checkBorders();
+ void rotateTo(int16 to, int16 time);
+ void updateRotation();
+
+ void upscaleRect(Common::Rect &rect);
};
} // End of namespace ZVision
diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp
index 49b934dc37..df73247344 100644
--- a/engines/zvision/graphics/render_table.cpp
+++ b/engines/zvision/graphics/render_table.cpp
@@ -21,23 +21,22 @@
*/
#include "common/scummsys.h"
-
#include "zvision/graphics/render_table.h"
-
#include "common/rect.h"
-
#include "graphics/colormasks.h"
-
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];
+
+ memset(&_panoramaOptions, 0, sizeof(_panoramaOptions));
+ memset(&_tiltOptions, 0, sizeof(_tiltOptions));
}
RenderTable::~RenderTable() {
@@ -52,10 +51,11 @@ void RenderTable::setRenderState(RenderState newState) {
_panoramaOptions.fieldOfView = 27.0f;
_panoramaOptions.linearScale = 0.55f;
_panoramaOptions.reverse = false;
+ _panoramaOptions.zeroPoint = 0;
break;
case TILT:
_tiltOptions.fieldOfView = 27.0f;
- _tiltOptions.linearScale = 0.55f;
+ _tiltOptions.linearScale = 0.65f;
_tiltOptions.reverse = false;
break;
case FLAT:
@@ -81,28 +81,7 @@ const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Poi
return newPoint;
}
-uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) {
- assert(percentColorOne < 1.0f);
-
- float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift);
- float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift);
- float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift);
- float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift);
- float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift);
- float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift);
-
- float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne);
- float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne);
- float bFinal = bOne * percentColorOne + bTwo * (1.0f - 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);
-
- 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 +102,28 @@ void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 d
}
}
+void RenderTable::mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf) {
+ uint32 destOffset = 0;
+
+ uint16 *sourceBuffer = (uint16 *)srcBuf->getPixels();
+ uint16 *destBuffer = (uint16 *)dstBuf->getPixels();
+
+ for (int16 y = 0; y < srcBuf->h; ++y) {
+ uint32 sourceOffset = y * _numColumns;
+
+ for (int16 x = 0; x < srcBuf->w; ++x) {
+ uint32 index = sourceOffset + x;
+
+ // RenderTable only stores offsets from the original coordinates
+ uint32 sourceYIndex = y + _internalBuffer[index].y;
+ uint32 sourceXIndex = x + _internalBuffer[index].x;
+
+ destBuffer[destOffset] = sourceBuffer[sourceYIndex * _numColumns + sourceXIndex];
+ destOffset++;
+ }
+ }
+}
+
void RenderTable::generateRenderTable() {
switch (_renderState) {
case ZVision::RenderTable::PANORAMA:
@@ -177,6 +178,7 @@ void RenderTable::generateTiltLookupTable() {
float fovInRadians = (_tiltOptions.fieldOfView * M_PI / 180.0f);
float cylinderRadius = halfWidth / tan(fovInRadians);
+ _tiltOptions.gap = cylinderRadius * atan2((float)(halfHeight / cylinderRadius), 1.0f) * _tiltOptions.linearScale;
for (uint y = 0; y < _numRows; ++y) {
@@ -221,6 +223,18 @@ void RenderTable::setPanoramaReverse(bool reverse) {
_panoramaOptions.reverse = reverse;
}
+bool RenderTable::getPanoramaReverse() {
+ return _panoramaOptions.reverse;
+}
+
+void RenderTable::setPanoramaZeroPoint(uint16 point) {
+ _panoramaOptions.zeroPoint = point;
+}
+
+uint16 RenderTable::getPanoramaZeroPoint() {
+ return _panoramaOptions.zeroPoint;
+}
+
void RenderTable::setTiltFoV(float fov) {
assert(fov > 0.0f);
@@ -237,4 +251,26 @@ void RenderTable::setTiltReverse(bool reverse) {
_tiltOptions.reverse = reverse;
}
+float RenderTable::getTiltGap() {
+ return _tiltOptions.gap;
+}
+
+float RenderTable::getAngle() {
+ if (_renderState == TILT)
+ return _tiltOptions.fieldOfView;
+ else if (_renderState == PANORAMA)
+ return _panoramaOptions.fieldOfView;
+ else
+ return 1.0;
+}
+
+float RenderTable::getLinscale() {
+ if (_renderState == TILT)
+ return _tiltOptions.linearScale;
+ else if (_renderState == PANORAMA)
+ return _panoramaOptions.linearScale;
+ else
+ return 1.0;
+}
+
} // End of namespace ZVision
diff --git a/engines/zvision/graphics/render_table.h b/engines/zvision/graphics/render_table.h
index f066187ad1..7455d9ba39 100644
--- a/engines/zvision/graphics/render_table.h
+++ b/engines/zvision/graphics/render_table.h
@@ -24,7 +24,7 @@
#define ZVISION_RENDER_TABLE_H
#include "common/rect.h"
-
+#include "graphics/surface.h"
namespace ZVision {
@@ -49,6 +49,7 @@ private:
float fieldOfView;
float linearScale;
bool reverse;
+ uint16 zeroPoint;
} _panoramaOptions;
// TODO: See if tilt and panorama need to have separate options
@@ -56,25 +57,36 @@ private:
float fieldOfView;
float linearScale;
bool reverse;
+ float gap;
} _tiltOptions;
public:
- RenderState getRenderState() { return _renderState; }
+ RenderState getRenderState() {
+ return _renderState;
+ }
void setRenderState(RenderState newState);
const Common::Point convertWarpedCoordToFlatCoord(const Common::Point &point);
- void mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 destWidth, const Common::Rect &subRect);
+ void mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect);
+ void mutateImage(Graphics::Surface *dstBuf, Graphics::Surface *srcBuf);
void generateRenderTable();
void setPanoramaFoV(float fov);
void setPanoramaScale(float scale);
void setPanoramaReverse(bool reverse);
+ void setPanoramaZeroPoint(uint16 point);
+ uint16 getPanoramaZeroPoint();
+ bool getPanoramaReverse();
void setTiltFoV(float fov);
void setTiltScale(float scale);
void setTiltReverse(bool reverse);
+ float getTiltGap();
+ float getAngle();
+ float getLinscale();
+
private:
void generatePanoramaLookupTable();
void generateTiltLookupTable();
diff --git a/engines/zvision/module.mk b/engines/zvision/module.mk
index 2e298f24c6..93fba2879d 100644
--- a/engines/zvision/module.mk
+++ b/engines/zvision/module.mk
@@ -1,32 +1,51 @@
MODULE := engines/zvision
MODULE_OBJS := \
- animation/rlf_animation.o \
- archives/zfs_archive.o \
core/console.o \
+ core/clock.o \
core/events.o \
- core/save_manager.o \
- cursors/cursor.o \
- cursors/cursor_manager.o \
detection.o \
- fonts/truetype_font.o \
+ file/lzss_read_stream.o \
+ file/save_manager.o \
+ file/search_manager.o \
+ file/zfs_archive.o \
+ graphics/cursors/cursor_manager.o \
+ graphics/cursors/cursor.o \
+ graphics/effects/fog.o \
+ graphics/effects/light.o \
+ graphics/effects/wave.o \
graphics/render_manager.o \
graphics/render_table.o \
scripting/actions.o \
scripting/control.o \
- scripting/controls/animation_control.o \
+ scripting/controls/fist_control.o \
+ scripting/controls/hotmov_control.o \
scripting/controls/input_control.o \
scripting/controls/lever_control.o \
+ scripting/controls/paint_control.o \
scripting/controls/push_toggle_control.o \
- scripting/controls/timer_node.o \
+ scripting/controls/safe_control.o \
+ scripting/controls/save_control.o \
+ scripting/controls/slot_control.o \
+ scripting/controls/titler_control.o \
+ scripting/inventory.o \
+ scripting/menu.o \
scripting/scr_file_handling.o \
scripting/script_manager.o \
+ scripting/effects/animation_effect.o \
+ scripting/effects/distort_effect.o \
+ scripting/effects/music_effect.o \
+ scripting/effects/region_effect.o \
+ scripting/effects/syncsound_effect.o \
+ scripting/effects/timer_effect.o \
+ scripting/effects/ttytext_effect.o \
+ sound/midi.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 \
+ text/string_manager.o \
+ text/subtitles.o \
+ text/text.o \
+ text/truetype_font.o \
+ video/rlf_decoder.o \
video/video.o \
video/zork_avi_decoder.o \
zvision.o
diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp
index 517278e155..9a8b734e0c 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.
@@ -21,380 +21,1075 @@
*/
#include "common/scummsys.h"
+#include "video/video_decoder.h"
#include "zvision/scripting/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 "common/file.h"
-
-#include "audio/decoders/wave.h"
-
+#include "zvision/file/save_manager.h"
+#include "zvision/scripting/menu.h"
+#include "zvision/scripting/effects/timer_effect.h"
+#include "zvision/scripting/effects/music_effect.h"
+#include "zvision/scripting/effects/syncsound_effect.h"
+#include "zvision/scripting/effects/animation_effect.h"
+#include "zvision/scripting/effects/distort_effect.h"
+#include "zvision/scripting/effects/ttytext_effect.h"
+#include "zvision/scripting/effects/region_effect.h"
+#include "zvision/scripting/controls/titler_control.h"
+#include "zvision/graphics/render_table.h"
+#include "zvision/graphics/graphics_effect.h"
+#include "zvision/graphics/effects/fog.h"
+#include "zvision/graphics/effects/light.h"
+#include "zvision/graphics/effects/wave.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
namespace ZVision {
+ResultAction::ResultAction(ZVision *engine, int32 slotkey) : _engine(engine), _slotKey(slotkey), _scriptManager(engine->getScriptManager()) {
+}
+
//////////////////////////////////////////////////////////////////////////////
// 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) {
+ _key = 0;
+ _value = 0;
+
+ sscanf(line.c_str(), "%u,%d", &_key, &_value);
}
-bool ActionAdd::execute(ZVision *engine) {
- engine->getScriptManager()->addToStateValue(_key, _value);
+bool ActionAdd::execute() {
+ _scriptManager->setStateValue(_key, _scriptManager->getStateValue(_key) + _value);
return true;
}
-
//////////////////////////////////////////////////////////////////////////////
// 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) {
+ _key = 0;
+
+ char buf[64];
+ memset(buf, 0, 64);
+ sscanf(line.c_str(), "%u, %s", &_key, buf);
+ _value = new ValueSlot(_scriptManager, buf);
}
-bool ActionAssign::execute(ZVision *engine) {
- engine->getScriptManager()->setStateValue(_key, _value);
- return true;
+ActionAssign::~ActionAssign() {
+ delete _value;
}
+bool ActionAssign::execute() {
+ _scriptManager->setStateValue(_key, _value->getValue());
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// 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) {
+ _key = 0;
+ _attenuation = 0;
+
+ sscanf(line.c_str(), "%u, %d", &_key, &_attenuation);
}
-bool ActionAttenuate::execute(ZVision *engine) {
- // TODO: Implement
+bool ActionAttenuate::execute() {
+ ScriptingEffect *fx = _scriptManager->getSideFX(_key);
+ if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) {
+ MusicNodeBASE *mus = (MusicNodeBASE *)fx;
+ mus->setVolume(255 * (10000 - abs(_attenuation)) / 10000 );
+ }
return true;
}
-
//////////////////////////////////////////////////////////////////////////////
// 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) {
+ _world = 'g';
+ _room = 'a';
+ _node = 'r';
+ _view = 'y';
+ _offset = 0;
+
+ 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);
+ _scriptManager->changeLocation(_world, _room, _node, _view, _offset);
// Tell the puzzle system to stop checking any more puzzles
return false;
}
-
//////////////////////////////////////////////////////////////////////////////
// ActionCrossfade
//////////////////////////////////////////////////////////////////////////////
-ActionCrossfade::ActionCrossfade(const Common::String &line) {
+ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _keyOne = 0;
+ _keyTwo = 0;
+ _oneStartVolume = 0;
+ _twoStartVolume = 0;
+ _oneEndVolume = 0;
+ _twoEndVolume = 0;
+ _timeInMillis = 0;
+
sscanf(line.c_str(),
- "%*[^(](%u %u %u %u %u %u %u)",
- &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis);
+ "%u %u %d %d %d %d %d",
+ &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis);
+}
+
+bool ActionCrossfade::execute() {
+ if (_keyOne) {
+ ScriptingEffect *fx = _scriptManager->getSideFX(_keyOne);
+ if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) {
+ MusicNodeBASE *mus = (MusicNodeBASE *)fx;
+ if (_oneStartVolume >= 0)
+ mus->setVolume((_oneStartVolume * 255) / 100);
+
+ mus->setFade(_timeInMillis, (_oneEndVolume * 255) / 100);
+ }
+ }
+
+ if (_keyTwo) {
+ ScriptingEffect *fx = _scriptManager->getSideFX(_keyTwo);
+ if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) {
+ MusicNodeBASE *mus = (MusicNodeBASE *)fx;
+ if (_twoStartVolume >= 0)
+ mus->setVolume((_twoStartVolume * 255) / 100);
+
+ mus->setFade(_timeInMillis, (_twoEndVolume * 255) / 100);
+ }
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionCursor
+//////////////////////////////////////////////////////////////////////////////
+
+ActionCursor::ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ Common::String up = line;
+ up.toUppercase();
+ _action = 0;
+
+ if (up[0] == 'B')
+ _action = 2;
+ else if (up[0] == 'I')
+ _action = 3;
+ else if (up[0] == 'U')
+ _action = 0;
+ else if (up[0] == 'H')
+ _action = 1;
}
-bool ActionCrossfade::execute(ZVision *engine) {
- // TODO: Implement
+bool ActionCursor::execute() {
+ switch (_action) {
+ case 1:
+ _engine->getCursorManager()->showMouse(false);
+ break;
+ default:
+ _engine->getCursorManager()->showMouse(true);
+ break;
+ }
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionDelayRender
+//////////////////////////////////////////////////////////////////////////////
+
+ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _framesToDelay = 0;
+ sscanf(line.c_str(), "%u", &_framesToDelay);
+ // Limit to 10 frames maximum. This fixes the script bug in ZGI scene px10
+ // (outside Frobozz Electric building), where this is set to 100 (bug #6791).
+ _framesToDelay = MIN<uint32>(_framesToDelay, 10);
+}
+
+bool ActionDelayRender::execute() {
+ _engine->setRenderDelay(_framesToDelay);
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionDisableControl
//////////////////////////////////////////////////////////////////////////////
-ActionDisableControl::ActionDisableControl(const Common::String &line) {
- sscanf(line.c_str(), "%*[^(](%u)", &_key);
+ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _key = 0;
+
+ sscanf(line.c_str(), "%u", &_key);
+}
+
+bool ActionDisableControl::execute() {
+ _scriptManager->setStateFlag(_key, Puzzle::DISABLED);
+ return true;
}
-bool ActionDisableControl::execute(ZVision *engine) {
- debug("Disabling control %u", _key);
-
- ScriptManager *scriptManager = engine->getScriptManager();
- scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED);
+//////////////////////////////////////////////////////////////////////////////
+// ActionDisplayMessage
+//////////////////////////////////////////////////////////////////////////////
+ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _control = 0;
+ _msgid = 0;
+
+ sscanf(line.c_str(), "%hd %hd", &_control, &_msgid);
+}
+
+bool ActionDisplayMessage::execute() {
+ Control *ctrl = _scriptManager->getControl(_control);
+ if (ctrl && ctrl->getType() == Control::CONTROL_TITLER) {
+ TitlerControl *titler = (TitlerControl *)ctrl;
+ titler->setString(_msgid);
+ }
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionDissolve
+//////////////////////////////////////////////////////////////////////////////
+
+ActionDissolve::ActionDissolve(ZVision *engine) :
+ ResultAction(engine, 0) {
+}
+
+bool ActionDissolve::execute() {
+ // Cause black screen flick
+ // _engine->getRenderManager()->bkgFill(0, 0, 0);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionDistort - only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30)
+//////////////////////////////////////////////////////////////////////////////
+
+ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _distSlot = 0;
+ _speed = 0;
+ _startAngle = 60.0;
+ _endAngle = 60.0;
+ _startLineScale = 1.0;
+ _endLineScale = 1.0;
+
+ sscanf(line.c_str(), "%hd %hd %f %f %f %f", &_distSlot, &_speed, &_startAngle, &_endAngle, &_startLineScale, &_endLineScale);
+}
+
+ActionDistort::~ActionDistort() {
+ _scriptManager->killSideFx(_distSlot);
+}
+
+bool ActionDistort::execute() {
+ if (_scriptManager->getSideFX(_distSlot))
+ return true;
+
+ _scriptManager->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale));
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionEnableControl
//////////////////////////////////////////////////////////////////////////////
-ActionEnableControl::ActionEnableControl(const Common::String &line) {
- sscanf(line.c_str(), "%*[^(](%u)", &_key);
+ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _key = 0;
+
+ sscanf(line.c_str(), "%u", &_key);
+}
+
+bool ActionEnableControl::execute() {
+ _scriptManager->unsetStateFlag(_key, Puzzle::DISABLED);
+ return true;
}
-bool ActionEnableControl::execute(ZVision *engine) {
- debug("Enabling control %u", _key);
+//////////////////////////////////////////////////////////////////////////////
+// ActionFlushMouseEvents
+//////////////////////////////////////////////////////////////////////////////
- ScriptManager *scriptManager = engine->getScriptManager();
- scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~ScriptManager::DISABLED);
+ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotkey) :
+ ResultAction(engine, slotkey) {
+}
+bool ActionFlushMouseEvents::execute() {
+ _scriptManager->flushEvent(Common::EVENT_LBUTTONUP);
+ _scriptManager->flushEvent(Common::EVENT_LBUTTONDOWN);
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionInventory
+//////////////////////////////////////////////////////////////////////////////
+
+ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _type = -1;
+ _key = 0;
+
+ char buf[25];
+ sscanf(line.c_str(), "%24s %d", buf, &_key);
+
+ if (strcmp(buf, "add") == 0) {
+ _type = 0;
+ } else if (strcmp(buf, "addi") == 0) {
+ _type = 1;
+ } else if (strcmp(buf, "drop") == 0) {
+ _type = 2;
+ } else if (strcmp(buf, "dropi") == 0) {
+ _type = 3;
+ } else if (strcmp(buf, "cycle") == 0) {
+ _type = 4;
+ }
+
+}
+
+bool ActionInventory::execute() {
+ switch (_type) {
+ case 0: // add
+ _scriptManager->inventoryAdd(_key);
+ break;
+ case 1: // addi
+ _scriptManager->inventoryAdd(_scriptManager->getStateValue(_key));
+ break;
+ case 2: // drop
+ if (_key >= 0)
+ _scriptManager->inventoryDrop(_key);
+ else
+ _scriptManager->inventoryDrop(_scriptManager->getStateValue(StateKey_InventoryItem));
+ break;
+ case 3: // dropi
+ _scriptManager->inventoryDrop(_scriptManager->getStateValue(_key));
+ break;
+ case 4: // cycle
+ _scriptManager->inventoryCycle();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionKill - only used by ZGI
+//////////////////////////////////////////////////////////////////////////////
+
+ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _key = 0;
+ _type = 0;
+ char keytype[25];
+ sscanf(line.c_str(), "%24s", keytype);
+ if (keytype[0] == '"') {
+ if (!scumm_stricmp(keytype, "\"ANIM\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_ANIM;
+ else if (!scumm_stricmp(keytype, "\"AUDIO\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_AUDIO;
+ else if (!scumm_stricmp(keytype, "\"DISTORT\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_DISTORT;
+ else if (!scumm_stricmp(keytype, "\"PANTRACK\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_PANTRACK;
+ else if (!scumm_stricmp(keytype, "\"REGION\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_REGION;
+ else if (!scumm_stricmp(keytype, "\"TIMER\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_TIMER;
+ else if (!scumm_stricmp(keytype, "\"TTYTEXT\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_TTYTXT;
+ else if (!scumm_stricmp(keytype, "\"ALL\""))
+ _type = ScriptingEffect::SCRIPTING_EFFECT_ALL;
+ } else
+ _key = atoi(keytype);
+}
+
+bool ActionKill::execute() {
+ if (_type)
+ _scriptManager->killSideFxType((ScriptingEffect::ScriptingEffectType)_type);
+ else
+ _scriptManager->killSideFx(_key);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionMenuBarEnable
+//////////////////////////////////////////////////////////////////////////////
+
+ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _menus = 0xFFFF;
+
+ sscanf(line.c_str(), "%hu", &_menus);
+}
+
+bool ActionMenuBarEnable::execute() {
+ _engine->getMenuHandler()->setEnable(_menus);
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionMusic
//////////////////////////////////////////////////////////////////////////////
-ActionMusic::ActionMusic(const Common::String &line) : _volume(255) {
- uint type;
- char fileNameBuffer[26];
- uint loop;
- uint volume = 255;
+ActionMusic::ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global) :
+ ResultAction(engine, slotkey),
+ _note(0),
+ _prog(0),
+ _universe(global) {
+ uint type = 0;
+ char fileNameBuffer[25];
+ uint loop = 0;
+ char volumeBuffer[15];
+
+ // Volume is optional. If it doesn't appear, assume full volume
+ strcpy(volumeBuffer, "100");
- sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u %25s %u %u)", &_key, &type, fileNameBuffer, &loop, &volume);
+ sscanf(line.c_str(), "%u %24s %u %14s", &type, fileNameBuffer, &loop, volumeBuffer);
- // type 4 are midi sound effect files
+ // Type 4 actions are MIDI commands, not files. These are only used by
+ // Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well
+ // as vr)
if (type == 4) {
- _soundType = Audio::Mixer::kSFXSoundType;
- _fileName = Common::String::format("midi/%s/%u.wav", fileNameBuffer, loop);
- _loop = false;
+ _midi = true;
+ int note;
+ int prog;
+ sscanf(line.c_str(), "%u %d %d %14s", &type, &prog, &note, volumeBuffer);
+ _volume = new ValueSlot(_scriptManager, volumeBuffer);
+ _note = note;
+ _prog = prog;
} else {
- // TODO: See what the other types are so we can specify the correct Mixer::SoundType. In the meantime use kPlainSoundType
- _soundType = Audio::Mixer::kPlainSoundType;
+ _midi = false;
_fileName = Common::String(fileNameBuffer);
_loop = loop == 1 ? true : false;
+ if (volumeBuffer[0] != '[' && atoi(volumeBuffer) > 100) {
+ // I thought I saw a case like this in Zork Nemesis, so
+ // let's guard against it.
+ warning("ActionMusic: Adjusting volume for %s from %s to 100", _fileName.c_str(), volumeBuffer);
+ strcpy(volumeBuffer, "100");
+ }
+ _volume = new ValueSlot(_scriptManager, volumeBuffer);
}
- // Volume is optional. If it doesn't appear, assume full volume
- if (volume != 255) {
- // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255]
- _volume = volume * 255 / 100;
- }
+ // WORKAROUND for a script bug in Zork Nemesis, rooms mq70/mq80.
+ // Fixes an edge case where the player goes to the dark room with the grue
+ // without holding a torch, and then quickly runs away before the grue's
+ // sound effect finishes. Fixes script bug #6794.
+ if (engine->getGameId() == GID_NEMESIS && _slotKey == 14822 && _scriptManager->getStateValue(_slotKey) == 2)
+ _scriptManager->setStateValue(_slotKey, 0);
+
}
-bool ActionMusic::execute(ZVision *engine) {
- Audio::RewindableAudioStream *audioStream;
+ActionMusic::~ActionMusic() {
+ if (!_universe)
+ _scriptManager->killSideFx(_slotKey);
+ delete _volume;
+}
- if (_fileName.contains(".wav")) {
- Common::File *file = new Common::File();
- if (file->open(_fileName)) {
- audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
- } else {
- warning("Unable to open %s", _fileName.c_str());
- return false;
- }
- } else {
- audioStream = makeRawZorkStream(_fileName, engine);
+bool ActionMusic::execute() {
+ if (_scriptManager->getSideFX(_slotKey)) {
+ _scriptManager->killSideFx(_slotKey);
+ _scriptManager->setStateValue(_slotKey, 2);
}
-
- if (_loop) {
- Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES);
- engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume);
+
+ uint volume = _volume->getValue();
+
+ if (_midi) {
+ _scriptManager->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, volume));
} else {
- engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume);
+ if (!_engine->getSearchManager()->hasFile(_fileName))
+ return true;
+
+ // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255]
+ _scriptManager->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, volume * 255 / 100));
}
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionPanTrack
+//////////////////////////////////////////////////////////////////////////////
+
+ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey),
+ _pos(0),
+ _musicSlot(0) {
+
+ sscanf(line.c_str(), "%u %d", &_musicSlot, &_pos);
+}
+
+ActionPanTrack::~ActionPanTrack() {
+ _scriptManager->killSideFx(_slotKey);
+}
+
+bool ActionPanTrack::execute() {
+ if (_scriptManager->getSideFX(_slotKey))
+ return true;
+
+ _scriptManager->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos));
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionPreferences
+//////////////////////////////////////////////////////////////////////////////
+
+ActionPreferences::ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ if (line.compareToIgnoreCase("save") == 0)
+ _save = true;
+ else
+ _save = false;
+}
+
+bool ActionPreferences::execute() {
+ if (_save)
+ _engine->saveSettings();
+ else
+ _engine->loadSettings();
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionPreloadAnimation
//////////////////////////////////////////////////////////////////////////////
-ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) {
- char fileName[26];
+ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _mask = 0;
+ _framerate = 0;
- // 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);
+ char fileName[25];
+
+ // The two %*u are usually 0 and dont seem to have a use
+ sscanf(line.c_str(), "%24s %*u %*u %d %d", fileName, &_mask, &_framerate);
+
+ // Mask 0 means "no transparency" in this case. Since we use a common blitting
+ // code for images and animations, we set it to -1 to avoid confusion with
+ // color 0, which is used as a mask in some images
+ if (_mask == 0)
+ _mask = -1;
_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() {
+ _scriptManager->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() {
+ AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey);
+ if (!nod) {
+ nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate, false);
+ _scriptManager->addSideFX(nod);
+ } else
+ nod->stop();
+ _scriptManager->setStateValue(_slotKey, 2);
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionUnloadAnimation
+//////////////////////////////////////////////////////////////////////////////
+
+ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _key = 0;
+
+ sscanf(line.c_str(), "%u", &_key);
+}
+
+bool ActionUnloadAnimation::execute() {
+ AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_key);
+
+ if (nod && nod->getType() == ScriptingEffect::SCRIPTING_EFFECT_ANIM)
+ _scriptManager->deleteSideFx(_key);
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionPlayAnimation
//////////////////////////////////////////////////////////////////////////////
-ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) {
- char fileName[26];
+ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _x = 0;
+ _y = 0;
+ _x2 = 0;
+ _y2 = 0;
+ _start = 0;
+ _end = 0;
+ _loopCount = 0;
+ _mask = 0;
+ _framerate = 0;
+
+ 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);
+ "%24s %u %u %u %u %u %u %d %*u %*u %d %d",
+ fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate);
+
+ // Mask 0 means "no transparency" in this case. Since we use a common blitting
+ // code for images and animations, we set it to -1 to avoid confusion with
+ // color 0, which is used as a mask in some images
+ if (_mask == 0)
+ _mask = -1;
_fileName = Common::String(fileName);
+
+ // WORKAROUND for bug #6769, location me1g.scr (the "Alchemical debacle"
+ // video in ZGI). We only scale up by 2x, in AnimationEffect::process(),
+ // but the dimensions of the target frame are off by 2 pixels. We fix that
+ // here, so that the video can be scaled.
+ if (_fileName == "me1ga011.avi" && _y2 == 213)
+ _y2 = 215;
}
-bool ActionPlayAnimation::execute(ZVision *engine) {
- // TODO: Implement
- return true;
+ActionPlayAnimation::~ActionPlayAnimation() {
+ _scriptManager->deleteSideFx(_slotKey);
}
+bool ActionPlayAnimation::execute() {
+ AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey);
+
+ if (!nod) {
+ nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate);
+ _scriptManager->addSideFX(nod);
+ } else
+ nod->stop();
+
+ if (nod)
+ nod->addPlayNode(_slotKey, _x, _y, _x2, _y2, _start, _end, _loopCount);
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionPlayPreloadAnimation
//////////////////////////////////////////////////////////////////////////////
-ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(const Common::String &line) {
+ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _controlKey = 0;
+ _x1 = 0;
+ _y1 = 0;
+ _x2 = 0;
+ _y2 = 0;
+ _startFrame = 0;
+ _endFrame = 0;
+ _loopCount = 0;
+
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);
+bool ActionPlayPreloadAnimation::execute() {
+ AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_controlKey);
- // Set the needed values within the control
- control->setAnimationKey(_animationKey);
- control->setLoopCount(_loopCount);
- control->setXPos(_x1);
- control->setYPost(_y1);
-
- // 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;
}
-
//////////////////////////////////////////////////////////////////////////////
// ActionQuit
//////////////////////////////////////////////////////////////////////////////
-bool ActionQuit::execute(ZVision *engine) {
- engine->quitGame();
+bool ActionQuit::execute() {
+ _engine->quitGame();
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionRegion - only used by Zork: Nemesis
+//////////////////////////////////////////////////////////////////////////////
+
+ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _delay = 0;
+ _type = 0;
+ _unk1 = 0;
+ _unk2 = 0;
+
+ char art[64];
+ char custom[64];
+
+ int32 x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+ sscanf(line.c_str(), "%s %d %d %d %d %hu %hu %hu %hu %s", art, &x1, &y1, &x2, &y2, &_delay, &_type, &_unk1, &_unk2, custom);
+ _art = Common::String(art);
+ _custom = Common::String(custom);
+ _rect = Common::Rect(x1, y1, x2 + 1, y2 + 1);
+}
+
+ActionRegion::~ActionRegion() {
+ _scriptManager->killSideFx(_slotKey);
+}
+
+bool ActionRegion::execute() {
+ if (_scriptManager->getSideFX(_slotKey))
+ return true;
+
+ GraphicsEffect *effect = NULL;
+ switch (_type) {
+ case 0: {
+ uint16 centerX, centerY, frames;
+ double amplitude, waveln, speed;
+ sscanf(_custom.c_str(), "%hu,%hu,%hu,%lf,%lf,%lf,", &centerX, &centerY, &frames, &amplitude, &waveln, &speed);
+ effect = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed);
+ }
+ break;
+ case 1: {
+ uint16 aX, aY, aD;
+ if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
+ sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aY, &aX, &aD);
+ else
+ sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aX, &aY, &aD);
+ int8 minD;
+ int8 maxD;
+ EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD);
+ effect = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD);
+ }
+ break;
+ case 9: {
+ int16 dum1;
+ int32 dum2;
+ char buf[64];
+ sscanf(_custom.c_str(), "%hd,%d,%s", &dum1, &dum2, buf);
+ Graphics::Surface tempMask;
+ _engine->getRenderManager()->readImageToSurface(_art, tempMask);
+ if (_rect.width() != tempMask.w)
+ _rect.setWidth(tempMask.w);
+ if (_rect.height() != tempMask.h)
+ _rect.setHeight(tempMask.h);
+
+ EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0);
+ effect = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf));
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (effect) {
+ _scriptManager->addSideFX(new RegionNode(_engine, _slotKey, effect, _delay));
+ _engine->getRenderManager()->addEffect(effect);
+ }
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionRandom
//////////////////////////////////////////////////////////////////////////////
-ActionRandom::ActionRandom(const Common::String &line) {
- sscanf(line.c_str(), "%*[^:]:%*[^:]:%u, %u)", &_key, &_max);
+ActionRandom::ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ char maxBuffer[64];
+ memset(maxBuffer, 0, 64);
+ sscanf(line.c_str(), "%s", maxBuffer);
+ _max = new ValueSlot(_scriptManager, maxBuffer);
+}
+
+ActionRandom::~ActionRandom() {
+ 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());
+ _scriptManager->setStateValue(_slotKey, randNumber);
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionRestoreGame
+//////////////////////////////////////////////////////////////////////////////
+
+ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ char buf[128];
+ sscanf(line.c_str(), "%s", buf);
+ _fileName = Common::String(buf);
+}
+
+bool ActionRestoreGame::execute() {
+ _engine->getSaveManager()->loadGame(-1);
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionRotateTo
+//////////////////////////////////////////////////////////////////////////////
+
+ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _time = 0;
+ _toPos = 0;
+
+ sscanf(line.c_str(), "%d, %d", &_toPos, &_time);
+}
+
+bool ActionRotateTo::execute() {
+ _engine->getRenderManager()->rotateTo(_toPos, _time);
+
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionSetPartialScreen
//////////////////////////////////////////////////////////////////////////////
-ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) {
- char fileName[26];
- uint color;
+ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _x = 0;
+ _y = 0;
+
+ char fileName[25];
- sscanf(line.c_str(), "%*[^(](%u %u %25s %*u %u)", &_x, &_y, fileName, &color);
+ sscanf(line.c_str(), "%u %u %24s %*u %d", &_x, &_y, fileName, &_backgroundColor);
_fileName = Common::String(fileName);
- if (color > 0xFFFF) {
+ if (_backgroundColor > 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);
+bool ActionSetPartialScreen::execute() {
+ RenderManager *renderManager = _engine->getRenderManager();
+
+ if (_engine->getGameId() == GID_NEMESIS) {
+ if (_backgroundColor)
+ renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0);
+ else
+ renderManager->renderImageToBackground(_fileName, _x, _y);
+ } else {
+ if (_backgroundColor >= 0)
+ renderManager->renderImageToBackground(_fileName, _x, _y, _backgroundColor);
+ else if (_backgroundColor == -2)
+ renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0);
+ else
+ renderManager->renderImageToBackground(_fileName, _x, _y);
}
- renderManager->renderImageToScreen(_fileName, _x, _y);
return true;
}
-
//////////////////////////////////////////////////////////////////////////////
// ActionSetScreen
//////////////////////////////////////////////////////////////////////////////
-ActionSetScreen::ActionSetScreen(const Common::String &line) {
- char fileName[26];
- sscanf(line.c_str(), "%*[^(](%25[^)])", fileName);
+ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ char fileName[25];
+ sscanf(line.c_str(), "%24s", 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() {
+ _scriptManager->stopSideFx(_key);
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionStreamVideo
//////////////////////////////////////////////////////////////////////////////
-ActionStreamVideo::ActionStreamVideo(const Common::String &line) {
- char fileName[26];
- uint skippable;
+ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _x1 = 0;
+ _x2 = 0;
+ _y1 = 0;
+ _y2 = 0;
+ _flags = 0;
- sscanf(line.c_str(), "%*[^(](%25s %u %u %u %u %u %u)", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skippable);
+ char fileName[25];
+ uint skipline = 0; //skipline - render video with skip every second line, not skippable.
+
+ sscanf(line.c_str(), "%24s %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) {
- ZorkAVIDecoder decoder;
- if (!decoder.loadFile(_fileName)) {
+bool ActionStreamVideo::execute() {
+ Video::VideoDecoder *decoder;
+ 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);
+ bool subtitleExists = _engine->getSearchManager()->hasFile(subname);
+ bool switchToHires = false;
+
+// NOTE: We only show the hires MPEG2 videos when libmpeg2 is compiled in,
+// otherwise we fall back to the lowres ones
+#ifdef USE_MPEG2
+ Common::String hiresFileName = _fileName;
+ hiresFileName.setChar('d', hiresFileName.size() - 8);
+ hiresFileName.setChar('v', hiresFileName.size() - 3);
+ hiresFileName.setChar('o', hiresFileName.size() - 2);
+ hiresFileName.setChar('b', hiresFileName.size() - 1);
+
+ if (_scriptManager->getStateValue(StateKey_MPEGMovies) == 1 &&_engine->getSearchManager()->hasFile(hiresFileName)) {
+ // TODO: Enable once AC3 support is implemented
+ if (!_engine->getSearchManager()->hasFile(_fileName)) // Check for the regular video
+ return true;
+ warning("The hires videos of the DVD version of ZGI aren't supported yet, using lowres");
+ //_fileName = hiresFileName;
+ //switchToHires = true;
+ } else if (!_engine->getSearchManager()->hasFile(_fileName))
+ return true;
+#else
+ if (!_engine->getSearchManager()->hasFile(_fileName))
return true;
+#endif
+
+ decoder = _engine->loadAnimation(_fileName);
+ Subtitle *sub = (subtitleExists) ? new Subtitle(_engine, subname, switchToHires) : NULL;
+
+ _engine->getCursorManager()->showMouse(false);
+
+ if (switchToHires) {
+ _engine->initHiresScreen();
+ destRect = Common::Rect(40, -40, 760, 440);
+ Common::Rect workingWindow = _engine->_workingWindow;
+ workingWindow.translate(0, -40);
+ _engine->getRenderManager()->initSubArea(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, workingWindow);
}
- Common::Rect destRect;
- if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) {
- destRect = Common::Rect(_x1, _y1, _x2, _y2);
+ _engine->playVideo(*decoder, destRect, _skippable, sub);
+
+ if (switchToHires) {
+ _engine->initScreen();
+ _engine->getRenderManager()->initSubArea(WINDOW_WIDTH, WINDOW_HEIGHT, _engine->_workingWindow);
}
- engine->playVideo(decoder, destRect, _skippable);
+ _engine->getCursorManager()->showMouse(true);
+
+ delete decoder;
+ delete sub;
+
return true;
}
+//////////////////////////////////////////////////////////////////////////////
+// ActionSyncSound
+//////////////////////////////////////////////////////////////////////////////
+
+ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _syncto = 0;
+
+ char fileName[25];
+ int notUsed = 0;
+
+ sscanf(line.c_str(), "%d %d %24s", &_syncto, &notUsed, fileName);
+
+ _fileName = Common::String(fileName);
+}
+
+bool ActionSyncSound::execute() {
+ ScriptingEffect *fx = _scriptManager->getSideFX(_syncto);
+ if (!fx)
+ return true;
+
+ if (!(fx->getType() & ScriptingEffect::SCRIPTING_EFFECT_ANIM))
+ return true;
+
+ _scriptManager->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto));
+ return true;
+}
//////////////////////////////////////////////////////////////////////////////
// ActionTimer
//////////////////////////////////////////////////////////////////////////////
-ActionTimer::ActionTimer(const Common::String &line) {
- sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u)", &_key, &_time);
+ActionTimer::ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ char timeBuffer[64];
+ memset(timeBuffer, 0, 64);
+ sscanf(line.c_str(), "%s", timeBuffer);
+ _time = new ValueSlot(_scriptManager, timeBuffer);
+}
+
+ActionTimer::~ActionTimer() {
+ delete _time;
+ _scriptManager->killSideFx(_slotKey);
+}
+
+bool ActionTimer::execute() {
+ if (_scriptManager->getSideFX(_slotKey))
+ return true;
+ _scriptManager->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue()));
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ActionTtyText
+//////////////////////////////////////////////////////////////////////////////
+
+ActionTtyText::ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line) :
+ ResultAction(engine, slotkey) {
+ _delay = 0;
+
+ char filename[64];
+ int32 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ sscanf(line.c_str(), "%d %d %d %d %63s %u", &x1, &y1, &x2, &y2, filename, &_delay);
+ _r = Common::Rect(x1, y1, x2, y2);
+ _filename = Common::String(filename);
+}
+
+ActionTtyText::~ActionTtyText() {
+ _scriptManager->killSideFx(_slotKey);
}
-bool ActionTimer::execute(ZVision *engine) {
- engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time));
+bool ActionTtyText::execute() {
+ if (_scriptManager->getSideFX(_slotKey))
+ return true;
+ _scriptManager->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..ff19fc54fc 100644
--- a/engines/zvision/scripting/actions.h
+++ b/engines/zvision/scripting/actions.h
@@ -24,14 +24,16 @@
#define ZVISION_ACTIONS_H
#include "common/str.h"
+#include "common/rect.h"
#include "audio/mixer.h"
-
namespace ZVision {
// Forward declaration of ZVision. This file is included before ZVision is declared
class ZVision;
+class ScriptManager;
+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);
virtual ~ResultAction() {}
/**
* This is called by the script system whenever a Puzzle's criteria are found to be true.
@@ -48,75 +51,48 @@ 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;
-};
-
-
-// The different types of actions
-// DEBUG,
-// DISABLE_CONTROL,
-// DISABLE_VENUS,
-// DISPLAY_MESSAGE,
-// DISSOLVE,
-// DISTORT,
-// ENABLE_CONTROL,
-// FLUSH_MOUSE_EVENTS,
-// INVENTORY,
-// KILL,
-// MENU_BAR_ENABLE,
-// MUSIC,
-// PAN_TRACK,
-// PLAY_PRELOAD,
-// PREFERENCES,
-// QUIT,
-// RANDOM,
-// REGION,
-// RESTORE_GAME,
-// ROTATE_TO,
-// SAVE_GAME,
-// SET_PARTIAL_SCREEN,
-// SET_SCREEN,
-// SET_VENUS,
-// STOP,
-// STREAM_VIDEO,
-// SYNC_SOUND,
-// TTY_TEXT,
-// UNIVERSE_MUSIC,
+ virtual bool execute() = 0;
+protected:
+ ZVision *_engine;
+ ScriptManager *_scriptManager;
+ int32 _slotKey;
+};
class ActionAdd : public ResultAction {
public:
- ActionAdd(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint32 _key;
- uint _value;
+ int _value;
};
class ActionAssign : public ResultAction {
public:
- ActionAssign(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionAssign();
+ bool execute();
private:
uint32 _key;
- uint _value;
+ ValueSlot *_value;
};
class ActionAttenuate : public ResultAction {
public:
- ActionAttenuate(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint32 _key;
- int _attenuation;
+ int32 _attenuation;
};
class ActionChangeLocation : public ResultAction {
public:
- ActionChangeLocation(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
char _world;
@@ -128,121 +104,169 @@ 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 {
+class ActionCursor : public ResultAction {
public:
- ActionDebug(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
+ uint8 _action;
};
class ActionDelayRender : public ResultAction {
public:
- ActionDelayRender(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
- // TODO: Check if this should actually be frames or if it should be milliseconds/seconds
- uint32 framesToDelay;
+ uint32 _framesToDelay;
};
class ActionDisableControl : public ResultAction {
public:
- ActionDisableControl(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint32 _key;
};
-class ActionDisableVenus : public ResultAction {
+class ActionDisplayMessage : public ResultAction {
public:
- ActionDisableVenus(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
+ int16 _control;
+ int16 _msgid;
};
-class ActionDisplayMessage : public ResultAction {
+class ActionDissolve : public ResultAction {
public:
- ActionDisplayMessage(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionDissolve(ZVision *engine);
+ bool execute();
+};
+
+class ActionDistort : public ResultAction {
+public:
+ ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionDistort();
+ bool execute();
private:
+ int16 _distSlot;
+ int16 _speed;
+ float _startAngle;
+ float _endAngle;
+ float _startLineScale;
+ float _endLineScale;
};
-class ActionDissolve : public ResultAction {
+class ActionEnableControl : public ResultAction {
public:
- ActionDissolve();
- bool execute(ZVision *engine);
+ ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+
+private:
+ uint32 _key;
};
-class ActionDistort : public ResultAction {
+class ActionFlushMouseEvents : public ResultAction {
public:
- ActionDistort(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionFlushMouseEvents(ZVision *engine, int32 slotkey);
+ bool execute();
+};
+class ActionInventory : public ResultAction {
+public:
+ ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
+ int8 _type;
+ int32 _key;
};
-class ActionEnableControl : public ResultAction {
+class ActionKill : public ResultAction {
public:
- ActionEnableControl(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionKill(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint32 _key;
+ uint32 _type;
+};
+
+class ActionMenuBarEnable : public ResultAction {
+public:
+ ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+private:
+ uint16 _menus;
};
class ActionMusic : public ResultAction {
public:
- ActionMusic(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global);
+ ~ActionMusic();
+ bool execute();
private:
- uint32 _key;
- Audio::Mixer::SoundType _soundType;
Common::String _fileName;
bool _loop;
- byte _volume;
+ ValueSlot *_volume;
+ bool _universe;
+ bool _midi;
+ int8 _note;
+ int8 _prog;
+};
+
+class ActionPanTrack : public ResultAction {
+public:
+ ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionPanTrack();
+ bool execute();
+
+private:
+ int32 _pos;
+ uint32 _musicSlot;
};
class ActionPlayAnimation : public ResultAction {
public:
- ActionPlayAnimation(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionPlayAnimation();
+ bool execute();
private:
- uint32 _key;
Common::String _fileName;
uint32 _x;
uint32 _y;
- uint32 _width;
- uint32 _height;
+ uint32 _x2;
+ uint32 _y2;
uint32 _start;
uint32 _end;
- uint _mask;
- uint _framerate;
- uint _loopCount;
+ int32 _mask;
+ int32 _framerate;
+ int32 _loopCount;
};
class ActionPlayPreloadAnimation : public ResultAction {
public:
- ActionPlayPreloadAnimation(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint32 _animationKey;
@@ -258,64 +282,119 @@ 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 ActionPreferences : public ResultAction {
+public:
+ ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+
+private:
+ bool _save;
};
class ActionQuit : public ResultAction {
public:
- ActionQuit() {}
- bool execute(ZVision *engine);
+ ActionQuit(ZVision *engine, int32 slotkey) : ResultAction(engine, slotkey) {}
+ bool execute();
};
-// TODO: See if this exists in ZGI. It doesn't in ZNem
+class ActionRegion : public ResultAction {
+public:
+ ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionRegion();
+ bool execute();
+
+private:
+ Common::String _art;
+ Common::String _custom;
+ Common::Rect _rect;
+ uint16 _delay;
+ uint16 _type;
+ uint16 _unk1;
+ uint16 _unk2;
+};
+
+// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
class ActionUnloadAnimation : public ResultAction {
public:
- ActionUnloadAnimation(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+private:
+ uint32 _key;
};
class ActionRandom : public ResultAction {
public:
- ActionRandom(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionRandom();
+ bool execute();
private:
- uint32 _key;
- uint _max;
+ ValueSlot *_max;
+};
+
+class ActionRestoreGame : public ResultAction {
+public:
+ ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+
+private:
+ Common::String _fileName;
+};
+
+class ActionRotateTo : public ResultAction {
+public:
+ ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
+
+private:
+ int32 _toPos;
+ int32 _time;
};
class ActionSetPartialScreen : public ResultAction {
public:
- ActionSetPartialScreen(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
uint _x;
uint _y;
Common::String _fileName;
- uint16 _backgroundColor;
+ int32 _backgroundColor;
};
class ActionSetScreen : public ResultAction {
public:
- ActionSetScreen(const Common::String &line);
- bool execute(ZVision *engine);
+ ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line);
+ bool execute();
private:
Common::String _fileName;
};
+class 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 +410,36 @@ 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:
- uint32 _key;
- uint _time;
+ int _syncto;
+ Common::String _fileName;
};
+class ActionTimer : public ResultAction {
+public:
+ ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line);
+ ~ActionTimer();
+ bool execute();
+private:
+ 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..81123eb99b 100644
--- a/engines/zvision/scripting/control.cpp
+++ b/engines/zvision/scripting/control.cpp
@@ -23,34 +23,15 @@
#include "common/scummsys.h"
#include "zvision/scripting/control.h"
+#include "zvision/scripting/script_manager.h"
#include "zvision/zvision.h"
#include "zvision/graphics/render_manager.h"
-#include "zvision/utility/utility.h"
#include "common/stream.h"
-
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);
}
@@ -61,7 +42,7 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &
// Loop until we find the closing brace
Common::String line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
while (!stream.eos() && !line.contains('}')) {
if (line.matchString("angle*", true)) {
@@ -79,23 +60,26 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &
renderTable->setPanoramaReverse(true);
}
} else if (line.matchString("zeropoint*", true)) {
- // TODO: Implement
+ uint point;
+ sscanf(line.c_str(), "zeropoint(%u)", &point);
+ renderTable->setPanoramaZeroPoint(point);
}
line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
}
renderTable->generateRenderTable();
}
+// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) {
RenderTable *renderTable = engine->getRenderManager()->getRenderTable();
renderTable->setRenderState(RenderTable::TILT);
// Loop until we find the closing brace
Common::String line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
while (!stream.eos() && !line.contains('}')) {
if (line.matchString("angle*", true)) {
@@ -115,10 +99,40 @@ void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stre
}
line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
}
renderTable->generateRenderTable();
}
+void Control::getParams(const Common::String &inputStr, Common::String &parameter, Common::String &values) {
+ const char *chrs = inputStr.c_str();
+ uint lbr;
+
+ for (lbr = 0; lbr < inputStr.size(); lbr++)
+ if (chrs[lbr] == '(')
+ break;
+
+ if (lbr >= inputStr.size())
+ return;
+
+ uint rbr;
+
+ for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
+ if (chrs[rbr] == ')')
+ break;
+
+ if (rbr >= inputStr.size())
+ return;
+
+ parameter = Common::String(chrs, chrs + lbr);
+ values = Common::String(chrs + lbr + 1, chrs + rbr);
+}
+
+void Control::setVenus() {
+ if (_venusId >= 0)
+ if (_engine->getScriptManager()->getStateValue(_venusId) > 0)
+ _engine->getScriptManager()->setStateValue(StateKey_Venus, _venusId);
+}
+
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h
index ffeacb273d..108b83fd00 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,7 +24,7 @@
#define ZVISION_CONTROL_H
#include "common/keyboard.h"
-
+#include "common/str.h"
namespace Common {
class SeekableReadStream;
@@ -36,16 +36,39 @@ namespace ZVision {
class ZVision;
+/**
+ * The base class for all Controls.
+ *
+ * Controls are the things that the user interacts with. Ex: A lever on the door
+ */
class Control {
public:
- Control() : _engine(0), _key(0), _enabled(false) {}
- Control(ZVision *engine, uint32 key) : _engine(engine), _key(key), _enabled(false) {}
+
+ enum ControlType {
+ CONTROL_UNKNOW,
+ CONTROL_INPUT,
+ CONTROL_PUSHTGL,
+ CONTROL_SLOT,
+ CONTROL_LEVER,
+ CONTROL_SAVE,
+ CONTROL_SAFE,
+ CONTROL_FIST,
+ CONTROL_TITLER,
+ CONTROL_HOTMOV,
+ CONTROL_PAINT
+ };
+
+ Control(ZVision *engine, uint32 key, ControlType type) : _engine(engine), _key(key), _type(type), _venusId(-1) {}
virtual ~Control() {}
- uint32 getKey() { return _key; }
+ uint32 getKey() {
+ return _key;
+ }
+
+ ControlType getType() {
+ return _type;
+ }
- virtual void enable();
- virtual void disable();
virtual void focus() {}
virtual void unfocus() {}
/**
@@ -54,14 +77,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,78 +96,52 @@ 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.
*
* @param keycode The key that was pressed
*/
- virtual void onKeyDown(Common::KeyState keyState) {}
+ virtual bool onKeyDown(Common::KeyState keyState) {
+ return false;
+ }
/**
* Called when a key is released. Default is NOP.
*
* @param keycode The key that was pressed
*/
- virtual void onKeyUp(Common::KeyState keyState) {}
+ virtual bool onKeyUp(Common::KeyState keyState) {
+ return false;
+ }
/**
* Processes the node given the deltaTime since last frame. Default is NOP.
*
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
* @return If true, the node can be deleted after process() finishes
*/
- virtual bool process(uint32 deltaTimeInMillis) { return false; }
- /**
- * Serialize a Control for save game use. This should only be used if a Control needs
- * to save values that would be different from initialization. AKA a TimerNode needs to
- * store the amount of time left on the timer. Any Controls overriding this *MUST* write
- * their key as the first data outputted. The default implementation is NOP.
- *
- * NOTE: If this method is overridden, you MUST also override deserialize()
- * and needsSerialization()
- *
- * @param stream Stream to write any needed data to
- */
- virtual void serialize(Common::WriteStream *stream) {}
- /**
- * De-serialize data from a save game stream. This should only be implemented if the
- * Control also implements serialize(). The calling method assumes the size of the
- * data read from the stream exactly equals that written in serialize(). The default
- * implementation is NOP.
- *
- * NOTE: If this method is overridden, you MUST also override serialize()
- * and needsSerialization()
- *
- * @param stream Save game file stream
- */
- virtual void deserialize(Common::SeekableReadStream *stream) {}
- /**
- * If a Control overrides serialize() and deserialize(), this should return true
- *
- * @return Does the Control need save game serialization?
- */
- virtual inline bool needsSerialization() { return false; }
+ virtual bool process(uint32 deltaTimeInMillis) {
+ return false;
+ }
+
+ void setVenus();
protected:
- ZVision * _engine;
+ ZVision *_engine;
uint32 _key;
- bool _enabled;
+ int32 _venusId;
+ void getParams(const Common::String &inputStr, Common::String &parameter, Common::String &values);
// Static member functions
public:
static void parseFlatControl(ZVision *engine);
static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream);
static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream);
+private:
+ ControlType _type;
};
-// TODO: Implement InputControl
-// TODO: Implement SaveControl
-// TODO: Implement SlotControl
-// TODO: Implement SafeControl
-// TODO: Implement FistControl
-// TODO: Implement HotMovieControl
-// TODO: Implement PaintControl
-// TODO: Implement TilterControl
-
} // End of namespace ZVision
#endif
diff --git a/engines/zvision/scripting/controls/animation_control.cpp b/engines/zvision/scripting/controls/animation_control.cpp
deleted file mode 100644
index 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/fist_control.cpp b/engines/zvision/scripting/controls/fist_control.cpp
new file mode 100644
index 0000000000..f79c82dc79
--- /dev/null
+++ b/engines/zvision/scripting/controls/fist_control.cpp
@@ -0,0 +1,304 @@
+/* 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/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/scripting/controls/fist_control.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+#include "zvision/video/rlf_decoder.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+namespace ZVision {
+
+FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_FIST) {
+ _cursor = CursorIndex_Idle;
+ _animation = NULL;
+ _soundKey = 0;
+ _fiststatus = 0;
+ _order = 0;
+ _fistnum = 0;
+
+ _animationId = 0;
+
+ clearFistArray(_fistsUp);
+ clearFistArray(_fistsDwn);
+
+ _numEntries = 0;
+ _entries.clear();
+
+ _anmRect = Common::Rect();
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("sound_key", true)) {
+ _soundKey = atoi(values.c_str());
+ } else if (param.matchString("cursor", true)) {
+ _cursor = _engine->getCursorManager()->getCursorId(values);
+ } else if (param.matchString("descfile", true)) {
+ readDescFile(values);
+ } else if (param.matchString("animation_id", true)) {
+ _animationId = atoi(values.c_str());
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+}
+
+FistControl::~FistControl() {
+ if (_animation)
+ delete _animation;
+
+ clearFistArray(_fistsUp);
+ clearFistArray(_fistsDwn);
+ _entries.clear();
+}
+
+bool FistControl::process(uint32 deltaTimeInMillis) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_animation && _animation->isPlaying()) {
+ if (_animation->endOfVideo()) {
+ _animation->stop();
+ _engine->getScriptManager()->setStateValue(_animationId, 2);
+ return false;
+ }
+
+ if (_animation->needsUpdate()) {
+ const Graphics::Surface *frameData = _animation->decodeNextFrame();
+ if (frameData)
+ // WORKAROUND: Ignore the target frame dimensions for the finger animations.
+ // The target dimensions specify an area smaller than expected, thus if we
+ // scale the finger videos to fit these dimensions, they are not aligned
+ // correctly. Not scaling these videos yields a result identical to the
+ // original. Fixes bug #6784.
+ _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _anmRect.left, _anmRect.top);
+ }
+ }
+
+ return false;
+}
+
+bool FistControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (mouseIn(screenSpacePos, backgroundImageSpacePos) >= 0) {
+ _engine->getCursorManager()->changeCursor(_cursor);
+ return true;
+ }
+
+ return false;
+}
+
+bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ int fistNumber = mouseIn(screenSpacePos, backgroundImageSpacePos);
+
+ if (fistNumber >= 0) {
+ setVenus();
+
+ uint32 oldStatus = _fiststatus;
+ _fiststatus ^= (1 << fistNumber);
+
+ for (int i = 0; i < _numEntries; i++)
+ if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) {
+ if (_animation) {
+ _animation->stop();
+ _animation->seekToFrame(_entries[i]._anmStrt);
+ _animation->setEndFrame(_entries[i]._anmEnd);
+ _animation->start();
+ }
+
+ _engine->getScriptManager()->setStateValue(_animationId, 1);
+ _engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound);
+ break;
+ }
+
+ _engine->getScriptManager()->setStateValue(_key, _fiststatus);
+ }
+
+ return false;
+}
+
+void FistControl::readDescFile(const Common::String &fileName) {
+ Common::File file;
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
+ warning("Desc file %s could could be opened", fileName.c_str());
+ return;
+ }
+
+ Common::String line;
+ Common::String param;
+ Common::String values;
+
+ while (!file.eos()) {
+ line = file.readLine();
+ getFistParams(line, param, values);
+
+ if (param.matchString("animation_id", true)) {
+ // Not used
+ } else if (param.matchString("animation", true)) {
+ _animation = _engine->loadAnimation(values);
+ } else if (param.matchString("anim_rect", true)) {
+ int left, top, right, bottom;
+ sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom);
+ _anmRect = Common::Rect(left, top, right, bottom);
+ } else if (param.matchString("num_fingers", true)) {
+ sscanf(values.c_str(), "%d", &_fistnum);
+ _fistsUp.resize(_fistnum);
+ _fistsDwn.resize(_fistnum);
+ } else if (param.matchString("entries", true)) {
+ sscanf(values.c_str(), "%d", &_numEntries);
+ _entries.resize(_numEntries);
+ } else if (param.matchString("eval_order_ascending", true)) {
+ sscanf(values.c_str(), "%d", &_order);
+ } else if (param.matchString("up_hs_num_*", true)) {
+ int fist, num;
+ num = atoi(values.c_str());
+
+ sscanf(param.c_str(), "up_hs_num_%d", &fist);
+ _fistsUp[fist].resize(num);
+ } else if (param.matchString("up_hs_*", true)) {
+ int16 fist, box, x1, y1, x2, y2;
+ sscanf(param.c_str(), "up_hs_%hd_%hd", &fist, &box);
+ sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2);
+ (_fistsUp[fist])[box] = Common::Rect(x1, y1, x2, y2);
+ } else if (param.matchString("down_hs_num_*", true)) {
+ int fist, num;
+ num = atoi(values.c_str());
+
+ sscanf(param.c_str(), "down_hs_num_%d", &fist);
+ _fistsDwn[fist].resize(num);
+ } else if (param.matchString("down_hs_*", true)) {
+ int16 fist, box, x1, y1, x2, y2;
+ sscanf(param.c_str(), "down_hs_%hd_%hd", &fist, &box);
+ sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2);
+ (_fistsDwn[fist])[box] = Common::Rect(x1, y1, x2, y2);
+ } else {
+ int entry, start, end, sound;
+ char bitsStart[33];
+ char bitsEnd[33];
+ entry = atoi(param.c_str());
+ if (sscanf(values.c_str(), "%s %s %d %d (%d)", bitsStart, bitsEnd, &start, &end, &sound) == 5) {
+ _entries[entry]._bitsStrt = readBits(bitsStart);
+ _entries[entry]._bitsEnd = readBits(bitsEnd);
+ _entries[entry]._anmStrt = start;
+ _entries[entry]._anmEnd = end;
+ _entries[entry]._sound = sound;
+ }
+ }
+ }
+ file.close();
+}
+
+void FistControl::clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr) {
+ for (uint i = 0; i < arr.size(); i++)
+ arr[i].clear();
+
+ arr.clear();
+}
+
+uint32 FistControl::readBits(const char *str) {
+ uint32 bfield = 0;
+ int len = strlen(str);
+ for (int i = 0; i < len; i++)
+ if (str[i] != '0')
+ bfield |= (1 << i);
+ return bfield;
+}
+
+int FistControl::mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_order) {
+ for (int i = 0; i < _fistnum; i++) {
+ if (((_fiststatus >> i) & 1) == 1) {
+ for (uint j = 0; j < _fistsDwn[i].size(); j++)
+ if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
+ return i;
+ } else {
+ for (uint j = 0; j < _fistsUp[i].size(); j++)
+ if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
+ return i;
+ }
+ }
+ } else {
+ for (int i = _fistnum - 1; i >= 0; i--) {
+ if (((_fiststatus >> i) & 1) == 1) {
+ for (uint j = 0; j < _fistsDwn[i].size(); j++)
+ if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
+ return i;
+ } else {
+ for (uint j = 0; j < _fistsUp[i].size(); j++)
+ if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+void FistControl::getFistParams(const Common::String &inputStr, Common::String &parameter, Common::String &values) {
+ const char *chrs = inputStr.c_str();
+ uint lbr;
+
+ for (lbr = 0; lbr < inputStr.size(); lbr++)
+ if (chrs[lbr] == ':')
+ break;
+
+ if (lbr >= inputStr.size())
+ return;
+
+ uint rbr;
+
+ for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
+ if (chrs[rbr] == '~')
+ break;
+
+ if (rbr >= inputStr.size())
+ return;
+
+ parameter = Common::String(chrs, chrs + lbr);
+ values = Common::String(chrs + lbr + 1, chrs + rbr);
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/fist_control.h b/engines/zvision/scripting/controls/fist_control.h
new file mode 100644
index 0000000000..d7cbcb1f71
--- /dev/null
+++ b/engines/zvision/scripting/controls/fist_control.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_FIST_CONTROL_H
+#define ZVISION_FIST_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Video {
+ class VideoDecoder;
+}
+
+namespace ZVision {
+
+// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
+class FistControl : public Control {
+public:
+ FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~FistControl();
+
+private:
+ uint32 _fiststatus;
+ int _fistnum;
+ int16 _cursor;
+ int _order;
+
+ Common::Array< Common::Array<Common::Rect> > _fistsUp;
+ Common::Array< Common::Array<Common::Rect> > _fistsDwn;
+
+ int32 _numEntries;
+
+ struct entries {
+ uint32 _bitsStrt;
+ uint32 _bitsEnd;
+ int32 _anmStrt;
+ int32 _anmEnd;
+ int32 _sound;
+ };
+
+ Common::Array<entries> _entries;
+
+ Video::VideoDecoder *_animation;
+ Common::Rect _anmRect;
+ int32 _soundKey;
+ int32 _animationId;
+
+public:
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ void readDescFile(const Common::String &fileName);
+ void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr);
+ uint32 readBits(const char *str);
+ int mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ void getFistParams(const Common::String &inputStr, Common::String &parameter, Common::String &values);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/hotmov_control.cpp b/engines/zvision/scripting/controls/hotmov_control.cpp
new file mode 100644
index 0000000000..182447a990
--- /dev/null
+++ b/engines/zvision/scripting/controls/hotmov_control.cpp
@@ -0,0 +1,188 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/hotmov_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+namespace ZVision {
+
+HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_HOTMOV) {
+ _animation = NULL;
+ _cycle = 0;
+ _frames.clear();
+ _cyclesCount = 0;
+ _framesCount = 0;
+
+ _engine->getScriptManager()->setStateValue(_key, 0);
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("hs_frame_list", true)) {
+ readHsFile(values);
+ } else if (param.matchString("rectangle", true)) {
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height);
+
+ _rectangle = Common::Rect(x, y, width, height);
+ } else if (param.matchString("num_frames", true)) {
+ _framesCount = atoi(values.c_str());
+ } else if (param.matchString("num_cycles", true)) {
+ _cyclesCount = atoi(values.c_str());
+ } else if (param.matchString("animation", true)) {
+ char filename[64];
+ sscanf(values.c_str(), "%s", filename);
+ values = Common::String(filename);
+ _animation = _engine->loadAnimation(values);
+ _animation->start();
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+}
+
+HotMovControl::~HotMovControl() {
+ if (_animation)
+ delete _animation;
+
+ _frames.clear();
+}
+
+bool HotMovControl::process(uint32 deltaTimeInMillis) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_cycle < _cyclesCount) {
+ if (_animation && _animation->endOfVideo()) {
+ _cycle++;
+
+ if (_cycle == _cyclesCount) {
+ _engine->getScriptManager()->setStateValue(_key, 2);
+ return false;
+ }
+
+ _animation->rewind();
+ }
+
+ if (_animation && _animation->needsUpdate()) {
+ const Graphics::Surface *frameData = _animation->decodeNextFrame();
+ if (frameData)
+ _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle);
+ }
+ }
+
+ return false;
+}
+
+bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (!_animation)
+ return false;
+
+ if (_cycle < _cyclesCount) {
+ if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
+ _engine->getCursorManager()->changeCursor(CursorIndex_Active);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (!_animation)
+ return false;
+
+ if (_cycle < _cyclesCount) {
+ if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
+ setVenus();
+ _engine->getScriptManager()->setStateValue(_key, 1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void HotMovControl::readHsFile(const Common::String &fileName) {
+ if (_framesCount == 0)
+ return;
+
+ Common::File file;
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
+ warning("HS file %s could could be opened", fileName.c_str());
+ return;
+ }
+
+ Common::String line;
+
+ _frames.resize(_framesCount);
+
+ while (!file.eos()) {
+ line = file.readLine();
+
+ int frame;
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(line.c_str(), "%d:%d %d %d %d~", &frame, &x, &y, &width, &height);
+
+ if (frame >= 0 && frame < _framesCount)
+ _frames[frame] = Common::Rect(x, y, width, height);
+ }
+ file.close();
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/hotmov_control.h b/engines/zvision/scripting/controls/hotmov_control.h
new file mode 100644
index 0000000000..99d1fd0979
--- /dev/null
+++ b/engines/zvision/scripting/controls/hotmov_control.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_HOTMOV_CONTROL_H
+#define ZVISION_HOTMOV_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Video {
+ class VideoDecoder;
+}
+
+namespace ZVision {
+
+// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
+class HotMovControl : public Control {
+public:
+ HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~HotMovControl();
+
+private:
+ int32 _framesCount;
+ int32 _cycle;
+ int32 _cyclesCount;
+ Video::VideoDecoder *_animation;
+ Common::Rect _rectangle;
+ Common::Array<Common::Rect> _frames;
+public:
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ void readHsFile(const Common::String &fileName);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp
index a35548d02e..df0c77ba96 100644
--- a/engines/zvision/scripting/controls/input_control.cpp
+++ b/engines/zvision/scripting/controls/input_control.cpp
@@ -8,12 +8,12 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -23,120 +23,249 @@
#include "common/scummsys.h"
#include "zvision/scripting/controls/input_control.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
#include "zvision/zvision.h"
#include "zvision/scripting/script_manager.h"
-#include "zvision/strings/string_manager.h"
+#include "zvision/text/string_manager.h"
#include "zvision/graphics/render_manager.h"
-#include "zvision/utility/utility.h"
#include "common/str.h"
#include "common/stream.h"
#include "common/rect.h"
-
+#include "video/video_decoder.h"
namespace ZVision {
InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
- : Control(engine, key),
- _nextTabstop(0),
- _focused(false),
- _textChanged(false),
- _cursorOffset(0) {
+ : Control(engine, key, CONTROL_INPUT),
+ _background(0),
+ _nextTabstop(0),
+ _focused(false),
+ _textChanged(false),
+ _cursorOffset(0),
+ _enterPressed(false),
+ _readOnly(false),
+ _txtWidth(0),
+ _animation(NULL) {
// Loop until we find the closing brace
Common::String line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
while (!stream.eos() && !line.contains('}')) {
- if (line.matchString("*rectangle*", true)) {
+ if (param.matchString("rectangle", true)) {
int x1;
int y1;
int x2;
int y2;
- sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2);
+ sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2);
_textRectangle = Common::Rect(x1, y1, x2, y2);
- } else if (line.matchString("*aux_hotspot*", true)) {
+ } else if (param.matchString("aux_hotspot", true)) {
int x1;
int y1;
int x2;
int y2;
- sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2);
+ sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2);
_headerRectangle = Common::Rect(x1, y1, x2, y2);
- } else if (line.matchString("*string_init*", true)) {
+ } else if (param.matchString("string_init", true)) {
uint fontFormatNumber;
- sscanf(line.c_str(), "%*[^(](%u)", &fontFormatNumber);
+ sscanf(values.c_str(), "%u", &fontFormatNumber);
- _textStyle = _engine->getStringManager()->getTextStyle(fontFormatNumber);
- } else if (line.matchString("*next_tabstop*", true)) {
- sscanf(line.c_str(), "%*[^(](%u)", &_nextTabstop);
- } else if (line.matchString("*cursor_animation*", true)) {
- char fileName[26];
+ _stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
+ } else if (param.matchString("chooser_init_string", true)) {
+ uint fontFormatNumber;
- sscanf(line.c_str(), "%*[^(](%25s %*u)", fileName);
+ sscanf(values.c_str(), "%u", &fontFormatNumber);
- _cursorAnimationFileName = Common::String(fileName);
- } else if (line.matchString("*cursor_dimensions*", true)) {
+ _stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
+ } else if (param.matchString("next_tabstop", true)) {
+ sscanf(values.c_str(), "%u", &_nextTabstop);
+ } else if (param.matchString("cursor_dimensions", true)) {
// Ignore, use the dimensions in the animation file
- } else if (line.matchString("*cursor_animation_frames*", true)) {
+ } else if (param.matchString("cursor_animation_frames", true)) {
// Ignore, use the frame count in the animation file
- } else if (line.matchString("*focus*", true)) {
+ } else if (param.matchString("cursor_animation", true)) {
+ char fileName[25];
+
+ sscanf(values.c_str(), "%24s %*u", fileName);
+
+ _animation = _engine->loadAnimation(fileName);
+ _animation->start();
+ } else if (param.matchString("focus", true)) {
_focused = true;
+ _engine->getScriptManager()->setFocusControlKey(_key);
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
}
line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ _maxTxtWidth = _textRectangle.width();
+ if (_animation)
+ _maxTxtWidth -= _animation->getWidth();
+}
+
+InputControl::~InputControl() {
+ _background->free();
+ delete _background;
+}
+
+bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_textRectangle.contains(backgroundImageSpacePos)) {
+ if (!_readOnly) {
+ // Save
+ _engine->getScriptManager()->focusControl(_key);
+ setVenus();
+ } else {
+ // Restore
+ if (_currentInputText.size()) {
+ setVenus();
+ _enterPressed = true;
+ }
+ }
}
+ return false;
}
-void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
- _engine->getScriptManager()->focusControl(_key);
+bool InputControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_textRectangle.contains(backgroundImageSpacePos)) {
+ if (!_readOnly) {
+ // Save
+ _engine->getCursorManager()->changeCursor(CursorIndex_Active);
+ return true;
+ } else {
+ // Restore
+ if (_currentInputText.size()) {
+ _engine->getCursorManager()->changeCursor(CursorIndex_Active);
+ _engine->getScriptManager()->focusControl(_key);
+ return true;
+ }
+ }
+ }
+ return false;
}
-void InputControl::onKeyDown(Common::KeyState keyState) {
+bool InputControl::onKeyDown(Common::KeyState keyState) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
if (!_focused) {
- return;
+ return false;
}
if (keyState.keycode == Common::KEYCODE_BACKSPACE) {
- _currentInputText.deleteLastChar();
+ if (!_readOnly) {
+ _currentInputText.deleteLastChar();
+ _textChanged = true;
+ }
+ } else if (keyState.keycode == Common::KEYCODE_RETURN) {
+ _enterPressed = true;
} else if (keyState.keycode == Common::KEYCODE_TAB) {
- _focused = false;
+ unfocus();
// Focus the next input control
_engine->getScriptManager()->focusControl(_nextTabstop);
+ // Don't process this event for other controls
+ return true;
} else {
- // Otherwise, append the new character to the end of the current text
-
- uint16 asciiValue = keyState.ascii;
- // We only care about text values
- if (asciiValue >= 32 && asciiValue <= 126) {
- _currentInputText += (char)asciiValue;
- _textChanged = true;
+ if (!_readOnly) {
+ // Otherwise, append the new character to the end of the current text
+ uint16 asciiValue = keyState.ascii;
+ // We only care about text values
+ if (asciiValue >= 32 && asciiValue <= 126) {
+ _currentInputText += (char)asciiValue;
+ _textChanged = true;
+ }
}
}
+ return false;
}
bool InputControl::process(uint32 deltaTimeInMillis) {
- if (!_focused) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
return false;
+
+ if (!_background) {
+ _background = _engine->getRenderManager()->getBkgRect(_textRectangle);
}
// First see if we need to render the text
if (_textChanged) {
// Blit the text using the RenderManager
- Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width());
- _cursorOffset = destRect.left - _textRectangle.left;
+ Graphics::Surface txt;
+ txt.copyFrom(*_background);
+
+ int32 oldTxtWidth = _txtWidth;
+
+ if (!_readOnly || !_focused)
+ _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt);
+ else
+ _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt);
+
+ if (_readOnly || _txtWidth <= _maxTxtWidth)
+ _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top);
+ else {
+ // Assume the last character caused the overflow.
+ _currentInputText.deleteLastChar();
+ _txtWidth = oldTxtWidth;
+ }
+
+ txt.free();
}
- // Render the next frame of the animation
- // TODO: Implement animation handling
+ if (_animation && !_readOnly && _focused) {
+ if (_animation->endOfVideo())
+ _animation->rewind();
+ if (_animation->needsUpdate()) {
+ const Graphics::Surface *srf = _animation->decodeNextFrame();
+ int16 xx = _textRectangle.left + _txtWidth;
+ if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth()))
+ xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth();
+ _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top);
+ }
+ }
+
+ _textChanged = false;
+ return false;
+}
+
+bool InputControl::enterPress() {
+ if (_enterPressed) {
+ _enterPressed = false;
+ return true;
+ }
return false;
}
+void InputControl::setText(const Common::String &_str) {
+ _currentInputText = _str;
+ _textChanged = true;
+}
+
+const Common::String InputControl::getText() {
+ return _currentInputText;
+}
+
+void InputControl::setReadOnly(bool readonly) {
+ _readOnly = readonly;
+}
+
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h
index 32432438bb..9b48514e16 100644
--- a/engines/zvision/scripting/controls/input_control.h
+++ b/engines/zvision/scripting/controls/input_control.h
@@ -24,35 +24,60 @@
#define ZVISION_INPUT_CONTROL_H
#include "zvision/scripting/control.h"
-#include "zvision/strings/string_manager.h"
+#include "zvision/text/text.h"
+#include "zvision/text/string_manager.h"
#include "common/rect.h"
+namespace Video {
+ class VideoDecoder;
+}
namespace ZVision {
class InputControl : public Control {
public:
InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~InputControl();
private:
+ Graphics::Surface *_background;
Common::Rect _textRectangle;
Common::Rect _headerRectangle;
- StringManager::TextStyle _textStyle;
+ TextStyleState _stringInit;
+ TextStyleState _stringChooserInit;
uint32 _nextTabstop;
- Common::String _cursorAnimationFileName;
bool _focused;
Common::String _currentInputText;
bool _textChanged;
uint _cursorOffset;
+ bool _enterPressed;
+ bool _readOnly;
+
+ int16 _txtWidth;
+ int16 _maxTxtWidth;
+ Video::VideoDecoder *_animation;
+ int32 _frameDelay;
+ int16 _frame;
public:
- void focus() { _focused = true; }
- void unfocus() { _focused = false; }
- void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
- void onKeyDown(Common::KeyState keyState);
+ void focus() {
+ _focused = true;
+ _textChanged = true;
+ }
+ void unfocus() {
+ _focused = false;
+ _textChanged = true;
+ }
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool onKeyDown(Common::KeyState keyState);
bool process(uint32 deltaTimeInMillis);
+ void setText(const Common::String &_str);
+ const Common::String getText();
+ bool enterPress();
+ void setReadOnly(bool);
};
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp
index 9724e661b7..0f105b424c 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.
@@ -27,130 +27,128 @@
#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/graphics/cursors/cursor_manager.h"
#include "common/stream.h"
#include "common/file.h"
#include "common/tokenizer.h"
#include "common/system.h"
-
#include "graphics/surface.h"
-
+#include "video/video_decoder.h"
namespace ZVision {
LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
- : Control(engine, key),
- _frameInfo(0),
- _frameCount(0),
- _startFrame(0),
- _currentFrame(0),
- _lastRenderedFrame(0),
- _mouseIsCaptured(false),
- _isReturning(false),
- _accumulatedTime(0),
- _returnRoutesCurrentFrame(0) {
+ : Control(engine, key, CONTROL_LEVER),
+ _frameInfo(0),
+ _frameCount(0),
+ _startFrame(0),
+ _currentFrame(0),
+ _lastRenderedFrame(0),
+ _mouseIsCaptured(false),
+ _isReturning(false),
+ _accumulatedTime(0),
+ _returnRoutesCurrentFrame(0),
+ _animation(NULL),
+ _cursor(CursorIndex_Active),
+ _mirrored(false) {
// Loop until we find the closing brace
Common::String line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
while (!stream.eos() && !line.contains('}')) {
- if (line.matchString("*descfile*", true)) {
+ if (param.matchString("descfile", true)) {
char levFileName[25];
- sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName);
+ sscanf(values.c_str(), "%24s", levFileName);
parseLevFile(levFileName);
- } else if (line.matchString("*cursor*", true)) {
+ } else if (param.matchString("cursor", true)) {
char cursorName[25];
- sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName);
+ sscanf(values.c_str(), "%24s", cursorName);
- _cursorName = Common::String(cursorName);
+ _cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName));
}
line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
}
renderFrame(_currentFrame);
}
LeverControl::~LeverControl() {
- if (_fileType == AVI) {
- delete _animation.avi;
- } else if (_fileType == RLF) {
- delete _animation.rlf;
- }
-
+ if (_animation)
+ delete _animation;
+
delete[] _frameInfo;
}
void LeverControl::parseLevFile(const Common::String &fileName) {
Common::File file;
- if (!file.open(fileName)) {
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
warning("LEV file %s could could be opened", fileName.c_str());
return;
}
- Common::String line = file.readLine();
+ Common::String line;
+ Common::String param;
+ Common::String values;
while (!file.eos()) {
- if (line.matchString("*animation_id*", true)) {
+ line = file.readLine();
+ getLevParams(line, param, values);
+
+ if (param.matchString("animation_id", true)) {
// Not used
- } else if (line.matchString("*filename*", true)) {
- char fileNameBuffer[25];
- sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer);
-
- Common::String animationFileName(fileNameBuffer);
-
- if (animationFileName.hasSuffix(".avi")) {
- _animation.avi = new ZorkAVIDecoder();
- _animation.avi->loadFile(animationFileName);
- _fileType = AVI;
- } else if (animationFileName.hasSuffix(".rlf")) {
- _animation.rlf = new RlfAnimation(animationFileName, false);
- _fileType = RLF;
- }
- } else if (line.matchString("*skipcolor*", true)) {
+ } else if (param.matchString("filename", true)) {
+ _animation = _engine->loadAnimation(values);
+ } else if (param.matchString("skipcolor", true)) {
// Not used
- } else if (line.matchString("*anim_coords*", true)) {
+ } else if (param.matchString("anim_coords", true)) {
int left, top, right, bottom;
- sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom);
+ sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom);
_animationCoords.left = left;
_animationCoords.top = top;
_animationCoords.right = right;
_animationCoords.bottom = bottom;
- } else if (line.matchString("*mirrored*", true)) {
+ } else if (param.matchString("mirrored", true)) {
uint mirrored;
- sscanf(line.c_str(), "%*[^:]:%u~", &mirrored);
+ sscanf(values.c_str(), "%u", &mirrored);
_mirrored = mirrored == 0 ? false : true;
- } else if (line.matchString("*frames*", true)) {
- sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount);
+ } else if (param.matchString("frames", true)) {
+ sscanf(values.c_str(), "%u", &_frameCount);
_frameInfo = new FrameInfo[_frameCount];
- } else if (line.matchString("*elsewhere*", true)) {
+ } else if (param.matchString("elsewhere", true)) {
// Not used
- } else if (line.matchString("*out_of_control*", true)) {
+ } else if (param.matchString("out_of_control", true)) {
// Not used
- } else if (line.matchString("*start_pos*", true)) {
- sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame);
+ } else if (param.matchString("start_pos", true)) {
+ sscanf(values.c_str(), "%u", &_startFrame);
_currentFrame = _startFrame;
- } else if (line.matchString("*hotspot_deltas*", true)) {
+ } else if (param.matchString("hotspot_deltas", true)) {
uint x;
uint y;
- sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y);
+ sscanf(values.c_str(), "%u %u", &x, &y);
_hotspotDelta.x = x;
_hotspotDelta.y = y;
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
} else {
uint frameNumber;
uint x, y;
+ line.toLowercase();
+
if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) {
_frameInfo[frameNumber].hotspot.left = x;
_frameInfo[frameNumber].hotspot.top = y;
@@ -158,13 +156,13 @@ void LeverControl::parseLevFile(const Common::String &fileName) {
_frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y;
}
- Common::StringTokenizer tokenizer(line, " ^=()");
+ Common::StringTokenizer tokenizer(line, " ^=()~");
tokenizer.nextToken();
tokenizer.nextToken();
Common::String token = tokenizer.nextToken();
while (!tokenizer.empty()) {
- if (token == "D") {
+ if (token == "d") {
token = tokenizer.nextToken();
uint angle;
@@ -172,7 +170,7 @@ void LeverControl::parseLevFile(const Common::String &fileName) {
sscanf(token.c_str(), "%u,%u", &toFrame, &angle);
_frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame));
- } else if (token.hasPrefix("P")) {
+ } else if (token.hasPrefix("p")) {
// Format: P(<from> to <to>)
tokenizer.nextToken();
tokenizer.nextToken();
@@ -186,26 +184,26 @@ void LeverControl::parseLevFile(const Common::String &fileName) {
}
}
- line = file.readLine();
+ // Don't read lines in this place because last will not be parsed.
}
}
-void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
- if (!_enabled) {
- return;
- }
-
+bool LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
+ setVenus();
_mouseIsCaptured = true;
_lastMousePos = backgroundImageSpacePos;
}
+ return false;
}
-void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
- if (!_enabled) {
- return;
- }
-
+bool LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
if (_mouseIsCaptured) {
_mouseIsCaptured = false;
_engine->getScriptManager()->setStateValue(_key, _currentFrame);
@@ -214,20 +212,19 @@ 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) {
- // Make sure the square distance between the last point and the current point is greater than 64
+ // Make sure the square distance between the last point and the current point is greater than 16
// This is a heuristic. This determines how responsive the lever is to mouse movement.
- // TODO: Fiddle with the heuristic to get a good lever responsiveness 'feel'
- if (_lastMousePos.sqrDist(backgroundImageSpacePos) >= 64) {
+ if (_lastMousePos.sqrDist(backgroundImageSpacePos) >= 16) {
int angle = calculateVectorAngle(_lastMousePos, backgroundImageSpacePos);
_lastMousePos = backgroundImageSpacePos;
@@ -235,12 +232,15 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common
if (angle >= (int)iter->angle - ANGLE_DELTA && angle <= (int)iter->angle + ANGLE_DELTA) {
_currentFrame = iter->toFrame;
renderFrame(_currentFrame);
+ _engine->getScriptManager()->setStateValue(_key, _currentFrame);
break;
}
}
}
+ _engine->getCursorManager()->changeCursor(_cursor);
+ cursorWasChanged = true;
} else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
- _engine->getCursorManager()->changeCursor(_cursorName);
+ _engine->getCursorManager()->changeCursor(_cursor);
cursorWasChanged = true;
}
@@ -248,9 +248,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 +275,7 @@ bool LeverControl::process(uint32 deltaTimeInMillis) {
renderFrame(_returnRoutesCurrentFrame);
}
}
-
+
return false;
}
@@ -301,7 +300,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 +349,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 +376,36 @@ void LeverControl::renderFrame(uint frameNumber) {
_lastRenderedFrame = frameNumber;
}
- const uint16 *frameData = 0;
- int x = _animationCoords.left;
- int y = _animationCoords.top;
- int width = 0;
- int height = 0;
-
- if (_fileType == RLF) {
- // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can
- frameData = (const uint16 *)_animation.rlf->getFrameData(frameNumber)->getPixels();
- width = _animation.rlf->width(); // Use the animation width instead of _animationCoords.width()
- height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height()
- } else if (_fileType == AVI) {
- _animation.avi->seekToFrame(frameNumber);
- const Graphics::Surface *surface = _animation.avi->decodeNextFrame();
- frameData = (const uint16 *)surface->getPixels();
- width = surface->w;
- height = surface->h;
- }
+ const Graphics::Surface *frameData;
+
+ _animation->seekToFrame(frameNumber);
+ frameData = _animation->decodeNextFrame();
+ if (frameData)
+ _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _animationCoords);
+}
+
+void LeverControl::getLevParams(const Common::String &inputStr, Common::String &parameter, Common::String &values) {
+ const char *chrs = inputStr.c_str();
+ uint lbr;
+
+ for (lbr = 0; lbr < inputStr.size(); lbr++)
+ if (chrs[lbr] == ':')
+ break;
+
+ if (lbr >= inputStr.size())
+ return;
+
+ uint rbr;
+
+ for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
+ if (chrs[rbr] == '~')
+ break;
+
+ if (rbr >= inputStr.size())
+ return;
- _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height);
+ parameter = Common::String(chrs, chrs + lbr);
+ values = Common::String(chrs + lbr + 1, chrs + rbr);
}
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h
index 49e4fd3806..8787234c51 100644
--- a/engines/zvision/scripting/controls/lever_control.h
+++ b/engines/zvision/scripting/controls/lever_control.h
@@ -28,22 +28,19 @@
#include "common/list.h"
#include "common/rect.h"
+namespace Video {
+ class VideoDecoder;
+}
namespace ZVision {
-class ZorkAVIDecoder;
-class RlfAnimation;
-
+// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
class LeverControl : public Control {
public:
LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
~LeverControl();
private:
- enum FileType {
- RLF = 1,
- AVI = 2
- };
struct Direction {
Direction(uint a, uint t) : angle(a), toFrame(t) {}
@@ -64,13 +61,9 @@ private:
};
private:
- union {
- RlfAnimation *rlf;
- ZorkAVIDecoder *avi;
- } _animation;
- FileType _fileType;
+ Video::VideoDecoder *_animation;
- Common::String _cursorName;
+ int _cursor;
Common::Rect _animationCoords;
bool _mirrored;
uint _frameCount;
@@ -88,8 +81,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);
@@ -120,6 +113,7 @@ private:
*/
static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo);
void renderFrame(uint frameNumber);
+ void getLevParams(const Common::String &inputStr, Common::String &parameter, Common::String &values);
};
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/paint_control.cpp b/engines/zvision/scripting/controls/paint_control.cpp
new file mode 100644
index 0000000000..62dde3d170
--- /dev/null
+++ b/engines/zvision/scripting/controls/paint_control.cpp
@@ -0,0 +1,219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/paint_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+namespace ZVision {
+
+PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_PAINT) {
+
+ _cursor = CursorIndex_Active;
+ _paint = NULL;
+ _bkg = NULL;
+ _brush = NULL;
+ _colorKey = 0;
+ _mouseDown = false;
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("rectangle", true)) {
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height);
+
+ _rectangle = Common::Rect(x, y, width + x, height + y);
+ } else if (param.matchString("cursor", true)) {
+ _cursor = _engine->getCursorManager()->getCursorId(values);
+ } else if (param.matchString("brush_file", true)) {
+ _brush = _engine->getRenderManager()->loadImage(values, false);
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
+ } else if (param.matchString("paint_file", true)) {
+ _paint = _engine->getRenderManager()->loadImage(values, false);
+ } else if (param.matchString("eligible_objects", true)) {
+ char buf[256];
+ memset(buf, 0, 256);
+ strncpy(buf, values.c_str(), 255);
+
+ char *curpos = buf;
+ char *strend = buf + strlen(buf);
+ while (true) {
+ char *st = curpos;
+
+ if (st >= strend)
+ break;
+
+ while (*curpos != ' ' && curpos < strend)
+ curpos++;
+
+ *curpos = 0;
+ curpos++;
+
+ int obj = atoi(st);
+
+ _eligibleObjects.push_back(obj);
+ }
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ if (_paint) {
+ _colorKey = _paint->format.RGBToColor(255, 0, 255);
+ _bkg = new Graphics::Surface;
+ _bkg->create(_rectangle.width(), _rectangle.height(), _paint->format);
+ _bkg->fillRect(Common::Rect(_rectangle.width(), _rectangle.height()), _colorKey);
+
+ Graphics::Surface *tmp = new Graphics::Surface;
+ tmp->create(_rectangle.width(), _rectangle.height(), _paint->format);
+ _engine->getRenderManager()->blitSurfaceToSurface(*_paint, _rectangle, *tmp, 0, 0);
+ _paint->free();
+ delete _paint;
+ _paint = tmp;
+ }
+}
+
+PaintControl::~PaintControl() {
+ // Clear the state value back to 0
+ //_engine->getScriptManager()->setStateValue(_key, 0);
+ if (_paint) {
+ _paint->free();
+ delete _paint;
+ }
+ if (_brush) {
+ _brush->free();
+ delete _brush;
+ }
+ if (_bkg) {
+ _bkg->free();
+ delete _bkg;
+ }
+}
+
+bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ _mouseDown = false;
+
+ return false;
+}
+
+bool PaintControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_rectangle.contains(backgroundImageSpacePos)) {
+ int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
+
+ if (eligeblity(mouseItem)) {
+ setVenus();
+ _mouseDown = true;
+ }
+ }
+
+ return false;
+}
+
+bool PaintControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_rectangle.contains(backgroundImageSpacePos)) {
+ int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
+
+ if (eligeblity(mouseItem)) {
+ _engine->getCursorManager()->changeCursor(_cursor);
+
+ if (_mouseDown) {
+ Common::Rect bkgRect = paint(backgroundImageSpacePos);
+ if (!bkgRect.isEmpty()) {
+ Common::Rect imgRect = bkgRect;
+ imgRect.translate(-_rectangle.left, -_rectangle.top);
+
+ Graphics::Surface imgUpdate = _bkg->getSubArea(imgRect);
+
+ _engine->getRenderManager()->blitSurfaceToBkg(imgUpdate, bkgRect.left, bkgRect.top, _colorKey);
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PaintControl::eligeblity(int itemId) {
+ for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
+ if (*it == itemId)
+ return true;
+ return false;
+}
+
+Common::Rect PaintControl::paint(const Common::Point &point) {
+ Common::Rect paintRect = Common::Rect(_brush->w, _brush->h);
+ paintRect.moveTo(point);
+ paintRect.clip(_rectangle);
+
+ if (!paintRect.isEmpty()) {
+ Common::Rect brushRect = paintRect;
+ brushRect.translate(-point.x, -point.y);
+
+ Common::Rect bkgRect = paintRect;
+ bkgRect.translate(-_rectangle.left, -_rectangle.top);
+
+ for (int yy = 0; yy < brushRect.height(); yy++) {
+ uint16 *mask = (uint16 *)_brush->getBasePtr(brushRect.left, brushRect.top + yy);
+ uint16 *from = (uint16 *)_paint->getBasePtr(bkgRect.left, bkgRect.top + yy);
+ uint16 *to = (uint16 *)_bkg->getBasePtr(bkgRect.left, bkgRect.top + yy);
+ for (int xx = 0; xx < brushRect.width(); xx++) {
+ if (*mask != 0)
+ *(to + xx) = *(from + xx);
+
+ mask++;
+ }
+ }
+
+ }
+ return paintRect;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/paint_control.h b/engines/zvision/scripting/controls/paint_control.h
new file mode 100644
index 0000000000..8c01f0e68a
--- /dev/null
+++ b/engines/zvision/scripting/controls/paint_control.h
@@ -0,0 +1,92 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_PAINT_CONTROL_H
+#define ZVISION_PAINT_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "graphics/surface.h"
+
+#include "common/rect.h"
+#include "common/list.h"
+
+namespace ZVision {
+
+// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
+class PaintControl : public Control {
+public:
+ PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~PaintControl();
+
+ /**
+ * @param screenSpacePos The position of the mouse in screen space
+ * @param backgroundImageSpacePos The position of the mouse in background image space
+ */
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+
+ /**
+ * @param screenSpacePos The position of the mouse in screen space
+ * @param backgroundImageSpacePos The position of the mouse in background image space
+ */
+ bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ /**
+ * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
+ *
+ * @param engine The base engine
+ * @param screenSpacePos The position of the mouse in screen space
+ * @param backgroundImageSpacePos The position of the mouse in background image space
+ * @return Was the cursor changed?
+ */
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+
+ bool process(uint32 deltaTimeInMillis) {
+ return false;
+ };
+
+private:
+ /**
+ * The area that will trigger the event
+ * This is in image space coordinates, NOT screen space
+ */
+
+ uint32 _colorKey;
+
+ Graphics::Surface *_paint;
+ Graphics::Surface *_bkg;
+ Graphics::Surface *_brush;
+
+ Common::List<int> _eligibleObjects;
+
+ int _cursor;
+ Common::Rect _rectangle;
+
+ bool _mouseDown;
+
+ bool eligeblity(int itemId);
+ Common::Rect paint(const Common::Point &point);
+
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp
index 82736b7576..f51a28d644 100644
--- a/engines/zvision/scripting/controls/push_toggle_control.cpp
+++ b/engines/zvision/scripting/controls/push_toggle_control.cpp
@@ -26,73 +26,122 @@
#include "zvision/zvision.h"
#include "zvision/scripting/script_manager.h"
-#include "zvision/cursors/cursor_manager.h"
-#include "zvision/utility/utility.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
#include "common/stream.h"
-
namespace ZVision {
PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
- : Control(engine, key) {
+ : Control(engine, key, CONTROL_PUSHTGL),
+ _countTo(2),
+ _cursor(CursorIndex_Active),
+ _event(Common::EVENT_LBUTTONUP) {
+
+ _hotspots.clear();
+
// Loop until we find the closing brace
Common::String line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
while (!stream.eos() && !line.contains('}')) {
- if (line.matchString("*_hotspot*", true)) {
+ if (param.matchString("*_hotspot", true)) {
uint x;
uint y;
uint width;
uint height;
- sscanf(line.c_str(), "%*[^(](%u,%u,%u,%u)", &x, &y, &width, &height);
-
- _hotspot = Common::Rect(x, y, x + width, y + height);
- } else if (line.matchString("cursor*", true)) {
- char nameBuffer[25];
-
- sscanf(line.c_str(), "%*[^(](%25[^)])", nameBuffer);
-
- _hoverCursor = Common::String(nameBuffer);
+ sscanf(values.c_str(), "%u,%u,%u,%u", &x, &y, &width, &height);
+
+ _hotspots.push_back(Common::Rect(x, y, x + width + 1, y + height + 1));
+ } else if (param.matchString("cursor", true)) {
+ _cursor = _engine->getCursorManager()->getCursorId(values);
+ } else if (param.matchString("animation", true)) {
+ // Not used
+ } else if (param.matchString("sound", true)) {
+ // Not used
+ } else if (param.matchString("count_to", true)) {
+ sscanf(values.c_str(), "%u", &_countTo);
+ } else if (param.matchString("mouse_event", true)) {
+ if (values.equalsIgnoreCase("up")) {
+ _event = Common::EVENT_LBUTTONUP;
+ } else if (values.equalsIgnoreCase("down")) {
+ _event = Common::EVENT_LBUTTONDOWN;
+ } else if (values.equalsIgnoreCase("double")) {
+ // Not used
+ }
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
}
line = stream.readLine();
- trimCommentsAndWhiteSpace(&line);
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
}
- if (_hotspot.isEmpty() || _hoverCursor.empty()) {
- warning("Push_toggle cursor %u was parsed incorrectly", key);
+ if (_hotspots.size() == 0) {
+ warning("Push_toggle %u was parsed incorrectly", key);
}
}
PushToggleControl::~PushToggleControl() {
- // Clear the state value back to 0
- _engine->getScriptManager()->setStateValue(_key, 0);
+ _hotspots.clear();
}
-void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
- if (!_enabled) {
- return;
+bool PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_event != Common::EVENT_LBUTTONUP)
+ return false;
+
+ if (contain(backgroundImageSpacePos)) {
+ setVenus();
+ int32 val = _engine->getScriptManager()->getStateValue(_key);
+ val = (val + 1) % _countTo;
+ _engine->getScriptManager()->setStateValue(_key, val);
+ return true;
}
-
- if (_hotspot.contains(backgroundImageSpacePos)) {
- _engine->getScriptManager()->setStateValue(_key, 1);
+ 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 (contain(backgroundImageSpacePos)) {
+ setVenus();
+ int32 val = _engine->getScriptManager()->getStateValue(_key);
+ val = (val + 1) % _countTo;
+ _engine->getScriptManager()->setStateValue(_key, val);
+ return true;
}
+ return false;
}
bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
- if (!_enabled) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
return false;
- }
-
- if (_hotspot.contains(backgroundImageSpacePos)) {
- _engine->getCursorManager()->changeCursor(_hoverCursor);
+
+ if (contain(backgroundImageSpacePos)) {
+ _engine->getCursorManager()->changeCursor(_cursor);
return true;
}
return false;
}
+bool PushToggleControl::contain(const Common::Point &point) {
+ for (uint i = 0; i < _hotspots.size(); i++)
+ if (_hotspots[i].contains(point))
+ return true;
+ return false;
+}
+
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/push_toggle_control.h b/engines/zvision/scripting/controls/push_toggle_control.h
index 3854fc2005..9444c77cb6 100644
--- a/engines/zvision/scripting/controls/push_toggle_control.h
+++ b/engines/zvision/scripting/controls/push_toggle_control.h
@@ -26,7 +26,8 @@
#include "zvision/scripting/control.h"
#include "common/rect.h"
-
+#include "common/events.h"
+#include "common/array.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.
*
@@ -57,9 +65,15 @@ private:
* The area that will trigger the event
* This is in image space coordinates, NOT screen space
*/
- Common::Rect _hotspot;
+ Common::Array<Common::Rect> _hotspots;
/** The cursor to use when hovering over _hotspot */
- Common::String _hoverCursor;
+ int _cursor;
+ /** Button maximal values count */
+ uint _countTo;
+
+ Common::EventType _event;
+
+ bool contain(const Common::Point &point);
};
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/safe_control.cpp b/engines/zvision/scripting/controls/safe_control.cpp
new file mode 100644
index 0000000000..4d2a91a1ad
--- /dev/null
+++ b/engines/zvision/scripting/controls/safe_control.cpp
@@ -0,0 +1,180 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/safe_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "common/tokenizer.h"
+#include "common/system.h"
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+namespace ZVision {
+
+SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_SAFE) {
+ _statesCount = 0;
+ _curState = 0;
+ _animation = NULL;
+ _innerRaduis = 0;
+ _innerRadiusSqr = 0;
+ _outerRadius = 0;
+ _outerRadiusSqr = 0;
+ _zeroPointer = 0;
+ _startPointer = 0;
+ _targetFrame = 0;
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("animation", true)) {
+ _animation = _engine->loadAnimation(values);
+ _animation->start();
+ } else if (param.matchString("rectangle", true)) {
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height);
+
+ _rectangle = Common::Rect(x, y, width, height);
+ } else if (param.matchString("num_states", true)) {
+ _statesCount = atoi(values.c_str());
+ } else if (param.matchString("center", true)) {
+ int x;
+ int y;
+
+ sscanf(values.c_str(), "%d %d", &x, &y);
+ _center = Common::Point(x, y);
+ } else if (param.matchString("dial_inner_radius", true)) {
+ _innerRaduis = atoi(values.c_str());
+ _innerRadiusSqr = _innerRaduis * _innerRaduis;
+ } else if (param.matchString("radius", true)) {
+ _outerRadius = atoi(values.c_str());
+ _outerRadiusSqr = _outerRadius * _outerRadius;
+ } else if (param.matchString("zero_radians_offset", true)) {
+ _zeroPointer = atoi(values.c_str());
+ } else if (param.matchString("pointer_offset", true)) {
+ _startPointer = atoi(values.c_str());
+ _curState = _startPointer;
+ } else if (param.matchString("cursor", true)) {
+ // Not used
+ } else if (param.matchString("mirrored", true)) {
+ // Not used
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ if (_animation)
+ _animation->seekToFrame(_curState);
+}
+
+SafeControl::~SafeControl() {
+ if (_animation)
+ delete _animation;
+
+}
+
+bool SafeControl::process(uint32 deltaTimeInMillis) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) {
+ // If we're past the target frame, move back one
+ if (_animation->getCurFrame() > _targetFrame)
+ _animation->seekToFrame(_animation->getCurFrame() - 1);
+
+ const Graphics::Surface *frameData = _animation->decodeNextFrame();
+ if (_animation->getCurFrame() == _targetFrame)
+ _engine->getScriptManager()->setStateValue(_key, _curState);
+ if (frameData)
+ _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top);
+ }
+
+ return false;
+}
+
+bool SafeControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_rectangle.contains(backgroundImageSpacePos)) {
+ int32 mR = backgroundImageSpacePos.sqrDist(_center);
+ if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
+ _engine->getCursorManager()->changeCursor(CursorIndex_Active);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_rectangle.contains(backgroundImageSpacePos)) {
+ int32 mR = backgroundImageSpacePos.sqrDist(_center);
+ if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
+ setVenus();
+
+ Common::Point tmp = backgroundImageSpacePos - _center;
+
+ float dd = atan2((float)tmp.x, (float)tmp.y) * 57.29578;
+
+ int16 dp_state = 360 / _statesCount;
+
+ int16 m_state = (_statesCount - ((((int16)dd + 540) % 360) / dp_state)) % _statesCount;
+
+ int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount;
+
+ if (_animation)
+ _animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount);
+
+ _curState = (_statesCount * 2 + tmp2) % _statesCount;
+
+ _targetFrame = (_curState + _statesCount - _startPointer) % _statesCount;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/safe_control.h b/engines/zvision/scripting/controls/safe_control.h
new file mode 100644
index 0000000000..3e8c17635c
--- /dev/null
+++ b/engines/zvision/scripting/controls/safe_control.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_SAFE_CONTROL_H
+#define ZVISION_SAFE_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "common/list.h"
+#include "common/rect.h"
+
+namespace Video {
+ class VideoDecoder;
+}
+
+namespace ZVision {
+
+// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
+class SafeControl : public Control {
+public:
+ SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~SafeControl();
+
+private:
+ int16 _statesCount;
+ int16 _curState;
+ Video::VideoDecoder *_animation;
+ Common::Point _center;
+ Common::Rect _rectangle;
+ int16 _innerRaduis;
+ int32 _innerRadiusSqr;
+ int16 _outerRadius;
+ int32 _outerRadiusSqr;
+ int16 _zeroPointer;
+ int16 _startPointer;
+ int16 _targetFrame;
+
+public:
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ bool process(uint32 deltaTimeInMillis);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp
new file mode 100644
index 0000000000..2ac77c4776
--- /dev/null
+++ b/engines/zvision/scripting/controls/save_control.cpp
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/input_control.h"
+#include "zvision/scripting/controls/save_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/text/string_manager.h"
+
+#include "zvision/file/save_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace ZVision {
+
+SaveControl::SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_SAVE),
+ _saveControl(false) {
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("savebox", true)) {
+ int saveId;
+ int inputId;
+
+ sscanf(values.c_str(), "%d %d", &saveId, &inputId);
+ saveElement elmnt;
+ elmnt.inputKey = inputId;
+ elmnt.saveId = saveId;
+ elmnt.exist = false;
+ _inputs.push_back(elmnt);
+ } else if (param.matchString("control_type", true)) {
+ if (values.contains("save"))
+ _saveControl = true;
+ else
+ _saveControl = false;
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
+ Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
+ if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
+ InputControl *inp = (InputControl *)ctrl;
+ inp->setReadOnly(!_saveControl);
+ Common::SeekableReadStream *save = _engine->getSaveManager()->getSlotFile(iter->saveId);
+ if (save) {
+ SaveGameHeader header;
+ if (_engine->getSaveManager()->readSaveGameHeader(save, header)) {
+ inp->setText(header.saveName);
+ iter->exist = true;
+ }
+ delete save;
+ }
+ }
+ }
+}
+
+bool SaveControl::process(uint32 deltaTimeInMillis) {
+ for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
+ Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
+ if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
+ InputControl *inp = (InputControl *)ctrl;
+ if (inp->enterPress()) {
+ if (_saveControl) {
+ if (inp->getText().size() > 0) {
+ bool toSave = true;
+ if (iter->exist)
+ if (!_engine->getRenderManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST)))
+ toSave = false;
+
+ if (toSave) {
+ _engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true);
+ _engine->getRenderManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000);
+ _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation());
+ }
+ } else {
+ _engine->getRenderManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000);
+ }
+ } else {
+ _engine->getSaveManager()->loadGame(iter->saveId);
+ return true;
+ }
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/core/menu.h b/engines/zvision/scripting/controls/save_control.h
index 3ab6d4c2ec..cdc50eb54d 100644
--- a/engines/zvision/core/menu.h
+++ b/engines/zvision/scripting/controls/save_control.h
@@ -20,9 +20,36 @@
*
*/
-#ifndef ZVISION_MENU_H
-#define ZVISION_MENU_H
+#ifndef ZVISION_SAVE_CONTROL_H
+#define ZVISION_SAVE_CONTROL_H
-// TODO: Implement MenuHandler
+#include "zvision/scripting/control.h"
+
+#include "common/list.h"
+
+namespace ZVision {
+
+class SaveControl : public Control {
+public:
+ SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+
+private:
+ struct saveElement {
+ int saveId;
+ int inputKey;
+ bool exist;
+ };
+ typedef Common::List<saveElement> saveElmntList;
+ saveElmntList _inputs;
+
+ bool _saveControl;
+
+public:
+
+ bool process(uint32 deltaTimeInMillis);
+
+};
+
+} // End of namespace ZVision
#endif
diff --git a/engines/zvision/scripting/controls/slot_control.cpp b/engines/zvision/scripting/controls/slot_control.cpp
new file mode 100644
index 0000000000..42b54a9ab5
--- /dev/null
+++ b/engines/zvision/scripting/controls/slot_control.cpp
@@ -0,0 +1,219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/slot_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+#include "common/stream.h"
+
+namespace ZVision {
+
+SlotControl::SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_SLOT),
+ _cursor(CursorIndex_Active),
+ _distanceId('0') {
+
+ _renderedItem = 0;
+ _bkg = NULL;
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("hotspot", true)) {
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height);
+
+ _hotspot = Common::Rect(x, y, width, height);
+ } else if (param.matchString("rectangle", true)) {
+ int x;
+ int y;
+ int width;
+ int height;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height);
+
+ _rectangle = Common::Rect(x, y, width, height);
+ } else if (param.matchString("cursor", true)) {
+ _cursor = _engine->getCursorManager()->getCursorId(values);
+ } else if (param.matchString("distance_id", true)) {
+ sscanf(values.c_str(), "%c", &_distanceId);
+ } else if (param.matchString("venus_id", true)) {
+ _venusId = atoi(values.c_str());
+ } else if (param.matchString("eligible_objects", true)) {
+ char buf[256];
+ memset(buf, 0, 256);
+ strncpy(buf, values.c_str(), 255);
+
+ char *curpos = buf;
+ char *strend = buf + strlen(buf);
+ while (true) {
+ char *st = curpos;
+
+ if (st >= strend)
+ break;
+
+ while (*curpos != ' ' && curpos < strend)
+ curpos++;
+
+ *curpos = 0;
+ curpos++;
+
+ int obj = atoi(st);
+
+ _eligibleObjects.push_back(obj);
+ }
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ if (_hotspot.isEmpty() || _rectangle.isEmpty()) {
+ warning("Slot %u was parsed incorrectly", key);
+ }
+}
+
+SlotControl::~SlotControl() {
+ // Clear the state value back to 0
+ //_engine->getScriptManager()->setStateValue(_key, 0);
+
+ if (_bkg)
+ delete _bkg;
+}
+
+bool SlotControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_hotspot.contains(backgroundImageSpacePos)) {
+ setVenus();
+
+ int item = _engine->getScriptManager()->getStateValue(_key);
+ int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
+ if (item != 0) {
+ if (mouseItem != 0) {
+ if (eligeblity(mouseItem)) {
+ _engine->getScriptManager()->inventoryDrop(mouseItem);
+ _engine->getScriptManager()->inventoryAdd(item);
+ _engine->getScriptManager()->setStateValue(_key, mouseItem);
+ }
+ } else {
+ _engine->getScriptManager()->inventoryAdd(item);
+ _engine->getScriptManager()->setStateValue(_key, 0);
+ }
+ } else if (mouseItem == 0) {
+ if (eligeblity(0)) {
+ _engine->getScriptManager()->inventoryDrop(0);
+ _engine->getScriptManager()->setStateValue(_key, 0);
+ }
+ } else if (eligeblity(mouseItem)) {
+ _engine->getScriptManager()->setStateValue(_key, mouseItem);
+ _engine->getScriptManager()->inventoryDrop(mouseItem);
+ }
+ }
+ return false;
+}
+
+bool SlotControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_hotspot.contains(backgroundImageSpacePos)) {
+ _engine->getCursorManager()->changeCursor(_cursor);
+ return true;
+ }
+
+ return false;
+}
+
+bool SlotControl::process(uint32 deltaTimeInMillis) {
+ if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
+ return false;
+
+ if (_engine->canRender()) {
+ int curItem = _engine->getScriptManager()->getStateValue(_key);
+ if (curItem != _renderedItem) {
+ if (_renderedItem != 0 && curItem == 0) {
+ _engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
+ _renderedItem = curItem;
+ } else {
+ if (_renderedItem == 0) {
+ if (_bkg)
+ delete _bkg;
+
+ _bkg = _engine->getRenderManager()->getBkgRect(_rectangle);
+ } else {
+ _engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
+ }
+
+ char buf[16];
+ if (_engine->getGameId() == GID_NEMESIS)
+ sprintf(buf, "%d%cobj.tga", curItem, _distanceId);
+ else
+ sprintf(buf, "g0z%cu%2.2x1.tga", _distanceId, curItem);
+
+ Graphics::Surface *srf = _engine->getRenderManager()->loadImage(buf);
+
+ int16 drawx = _rectangle.left;
+ int16 drawy = _rectangle.top;
+
+ if (_rectangle.width() > srf->w)
+ drawx = _rectangle.left + (_rectangle.width() - srf->w) / 2;
+
+ if (_rectangle.height() > srf->h)
+ drawy = _rectangle.top + (_rectangle.height() - srf->h) / 2;
+
+ _engine->getRenderManager()->blitSurfaceToBkg(*srf, drawx, drawy, 0);
+
+ delete srf;
+
+ _renderedItem = curItem;
+ }
+ }
+ }
+ return false;
+}
+
+bool SlotControl::eligeblity(int itemId) {
+ for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
+ if (*it == itemId)
+ return true;
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/slot_control.h b/engines/zvision/scripting/controls/slot_control.h
new file mode 100644
index 0000000000..e776d99311
--- /dev/null
+++ b/engines/zvision/scripting/controls/slot_control.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_SLOT_CONTROL_H
+#define ZVISION_SLOT_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "graphics/surface.h"
+
+#include "common/rect.h"
+#include "common/list.h"
+
+namespace ZVision {
+
+class SlotControl : public Control {
+public:
+ SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~SlotControl();
+
+ /**
+ * Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1);
+ *
+ * @param screenSpacePos The position of the mouse in screen space
+ * @param backgroundImageSpacePos The position of the mouse in background image space
+ */
+ bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ /**
+ * Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
+ *
+ * @param engine The base engine
+ * @param screenSpacePos The position of the mouse in screen space
+ * @param backgroundImageSpacePos The position of the mouse in background image space
+ * @return Was the cursor changed?
+ */
+ bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ /**
+ * The area that will trigger the event
+ * This is in image space coordinates, NOT screen space
+ */
+ Common::Rect _rectangle;
+ Common::Rect _hotspot;
+
+ int _cursor;
+ char _distanceId;
+
+ int _renderedItem;
+
+ Common::List<int> _eligibleObjects;
+
+ bool eligeblity(int itemId);
+
+ Graphics::Surface *_bkg;
+
+ /** The cursor to use when hovering over _hotspot */
+ Common::String _hoverCursor;
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/titler_control.cpp b/engines/zvision/scripting/controls/titler_control.cpp
new file mode 100644
index 0000000000..683d6660af
--- /dev/null
+++ b/engines/zvision/scripting/controls/titler_control.cpp
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/titler_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/text/text.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+#include "common/stream.h"
+
+namespace ZVision {
+
+TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key, CONTROL_TITLER) {
+
+ _surface = NULL;
+ _curString = -1;
+
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ Common::String param;
+ Common::String values;
+ getParams(line, param, values);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (param.matchString("string_resource_file", true)) {
+ readStringsFile(values);
+ } else if (param.matchString("rectangle", true)) {
+ int x;
+ int y;
+ int x2;
+ int y2;
+
+ sscanf(values.c_str(), "%d %d %d %d", &x, &y, &x2, &y2);
+
+ _rectangle = Common::Rect(x, y, x2, y2);
+ }
+
+ line = stream.readLine();
+ _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
+ getParams(line, param, values);
+ }
+
+ if (!_rectangle.isEmpty()) {
+ _surface = new Graphics::Surface;
+ _surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat);
+ _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
+ }
+}
+
+TitlerControl::~TitlerControl() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+}
+
+void TitlerControl::setString(int strLine) {
+ if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) {
+ _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
+ _engine->getTextRenderer()->drawTextWithWordWrapping(_strings[strLine], *_surface);
+ _engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top);
+ _curString = strLine;
+ }
+}
+
+void TitlerControl::readStringsFile(const Common::String &fileName) {
+ Common::File file;
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
+ warning("String_resource_file %s could could be opened", fileName.c_str());
+ return;
+ }
+
+ _strings.clear();
+
+ while (!file.eos()) {
+
+ Common::String line = readWideLine(file);
+ _strings.push_back(line);
+ }
+ file.close();
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/titler_control.h b/engines/zvision/scripting/controls/titler_control.h
new file mode 100644
index 0000000000..dd96e4a846
--- /dev/null
+++ b/engines/zvision/scripting/controls/titler_control.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_TITLER_CONTROL_H
+#define ZVISION_TITLER_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "graphics/surface.h"
+
+#include "common/rect.h"
+#include "common/array.h"
+
+namespace ZVision {
+
+// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons
+class TitlerControl : public Control {
+public:
+ TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~TitlerControl();
+
+ void setString(int strLine);
+
+private:
+
+ Common::Array< Common::String > _strings;
+ Common::Rect _rectangle;
+ int16 _curString;
+ Graphics::Surface *_surface;
+
+ void readStringsFile(const Common::String &fileName);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/effects/animation_effect.cpp b/engines/zvision/scripting/effects/animation_effect.cpp
new file mode 100644
index 0000000000..511a0db353
--- /dev/null
+++ b/engines/zvision/scripting/effects/animation_effect.cpp
@@ -0,0 +1,217 @@
+/* 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/effects/animation_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/scripting/script_manager.h"
+
+#include "graphics/surface.h"
+#include "video/video_decoder.h"
+
+namespace ZVision {
+
+AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse)
+ : ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM),
+ _disposeAfterUse(disposeAfterUse),
+ _mask(mask),
+ _animation(NULL) {
+
+ _animation = engine->loadAnimation(fileName);
+
+ if (frate > 0) {
+ _frmDelayOverride = (int32)(1000.0 / frate);
+
+ // WORKAROUND: We do not allow the engine to delay more than 66 msec
+ // per frame (15fps max)
+ if (_frmDelayOverride > 66)
+ _frmDelayOverride = 66;
+ } else {
+ _frmDelayOverride = 0;
+ }
+}
+
+AnimationEffect::~AnimationEffect() {
+ 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) {
+ (*it)._scaled->free();
+ delete(*it)._scaled;
+ }
+ }
+
+ _playList.clear();
+}
+
+bool AnimationEffect::process(uint32 deltaTimeInMillis) {
+ ScriptManager *scriptManager = _engine->getScriptManager();
+ RenderManager *renderManager = _engine->getRenderManager();
+ RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState();
+ bool isPanorama = (renderState == RenderTable::PANORAMA);
+ int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
+
+ // Do not update animation nodes in panoramic mode while turning, if the user
+ // has set this option
+ if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity)
+ return false;
+
+ PlayNodes::iterator it = _playList.begin();
+ if (it != _playList.end()) {
+ playnode *nod = &(*it);
+
+ if (nod->_curFrame == -1) {
+ // The node is just beginning playback
+ nod->_curFrame = nod->start;
+
+ _animation->start();
+ _animation->seekToFrame(nod->start);
+ _animation->setEndFrame(nod->stop);
+
+ nod->_delay = deltaTimeInMillis; // Force the frame to draw
+ if (nod->slot)
+ scriptManager->setStateValue(nod->slot, 1);
+ } else if (_animation->endOfVideo()) {
+ // The node has reached the end; check if we need to loop
+ nod->loop--;
+
+ if (nod->loop == 0) {
+ if (nod->slot >= 0)
+ scriptManager->setStateValue(nod->slot, 2);
+ if (nod->_scaled) {
+ nod->_scaled->free();
+ delete nod->_scaled;
+ }
+ _playList.erase(it);
+ return _disposeAfterUse;
+ }
+
+ nod->_curFrame = nod->start;
+ _animation->seekToFrame(nod->start);
+ }
+
+ // Check if we need to draw a frame
+ bool needsUpdate = false;
+ if (_frmDelayOverride == 0) {
+ // If not overridden, use the VideoDecoder's check
+ needsUpdate = _animation->needsUpdate();
+ } else {
+ // Otherwise, implement our own timing
+ nod->_delay -= deltaTimeInMillis;
+
+ if (nod->_delay <= 0) {
+ nod->_delay += _frmDelayOverride;
+ needsUpdate = true;
+ }
+ }
+
+ if (needsUpdate) {
+ const Graphics::Surface *frame = _animation->decodeNextFrame();
+
+ if (frame) {
+ uint32 dstw;
+ uint32 dsth;
+ if (isPanorama) {
+ dstw = nod->pos.height();
+ dsth = nod->pos.width();
+ } else {
+ dstw = nod->pos.width();
+ dsth = nod->pos.height();
+ }
+
+ // We only scale down the animation to fit its frame, not up, otherwise we
+ // end up with distorted animations - e.g. the armor visor in location cz1e
+ // in Nemesis (one of the armors inside Irondune), or the planet in location
+ // aa10 in Nemesis (Juperon, outside the asylum). We do allow scaling up only
+ // when a simple 2x filter is requested (e.g. the alchemists and cup sequence
+ // in Nemesis)
+ if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) {
+ if (nod->_scaled)
+ if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) {
+ nod->_scaled->free();
+ delete nod->_scaled;
+ nod->_scaled = NULL;
+ }
+
+ if (!nod->_scaled) {
+ nod->_scaled = new Graphics::Surface;
+ nod->_scaled->create(dstw, dsth, frame->format);
+ }
+
+ renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth);
+ frame = nod->_scaled;
+ }
+
+ if (isPanorama) {
+ Graphics::Surface *transposed = RenderManager::tranposeSurface(frame);
+ renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask);
+ transposed->free();
+ delete transposed;
+ } else {
+ renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) {
+ playnode nod;
+ nod.loop = loops;
+ nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1);
+ nod.start = startFrame;
+ nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1);
+ nod.slot = slot;
+ nod._curFrame = -1;
+ nod._delay = 0;
+ nod._scaled = NULL;
+ _playList.push_back(nod);
+}
+
+bool AnimationEffect::stop() {
+ PlayNodes::iterator it = _playList.begin();
+ if (it != _playList.end()) {
+ _engine->getScriptManager()->setStateValue((*it).slot, 2);
+ if ((*it)._scaled) {
+ (*it)._scaled->free();
+ delete(*it)._scaled;
+ }
+ }
+
+ _playList.clear();
+
+ // We don't need to delete, it's may be reused
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/animation_control.h b/engines/zvision/scripting/effects/animation_effect.h
index 6c4d6dfcf7..fd6e24ab8b 100644
--- a/engines/zvision/scripting/controls/animation_control.h
+++ b/engines/zvision/scripting/effects/animation_effect.h
@@ -8,78 +8,70 @@
* 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_ANIMATION_NODE_H
+#define ZVISION_ANIMATION_NODE_H
-#include "zvision/scripting/control.h"
+#include "zvision/scripting/scripting_effect.h"
+#include "common/rect.h"
+#include "common/list.h"
-
-namespace Common {
-class String;
+namespace Graphics {
+struct Surface;
}
namespace Video {
-class VideoDecoder;
-}
-
-namespace Graphics {
-struct Surface;
+ class VideoDecoder;
}
namespace ZVision {
class ZVision;
-class RlfAnimation;
-class AnimationControl : public Control {
+class AnimationEffect : public ScriptingEffect {
public:
- AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName);
- ~AnimationControl();
-
-private:
- enum FileType {
- RLF = 1,
- AVI = 2
+ AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse = true);
+ ~AnimationEffect();
+
+ struct playnode {
+ Common::Rect pos;
+ int32 slot;
+ int32 start;
+ int32 stop;
+ int32 loop;
+ int32 _curFrame;
+ int32 _delay;
+ Graphics::Surface *_scaled;
};
private:
- uint32 _animationKey;
-
- union {
- RlfAnimation *rlf;
- Video::VideoDecoder *avi;
- } _animation;
+ typedef Common::List<playnode> PlayNodes;
- FileType _fileType;
- uint _loopCount;
- int32 _x;
- int32 _y;
+ PlayNodes _playList;
- uint _accumulatedTime;
- uint _currentLoop;
+ int32 _mask;
+ bool _disposeAfterUse;
- Graphics::Surface *_cachedFrame;
- bool _cachedFrameNeedsDeletion;
+ Video::VideoDecoder *_animation;
+ int32 _frmDelayOverride;
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; }
+ void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1);
+
+ bool stop();
};
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/distort_effect.cpp b/engines/zvision/scripting/effects/distort_effect.cpp
new file mode 100644
index 0000000000..113b5d048d
--- /dev/null
+++ b/engines/zvision/scripting/effects/distort_effect.cpp
@@ -0,0 +1,104 @@
+/* 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/effects/distort_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/graphics/render_table.h"
+
+#include "common/stream.h"
+
+namespace ZVision {
+
+DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale)
+ : ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) {
+
+ _angle = _engine->getRenderManager()->getRenderTable()->getAngle();
+ _linScale = _engine->getRenderManager()->getRenderTable()->getLinscale();
+
+ _speed = speed;
+ _incr = true;
+ _startAngle = startAngle;
+ _endAngle = endAngle;
+ _startLineScale = startLineScale;
+ _endLineScale = endLineScale;
+
+ _curFrame = 1.0;
+
+ _diffAngle = endAngle - startAngle;
+ _diffLinScale = endLineScale - startLineScale;
+
+ _frmSpeed = (float)speed / 15.0;
+ _frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed);
+ if (_frames <= 0)
+ _frames = 1;
+
+ if (_key != StateKey_NotSet)
+ _engine->getScriptManager()->setStateValue(_key, 1);
+}
+
+DistortNode::~DistortNode() {
+ setParams(_angle, _linScale);
+}
+
+bool DistortNode::process(uint32 deltaTimeInMillis) {
+ float updTime = deltaTimeInMillis / (1000.0 / 60.0);
+
+ if (_incr)
+ _curFrame += updTime;
+ else
+ _curFrame -= updTime;
+
+ if (_curFrame < 1.0) {
+ _curFrame = 1.0;
+ _incr = true;
+ } else if (_curFrame > _frames) {
+ _curFrame = _frames;
+ _incr = false;
+ }
+
+ float diff = (1.0 / (5.0 - (_curFrame * _frmSpeed))) / (5.0 - _frmSpeed);
+ setParams(_startAngle + diff * _diffAngle, _startLineScale + diff * _diffLinScale);
+
+ return false;
+}
+
+void DistortNode::setParams(float angl, float linScale) {
+ RenderTable *table = _engine->getRenderManager()->getRenderTable();
+ if (table->getRenderState() == RenderTable::PANORAMA) {
+ table->setPanoramaFoV(angl);
+ table->setPanoramaScale(linScale);
+ table->generateRenderTable();
+ _engine->getRenderManager()->markDirty();
+ } else if (table->getRenderState() == RenderTable::TILT) {
+ table->setTiltFoV(angl);
+ table->setTiltScale(linScale);
+ table->generateRenderTable();
+ _engine->getRenderManager()->markDirty();
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/distort_effect.h b/engines/zvision/scripting/effects/distort_effect.h
new file mode 100644
index 0000000000..c64f10e6ff
--- /dev/null
+++ b/engines/zvision/scripting/effects/distort_effect.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_DISTORT_NODE_H
+#define ZVISION_DISTORT_NODE_H
+
+#include "zvision/scripting/scripting_effect.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class DistortNode : public ScriptingEffect {
+public:
+ DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale);
+ ~DistortNode();
+
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ int16 _speed;
+ float _startAngle;
+ float _endAngle;
+ float _startLineScale;
+ float _endLineScale;
+
+ float _frmSpeed;
+ float _diffAngle;
+ float _diffLinScale;
+ bool _incr;
+ int16 _frames;
+
+ float _curFrame;
+
+ float _angle;
+ float _linScale;
+
+private:
+ void setParams(float angl, float linScale);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/effects/music_effect.cpp b/engines/zvision/scripting/effects/music_effect.cpp
new file mode 100644
index 0000000000..e3fdc96dba
--- /dev/null
+++ b/engines/zvision/scripting/effects/music_effect.cpp
@@ -0,0 +1,296 @@
+/* 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/effects/music_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/sound/midi.h"
+#include "zvision/sound/zork_raw.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "audio/decoders/wave.h"
+
+namespace ZVision {
+
+static const uint8 dbMapLinear[256] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
+2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
+8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14,
+14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 24, 25,
+26, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 40, 41, 43, 45,
+46, 48, 50, 52, 53, 55, 57, 60, 62, 64, 67, 69, 72, 74, 77, 80,
+83, 86, 89, 92, 96, 99, 103, 107, 111, 115, 119, 123, 128, 133, 137, 143,
+148, 153, 159, 165, 171, 177, 184, 191, 198, 205, 212, 220, 228, 237, 245, 255};
+
+MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, uint8 volume)
+ : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
+ _loop = loop;
+ _volume = volume;
+ _deltaVolume = 0;
+ _balance = 0;
+ _crossfade = false;
+ _crossfadeTarget = 0;
+ _crossfadeTime = 0;
+ _sub = NULL;
+ _stereo = false;
+ _loaded = false;
+
+ Audio::RewindableAudioStream *audioStream = NULL;
+
+ 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);
+ }
+
+ if (audioStream) {
+ _stereo = audioStream->isStereo();
+
+ if (_loop) {
+ Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES);
+ _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, dbMapLinear[_volume]);
+ } else {
+ _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, dbMapLinear[_volume]);
+ }
+
+ if (_key != StateKey_NotSet)
+ _engine->getScriptManager()->setStateValue(_key, 1);
+
+ // Change filename.raw into filename.sub
+ 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);
+
+ _loaded = true;
+ }
+}
+
+MusicNode::~MusicNode() {
+ if (_loaded)
+ _engine->_mixer->stopHandle(_handle);
+ if (_key != StateKey_NotSet)
+ _engine->getScriptManager()->setStateValue(_key, 2);
+ if (_sub)
+ delete _sub;
+ debug(1, "MusicNode: %d destroyed", _key);
+}
+
+void MusicNode::setDeltaVolume(uint8 volume) {
+ _deltaVolume = volume;
+ setVolume(_volume);
+}
+
+void MusicNode::setBalance(int8 balance) {
+ _balance = balance;
+ _engine->_mixer->setChannelBalance(_handle, _balance);
+}
+
+void MusicNode::setFade(int32 time, uint8 target) {
+ _crossfadeTarget = target;
+ _crossfadeTime = time;
+ _crossfade = true;
+}
+
+bool MusicNode::process(uint32 deltaTimeInMillis) {
+ if (!_loaded || ! _engine->_mixer->isSoundHandleActive(_handle))
+ return stop();
+ else {
+ uint8 _newvol = _volume;
+
+ if (_crossfade) {
+ if (_crossfadeTime > 0) {
+ if ((int32)deltaTimeInMillis > _crossfadeTime)
+ deltaTimeInMillis = _crossfadeTime;
+ _newvol += (int)(floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis);
+ _crossfadeTime -= deltaTimeInMillis;
+ } else {
+ _crossfade = false;
+ _newvol = _crossfadeTarget;
+ }
+ }
+
+ if (_volume != _newvol)
+ setVolume(_newvol);
+
+ if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1)
+ _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100);
+ }
+ return false;
+}
+
+void MusicNode::setVolume(uint8 newVolume) {
+ if (!_loaded)
+ return;
+
+ _volume = newVolume;
+
+ if (_deltaVolume >= _volume)
+ _engine->_mixer->setChannelVolume(_handle, 0);
+ else
+ _engine->_mixer->setChannelVolume(_handle, dbMapLinear[_volume - _deltaVolume]);
+}
+
+uint8 MusicNode::getVolume() {
+ return _volume;
+}
+
+PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos)
+ : ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK) {
+ _slot = slot;
+ _position = pos;
+
+ // Try to set pan value for music node immediately
+ process(0);
+}
+
+PanTrackNode::~PanTrackNode() {
+}
+
+bool PanTrackNode::process(uint32 deltaTimeInMillis) {
+ ScriptManager * scriptManager = _engine->getScriptManager();
+ ScriptingEffect *fx = scriptManager->getSideFX(_slot);
+ if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) {
+ MusicNodeBASE *mus = (MusicNodeBASE *)fx;
+
+ int curPos = scriptManager->getStateValue(StateKey_ViewPos);
+ int16 _width = _engine->getRenderManager()->getBkgSize().x;
+ int16 _halfWidth = _width / 2;
+ int16 _quarterWidth = _width / 4;
+
+ int tmp = 0;
+ if (curPos <= _position)
+ tmp = _position - curPos;
+ else
+ tmp = _position - curPos + _width;
+
+ int balance = 0;
+
+ if (tmp > _halfWidth)
+ tmp -= _width;
+
+ if (tmp > _quarterWidth) {
+ balance = 1;
+ tmp = _halfWidth - tmp;
+ } else if (tmp < -_quarterWidth) {
+ balance = -1;
+ tmp = -_halfWidth - tmp;
+ }
+
+ // Originally it's value -90...90 but we use -127...127 and therefore 360 replaced by 508
+ mus->setBalance( (508 * tmp) / _width );
+
+ tmp = (360 * tmp) / _width;
+
+ int deltaVol = balance;
+
+ // This value sets how fast volume goes off than sound source back of you
+ // By this value we can hack some "bugs" have place in originall game engine like beat sound in ZGI-dc10
+ int volumeCorrection = 2;
+
+ if (_engine->getGameId() == GID_GRANDINQUISITOR) {
+ if (scriptManager->getCurrentLocation() == "dc10")
+ volumeCorrection = 5;
+ }
+
+ if (deltaVol != 0)
+ deltaVol = (mus->getVolume() * volumeCorrection) * (90 - tmp * balance) / 90;
+ if (deltaVol > 255)
+ deltaVol = 255;
+
+ mus->setDeltaVolume(deltaVol);
+ }
+ return false;
+}
+
+MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume)
+ : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
+ _volume = volume;
+ _prog = program;
+ _noteNumber = note;
+ _pan = 0;
+
+ _chan = _engine->getMidiManager()->getFreeChannel();
+
+ if (_chan >= 0) {
+ _engine->getMidiManager()->setVolume(_chan, _volume);
+ _engine->getMidiManager()->setPan(_chan, _pan);
+ _engine->getMidiManager()->setProgram(_chan, _prog);
+ _engine->getMidiManager()->noteOn(_chan, _noteNumber, _volume);
+ }
+
+ if (_key != StateKey_NotSet)
+ _engine->getScriptManager()->setStateValue(_key, 1);
+}
+
+MusicMidiNode::~MusicMidiNode() {
+ if (_chan >= 0) {
+ _engine->getMidiManager()->noteOff(_chan);
+ }
+ if (_key != StateKey_NotSet)
+ _engine->getScriptManager()->setStateValue(_key, 2);
+}
+
+void MusicMidiNode::setDeltaVolume(uint8 volume) {
+}
+
+void MusicMidiNode::setBalance(int8 balance) {
+}
+
+void MusicMidiNode::setFade(int32 time, uint8 target) {
+}
+
+bool MusicMidiNode::process(uint32 deltaTimeInMillis) {
+ return false;
+}
+
+void MusicMidiNode::setVolume(uint8 newVolume) {
+ if (_chan >= 0) {
+ _engine->getMidiManager()->setVolume(_chan, newVolume);
+ }
+ _volume = newVolume;
+}
+
+uint8 MusicMidiNode::getVolume() {
+ return _volume;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/music_effect.h b/engines/zvision/scripting/effects/music_effect.h
new file mode 100644
index 0000000000..7657be8e09
--- /dev/null
+++ b/engines/zvision/scripting/effects/music_effect.h
@@ -0,0 +1,137 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_MUSIC_NODE_H
+#define ZVISION_MUSIC_NODE_H
+
+#include "audio/mixer.h"
+#include "zvision/scripting/scripting_effect.h"
+#include "zvision/text/subtitles.h"
+
+namespace Common {
+class String;
+}
+
+namespace ZVision {
+
+class MusicNodeBASE : public ScriptingEffect {
+public:
+ MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {}
+ ~MusicNodeBASE() {}
+
+ /**
+ * Decrement the timer by the delta time. If the timer is finished, set the status
+ * in _globalState and let this node be deleted
+ *
+ * @param deltaTimeInMillis The number of milliseconds that have passed since last frame
+ * @return If true, the node can be deleted after process() finishes
+ */
+ virtual bool process(uint32 deltaTimeInMillis) = 0;
+
+ virtual void setVolume(uint8 volume) = 0;
+ virtual uint8 getVolume() = 0;
+ virtual void setDeltaVolume(uint8 volume) = 0;
+ virtual void setBalance(int8 balance) = 0;
+
+ virtual void setFade(int32 time, uint8 target) = 0;
+};
+
+class MusicNode : public MusicNodeBASE {
+public:
+ MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, uint8 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);
+ uint8 getVolume();
+ void setDeltaVolume(uint8 volume);
+ void setBalance(int8 balance);
+
+ void setFade(int32 time, uint8 target);
+
+private:
+ uint8 _volume;
+ uint8 _deltaVolume;
+ int8 _balance;
+ bool _loop;
+ bool _crossfade;
+ uint8 _crossfadeTarget;
+ int32 _crossfadeTime;
+ bool _stereo;
+ Audio::SoundHandle _handle;
+ Subtitle *_sub;
+ bool _loaded;
+};
+
+// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr)
+class MusicMidiNode : public MusicNodeBASE {
+public:
+ MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume);
+ ~MusicMidiNode();
+
+ /**
+ * Decrement the timer by the delta time. If the timer is finished, set the status
+ * in _globalState and let this node be deleted
+ *
+ * @param deltaTimeInMillis The number of milliseconds that have passed since last frame
+ * @return If true, the node can be deleted after process() finishes
+ */
+ bool process(uint32 deltaTimeInMillis);
+
+ void setVolume(uint8 volume);
+ uint8 getVolume();
+ void setDeltaVolume(uint8 volume);
+ void setBalance(int8 balance);
+
+ void setFade(int32 time, uint8 target);
+
+private:
+ int8 _chan;
+ int8 _noteNumber;
+ int8 _pan;
+ int8 _volume;
+ int8 _prog;
+};
+
+class PanTrackNode : public ScriptingEffect {
+public:
+ PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos);
+ ~PanTrackNode();
+
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ uint32 _slot;
+ int16 _position;
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/effects/region_effect.cpp b/engines/zvision/scripting/effects/region_effect.cpp
new file mode 100644
index 0000000000..78061cf4de
--- /dev/null
+++ b/engines/zvision/scripting/effects/region_effect.cpp
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/effects/region_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+
+namespace ZVision {
+
+RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay)
+ : ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) {
+ _effect = effect;
+ _delay = delay;
+ _timeLeft = 0;
+}
+
+RegionNode::~RegionNode() {
+ _engine->getRenderManager()->deleteEffect(_key);
+}
+
+bool RegionNode::process(uint32 deltaTimeInMillis) {
+ _timeLeft -= deltaTimeInMillis;
+
+ if (_timeLeft <= 0) {
+ _timeLeft = _delay;
+ if (_effect)
+ _effect->update();
+ }
+
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/region_effect.h b/engines/zvision/scripting/effects/region_effect.h
new file mode 100644
index 0000000000..4fc16224ff
--- /dev/null
+++ b/engines/zvision/scripting/effects/region_effect.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_REGION_NODE_H
+#define ZVISION_REGION_NODE_H
+
+#include "graphics/surface.h"
+
+#include "zvision/scripting/scripting_effect.h"
+#include "zvision/graphics/graphics_effect.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class RegionNode : public ScriptingEffect {
+public:
+ RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay);
+ ~RegionNode();
+
+ /**
+ * Decrement the timer by the delta time. If the timer is finished, set the status
+ * in _globalState and let this node be deleted
+ *
+ * @param deltaTimeInMillis The number of milliseconds that have passed since last frame
+ * @return If true, the node can be deleted after process() finishes
+ */
+ bool process(uint32 deltaTimeInMillis);
+
+private:
+ int32 _timeLeft;
+ uint32 _delay;
+ GraphicsEffect *_effect;
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/effects/syncsound_effect.cpp b/engines/zvision/scripting/effects/syncsound_effect.cpp
new file mode 100644
index 0000000000..70ba97deb8
--- /dev/null
+++ b/engines/zvision/scripting/effects/syncsound_effect.cpp
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/effects/syncsound_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/sound/zork_raw.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "audio/decoders/wave.h"
+
+namespace ZVision {
+
+SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::String &filename, int32 syncto)
+ : ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) {
+ _syncto = syncto;
+ _sub = NULL;
+
+ Audio::RewindableAudioStream *audioStream = NULL;
+
+ 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 && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1)
+ _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100);
+ }
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/syncsound_effect.h b/engines/zvision/scripting/effects/syncsound_effect.h
new file mode 100644
index 0000000000..0eabff77a3
--- /dev/null
+++ b/engines/zvision/scripting/effects/syncsound_effect.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_SYNCSOUND_NODE_H
+#define ZVISION_SYNCSOUND_NODE_H
+
+#include "audio/mixer.h"
+#include "zvision/scripting/scripting_effect.h"
+#include "zvision/text/subtitles.h"
+
+namespace Common {
+class String;
+}
+
+namespace ZVision {
+class SyncSoundNode : public ScriptingEffect {
+public:
+ SyncSoundNode(ZVision *engine, uint32 key, Common::String &file, int32 syncto);
+ ~SyncSoundNode();
+
+ /**
+ * Decrement the timer by the delta time. If the timer is finished, set the status
+ * in _globalState and let this node be deleted
+ *
+ * @param deltaTimeInMillis The number of milliseconds that have passed since last frame
+ * @return If true, the node can be deleted after process() finishes
+ */
+ bool process(uint32 deltaTimeInMillis);
+private:
+ int32 _syncto;
+ Audio::SoundHandle _handle;
+ Subtitle *_sub;
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/timer_node.cpp b/engines/zvision/scripting/effects/timer_effect.cpp
index c8c8a85d34..778f9dec6c 100644
--- a/engines/zvision/scripting/controls/timer_node.cpp
+++ b/engines/zvision/scripting/effects/timer_effect.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,54 @@
#include "common/scummsys.h"
-#include "zvision/scripting/controls/timer_node.h"
+#include "zvision/scripting/effects/timer_effect.h"
#include "zvision/zvision.h"
#include "zvision/scripting/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) {
+ : ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) {
+ _timeLeft = 0;
+
+ 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/effects/timer_effect.h
index 48b5fad1e9..5e45d54d7d 100644
--- a/engines/zvision/scripting/controls/timer_node.h
+++ b/engines/zvision/scripting/effects/timer_effect.h
@@ -8,12 +8,12 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -23,13 +23,13 @@
#ifndef ZVISION_TIMER_NODE_H
#define ZVISION_TIMER_NODE_H
-#include "zvision/scripting/control.h"
+#include "zvision/scripting/scripting_effect.h"
namespace ZVision {
class ZVision;
-class TimerNode : public Control {
+class TimerNode : public ScriptingEffect {
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/effects/ttytext_effect.cpp b/engines/zvision/scripting/effects/ttytext_effect.cpp
new file mode 100644
index 0000000000..8d340dae39
--- /dev/null
+++ b/engines/zvision/scripting/effects/ttytext_effect.cpp
@@ -0,0 +1,172 @@
+/* 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/effects/ttytext_effect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/text/text.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+
+namespace ZVision {
+
+ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay) :
+ ScriptingEffect(engine, key, SCRIPTING_EFFECT_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->_resourcePixelFormat);
+ _state._sharp = true;
+ _state.readAllStyles(_txtbuf);
+ _state.updateFontWithTextState(_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 start = _txtpos;
+ int32 end = 0;
+ int16 ret = 0;
+ while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size())
+ _txtpos++;
+ end = _txtpos;
+ if (start != -1) {
+ if ((end - start - 1) > 0) {
+ ret = _state.parseStyle(_txtbuf.c_str() + start + 1, end - start - 1);
+ }
+ }
+
+ if (ret & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
+ _state.updateFontWithTextState(_fnt);
+ } else if (ret & TEXT_CHANGE_NEWLINE) {
+ newline();
+ }
+
+ if (ret & TEXT_CHANGE_HAS_STATE_BOX) {
+ Common::String buf;
+ buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_state._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->_resourcePixelFormat.RGBToColor(_state._red, _state._green, _state._blue);
+
+ if (_dx + _fnt.getCharWidth(chr) > _r.width())
+ newline();
+
+ if (_dy + _fnt.getFontHeight() >= _r.height())
+ scroll();
+
+ _fnt.drawChar(&_img, chr, _dx, _dy, clr);
+
+ _dx += _fnt.getCharWidth(chr);
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/effects/ttytext_effect.h b/engines/zvision/scripting/effects/ttytext_effect.h
new file mode 100644
index 0000000000..18cbbbaee3
--- /dev/null
+++ b/engines/zvision/scripting/effects/ttytext_effect.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_TTYTEXT_NODE_H
+#define ZVISION_TTYTEXT_NODE_H
+
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+#include "zvision/scripting/scripting_effect.h"
+#include "zvision/text/text.h"
+#include "zvision/text/truetype_font.h"
+
+namespace Common {
+class String;
+}
+
+namespace ZVision {
+class ttyTextNode : public ScriptingEffect {
+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;
+
+ TextStyleState _state;
+ StyledTTFont _fnt;
+ Common::String _txtbuf;
+ uint32 _txtpos;
+
+ int32 _delay;
+ int32 _nexttime;
+ Graphics::Surface _img;
+ int16 _dx;
+ int16 _dy;
+private:
+
+ void newline();
+ void scroll();
+ void outchar(uint16 chr);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/inventory.cpp b/engines/zvision/scripting/inventory.cpp
new file mode 100644
index 0000000000..76d43b200b
--- /dev/null
+++ b/engines/zvision/scripting/inventory.cpp
@@ -0,0 +1,122 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/script_manager.h"
+
+namespace ZVision {
+
+int8 ScriptManager::inventoryGetCount() {
+ return getStateValue(StateKey_Inv_Cnt_Slot);
+}
+
+void ScriptManager::inventorySetCount(int8 cnt) {
+ setStateValue(StateKey_Inv_Cnt_Slot, cnt);
+}
+
+int16 ScriptManager::inventoryGetItem(int8 id) {
+ if (id < 49 && id >= 0)
+ return getStateValue(StateKey_Inv_1_Slot + id);
+ return -1;
+}
+
+void ScriptManager::inventorySetItem(int8 id, int16 item) {
+ if (id < 49 && id >= 0)
+ setStateValue(StateKey_Inv_1_Slot + id, item);
+}
+
+void ScriptManager::inventoryAdd(int16 item) {
+ int8 cnt = inventoryGetCount();
+
+ if (cnt < 49) {
+ bool notExist = true;
+
+ if (cnt == 0) {
+ inventorySetItem(0, 0);
+ inventorySetCount(1); // we needed empty item for cycle code
+ cnt = 1;
+ }
+
+ for (int8 cur = 0; cur < cnt; cur++)
+ if (inventoryGetItem(cur) == item) {
+ notExist = false;
+ break;
+ }
+
+ if (notExist) {
+ for (int8 i = cnt; i > 0; i--)
+ inventorySetItem(i, inventoryGetItem(i - 1));
+
+ inventorySetItem(0, item);
+
+ setStateValue(StateKey_InventoryItem, item);
+
+ inventorySetCount(cnt + 1);
+ }
+ }
+}
+
+void ScriptManager::inventoryDrop(int16 item) {
+ int8 itemCount = inventoryGetCount();
+
+ // if items in inventory > 0
+ if (itemCount != 0) {
+ int8 index = 0;
+
+ // finding needed item
+ while (index < itemCount) {
+ if (inventoryGetItem(index) == item)
+ break;
+
+ index++;
+ }
+
+ // if item in the inventory
+ if (itemCount != index) {
+ // shift all items left with rewrite founded item
+ for (int8 v = index; v < itemCount - 1 ; v++)
+ inventorySetItem(v, inventoryGetItem(v + 1));
+
+ // del last item
+ inventorySetItem(itemCount - 1, 0);
+ inventorySetCount(inventoryGetCount() - 1);
+
+ setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
+ }
+ }
+}
+void ScriptManager::inventoryCycle() {
+ int8 itemCount = inventoryGetCount();
+ int8 curItem = inventoryGetItem(0);
+ if (itemCount > 1) {
+ for (int8 i = 0; i < itemCount - 1; i++)
+ inventorySetItem(i, inventoryGetItem(i + 1));
+
+ inventorySetItem(itemCount - 1, curItem);
+
+ setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
+
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/menu.cpp b/engines/zvision/scripting/menu.cpp
new file mode 100644
index 0000000000..e7775cbe3f
--- /dev/null
+++ b/engines/zvision/scripting/menu.cpp
@@ -0,0 +1,761 @@
+/* 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/graphics/render_manager.h"
+#include "zvision/scripting/menu.h"
+
+namespace ZVision {
+
+enum {
+ kMainMenuSave = 0,
+ kMainMenuLoad = 1,
+ kMainMenuPrefs = 2,
+ kMainMenuExit = 3
+};
+
+enum {
+ kMenuItem = 0,
+ kMenuMagic = 1,
+ kMenuMain = 2
+};
+
+MenuHandler::MenuHandler(ZVision *engine) {
+ _engine = engine;
+ menuBarFlag = 0xFFFF;
+}
+
+MenuZGI::MenuZGI(ZVision *engine) :
+ MenuHandler(engine) {
+ menuMouseFocus = -1;
+ inmenu = false;
+ scrolled[0] = false;
+ scrolled[1] = false;
+ scrolled[2] = false;
+ scrollPos[0] = 0;
+ scrollPos[1] = 0;
+ scrollPos[2] = 0;
+ mouseOnItem = -1;
+ redraw = false;
+ clean = false;
+
+ char buf[24];
+ for (int i = 1; i < 4; i++) {
+ sprintf(buf, "gmzau%2.2x1.tga", i);
+ _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][0], false);
+ sprintf(buf, "gmzau%2.2x1.tga", i + 0x10);
+ _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][1], false);
+ }
+ for (int i = 0; i < 4; i++) {
+ sprintf(buf, "gmzmu%2.2x1.tga", i);
+ _engine->getRenderManager()->readImageToSurface(buf, menubar[i][0], false);
+ sprintf(buf, "gmznu%2.2x1.tga", i);
+ _engine->getRenderManager()->readImageToSurface(buf, menubar[i][1], false);
+ }
+
+ for (int i = 0; i < 50; i++) {
+ items[i][0] = NULL;
+ items[i][1] = NULL;
+ itemId[i] = 0;
+ }
+
+ for (int i = 0; i < 12; i++) {
+ magic[i][0] = NULL;
+ magic[i][1] = NULL;
+ magicId[i] = 0;
+ }
+}
+
+MenuZGI::~MenuZGI() {
+ for (int i = 0; i < 3; i++) {
+ menuback[i][0].free();
+ menuback[i][1].free();
+ }
+ for (int i = 0; i < 4; i++) {
+ menubar[i][0].free();
+ menubar[i][1].free();
+ }
+ for (int i = 0; i < 50; i++) {
+ if (items[i][0]) {
+ items[i][0]->free();
+ delete items[i][0];
+ }
+ if (items[i][1]) {
+ items[i][1]->free();
+ delete items[i][1];
+ }
+ }
+ for (int i = 0; i < 12; i++) {
+ if (magic[i][0]) {
+ magic[i][0]->free();
+ delete magic[i][0];
+ }
+ if (magic[i][1]) {
+ magic[i][1]->free();
+ delete magic[i][1];
+ }
+ }
+}
+
+void MenuZGI::onMouseUp(const Common::Point &Pos) {
+ if (Pos.y < 40) {
+ switch (menuMouseFocus) {
+ case kMenuItem:
+ if (menuBarFlag & kMenubarItems) {
+ int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
+ if (itemCount == 0)
+ itemCount = 20;
+
+ for (int i = 0; i < itemCount; i++) {
+ int itemspace = (600 - 28) / itemCount;
+
+ if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0,
+ scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) {
+ int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
+ if (mouseItem >= 0 && mouseItem < 0xE0) {
+ _engine->getScriptManager()->inventoryDrop(mouseItem);
+ _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i));
+ _engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem);
+
+ redraw = true;
+ }
+ }
+ }
+ }
+ break;
+
+ case kMenuMagic:
+ if (menuBarFlag & kMenubarMagic) {
+ for (int i = 0; i < 12; i++) {
+
+ uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
+ if (itemnum != 0) {
+ if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 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[kMenuMagic], 0,
+ 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos))
+ _engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum);
+ }
+
+ }
+ break;
+
+ case kMenuMain:
+
+ // Exit
+ if (menuBarFlag & kMenubarExit)
+ if (Common::Rect(320 + 135,
+ scrollPos[kMenuMain],
+ 320 + 135 + 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ _engine->ifQuit();
+ }
+
+ // Settings
+ if (menuBarFlag & kMenubarSettings)
+ if (Common::Rect(320 ,
+ scrollPos[kMenuMain],
+ 320 + 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0);
+ }
+
+ // Load
+ if (menuBarFlag & kMenubarRestore)
+ if (Common::Rect(320 - 135,
+ scrollPos[kMenuMain],
+ 320,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0);
+ }
+
+ // Save
+ if (menuBarFlag & kMenubarSave)
+ if (Common::Rect(320 - 135 * 2,
+ scrollPos[kMenuMain],
+ 320 - 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0);
+ }
+ break;
+ }
+ }
+}
+
+void MenuZGI::onMouseMove(const Common::Point &Pos) {
+ if (Pos.y < 40) {
+
+ if (!inmenu)
+ redraw = true;
+ inmenu = true;
+ switch (menuMouseFocus) {
+ case kMenuItem:
+ if (menuBarFlag & kMenubarItems) {
+ int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
+ if (itemCount == 0)
+ itemCount = 20;
+ else if (itemCount > 50)
+ itemCount = 50;
+
+ int lastItem = mouseOnItem;
+
+ mouseOnItem = -1;
+
+ for (int i = 0; i < itemCount; i++) {
+ int itemspace = (600 - 28) / itemCount;
+
+ if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0,
+ scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) {
+ mouseOnItem = i;
+ break;
+ }
+ }
+
+ if (lastItem != mouseOnItem)
+ if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + mouseOnItem) ||
+ _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem))
+ redraw = true;
+ }
+ break;
+
+ case kMenuMagic:
+ if (menuBarFlag & kMenubarMagic) {
+ int lastItem = mouseOnItem;
+ mouseOnItem = -1;
+ for (int i = 0; i < 12; i++) {
+ if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0,
+ 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) {
+ mouseOnItem = i;
+ break;
+ }
+ }
+
+ if (lastItem != mouseOnItem)
+ if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + mouseOnItem) ||
+ _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem))
+ redraw = true;
+
+ }
+ break;
+
+ case kMenuMain: {
+ int lastItem = mouseOnItem;
+ mouseOnItem = -1;
+
+ // Exit
+ if (menuBarFlag & kMenubarExit)
+ if (Common::Rect(320 + 135,
+ scrollPos[kMenuMain],
+ 320 + 135 + 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuExit;
+ }
+
+ // Settings
+ if (menuBarFlag & kMenubarSettings)
+ if (Common::Rect(320 ,
+ scrollPos[kMenuMain],
+ 320 + 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuPrefs;
+ }
+
+ // Load
+ if (menuBarFlag & kMenubarRestore)
+ if (Common::Rect(320 - 135,
+ scrollPos[kMenuMain],
+ 320,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuLoad;
+ }
+
+ // Save
+ if (menuBarFlag & kMenubarSave)
+ if (Common::Rect(320 - 135 * 2,
+ scrollPos[kMenuMain],
+ 320 - 135,
+ scrollPos[kMenuMain] + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuSave;
+ }
+
+ if (lastItem != mouseOnItem)
+ redraw = true;
+ }
+ break;
+
+ default:
+ int cur_menu = menuMouseFocus;
+ if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main
+ menuMouseFocus = kMenuMain;
+ scrolled[kMenuMain] = false;
+ scrollPos[kMenuMain] = menuback[kMenuMain][1].h - menuback[kMenuMain][0].h;
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2);
+ }
+
+ if (menuBarFlag & kMenubarMagic)
+ if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic
+ menuMouseFocus = kMenuMagic;
+ scrolled[kMenuMagic] = false;
+ scrollPos[kMenuMagic] = 28;
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3);
+ }
+
+ if (menuBarFlag & kMenubarItems)
+ if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items
+ menuMouseFocus = kMenuItem;
+ scrolled[kMenuItem] = false;
+ scrollPos[kMenuItem] = 28 - 600;
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1);
+ }
+
+ if (cur_menu != menuMouseFocus)
+ clean = true;
+
+ break;
+ }
+ } else {
+ if (inmenu)
+ clean = true;
+ inmenu = false;
+ if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0)
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0);
+ menuMouseFocus = -1;
+ }
+}
+
+void MenuZGI::process(uint32 deltatime) {
+ if (clean) {
+ _engine->getRenderManager()->clearMenuSurface();
+ clean = false;
+ }
+ switch (menuMouseFocus) {
+ case kMenuItem:
+ if (menuBarFlag & kMenubarItems)
+ if (!scrolled[kMenuItem]) {
+ redraw = true;
+ float scrl = 600.0 * (deltatime / 1000.0);
+
+ if (scrl == 0)
+ scrl = 1.0;
+
+ scrollPos[kMenuItem] += (int)scrl;
+
+ if (scrollPos[kMenuItem] >= 0) {
+ scrolled[kMenuItem] = true;
+ scrollPos[kMenuItem] = 0;
+ }
+ }
+ if (redraw) {
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuItem][0], scrollPos[kMenuItem], 0);
+
+ int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
+ if (itemCount == 0)
+ itemCount = 20;
+ else if (itemCount > 50)
+ itemCount = 50;
+
+ for (int i = 0; i < itemCount; i++) {
+ int itemspace = (600 - 28) / itemCount;
+
+ bool inrect = false;
+
+ if (mouseOnItem == i)
+ inrect = true;
+
+ uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i);
+
+ if (curItemId != 0) {
+ if (itemId[i] != curItemId) {
+ char buf[16];
+ sprintf(buf, "gmzwu%2.2x1.tga", curItemId);
+ items[i][0] = _engine->getRenderManager()->loadImage(buf, false);
+ sprintf(buf, "gmzxu%2.2x1.tga", curItemId);
+ items[i][1] = _engine->getRenderManager()->loadImage(buf, false);
+ itemId[i] = curItemId;
+ }
+
+ if (inrect)
+ _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[kMenuItem] + itemspace * i, 0, 0);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[kMenuItem] + itemspace * i, 0, 0);
+
+ } else {
+ if (items[i][0]) {
+ items[i][0]->free();
+ delete items[i][0];
+ items[i][0] = NULL;
+ }
+ if (items[i][1]) {
+ items[i][1]->free();
+ delete items[i][1];
+ items[i][1] = NULL;
+ }
+ itemId[i] = 0;
+ }
+ }
+
+ redraw = false;
+ }
+ break;
+
+ case kMenuMagic:
+ if (menuBarFlag & kMenubarMagic)
+ if (!scrolled[kMenuMagic]) {
+ redraw = true;
+ float scrl = 600.0 * (deltatime / 1000.0);
+
+ if (scrl == 0)
+ scrl = 1.0;
+
+ scrollPos[kMenuMagic] += (int)scrl;
+
+ if (scrollPos[kMenuMagic] >= 600) {
+ scrolled[kMenuMagic] = true;
+ scrollPos[kMenuMagic] = 600;
+ }
+ }
+ if (redraw) {
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMagic][0], 640 - scrollPos[kMenuMagic], 0);
+
+ for (int i = 0; i < 12; i++) {
+ bool inrect = false;
+
+ if (mouseOnItem == i)
+ inrect = true;
+
+ uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
+ if (curItemId) {
+ if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
+ curItemId = 0xEE + i;
+ else
+ curItemId = 0xE0 + i;
+ }
+
+ if (curItemId != 0) {
+ if (itemId[i] != curItemId) {
+ char buf[16];
+ sprintf(buf, "gmzwu%2.2x1.tga", curItemId);
+ magic[i][0] = _engine->getRenderManager()->loadImage(buf, false);
+ sprintf(buf, "gmzxu%2.2x1.tga", curItemId);
+ magic[i][1] = _engine->getRenderManager()->loadImage(buf, false);
+ magicId[i] = curItemId;
+ }
+
+ if (inrect)
+ _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0);
+
+ } else {
+ if (magic[i][0]) {
+ magic[i][0]->free();
+ delete magic[i][0];
+ magic[i][0] = NULL;
+ }
+ if (magic[i][1]) {
+ magic[i][1]->free();
+ delete magic[i][1];
+ magic[i][1] = NULL;
+ }
+ magicId[i] = 0;
+ }
+ }
+ redraw = false;
+ }
+ break;
+
+ case kMenuMain:
+ if (!scrolled[kMenuMain]) {
+ redraw = true;
+ float scrl = 32.0 * 2.0 * (deltatime / 1000.0);
+
+ if (scrl == 0)
+ scrl = 1.0;
+
+ scrollPos[kMenuMain] += (int)scrl;
+
+ if (scrollPos[kMenuMain] >= 0) {
+ scrolled[kMenuMain] = true;
+ scrollPos[kMenuMain] = 0;
+ }
+ }
+ if (redraw) {
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMain][0], 30, scrollPos[kMenuMain]);
+
+ if (menuBarFlag & kMenubarExit) {
+ if (mouseOnItem == kMainMenuExit)
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuExit][1], 320 + 135, scrollPos[kMenuMain]);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuExit][0], 320 + 135, scrollPos[kMenuMain]);
+ }
+ if (menuBarFlag & kMenubarSettings) {
+ if (mouseOnItem == kMainMenuPrefs)
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuPrefs][1], 320, scrollPos[kMenuMain]);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuPrefs][0], 320, scrollPos[kMenuMain]);
+ }
+ if (menuBarFlag & kMenubarRestore) {
+ if (mouseOnItem == kMainMenuLoad)
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuLoad][1], 320 - 135, scrollPos[kMenuMain]);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuLoad][0], 320 - 135, scrollPos[kMenuMain]);
+ }
+ if (menuBarFlag & kMenubarSave) {
+ if (mouseOnItem == kMainMenuSave)
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuSave][1], 320 - 135 * 2, scrollPos[kMenuMain]);
+ else
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuSave][0], 320 - 135 * 2, scrollPos[kMenuMain]);
+ }
+ redraw = false;
+ }
+ break;
+ default:
+ if (redraw) {
+ if (inmenu) {
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMain][1], 30, 0);
+
+ if (menuBarFlag & kMenubarItems)
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuItem][1], 0, 0);
+
+ if (menuBarFlag & kMenubarMagic)
+ _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMagic][1], 640 - 28, 0);
+ }
+ redraw = false;
+ }
+ break;
+ }
+}
+
+MenuNemesis::MenuNemesis(ZVision *engine) :
+ MenuHandler(engine) {
+ inmenu = false;
+ scrolled = false;
+ scrollPos = 0;
+ mouseOnItem = -1;
+ redraw = false;
+ delay = 0;
+
+ char buf[24];
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 6; j++) {
+ sprintf(buf, "butfrm%d%d.tga", i + 1, j);
+ _engine->getRenderManager()->readImageToSurface(buf, but[i][j], false);
+ }
+
+ _engine->getRenderManager()->readImageToSurface("bar.tga", menubar, false);
+
+ frm = 0;
+}
+
+MenuNemesis::~MenuNemesis() {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 6; j++)
+ but[i][j].free();
+
+ menubar.free();
+}
+
+static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} };
+
+void MenuNemesis::onMouseUp(const Common::Point &Pos) {
+ if (Pos.y < 40) {
+ // Exit
+ if (menuBarFlag & kMenubarExit)
+ if (Common::Rect(buts[3][1],
+ scrollPos,
+ buts[3][0] + buts[3][1],
+ scrollPos + 32).contains(Pos)) {
+ _engine->ifQuit();
+ frm = 5;
+ redraw = true;
+ }
+
+ // Settings
+ if (menuBarFlag & kMenubarSettings)
+ if (Common::Rect(buts[2][1],
+ scrollPos,
+ buts[2][0] + buts[2][1],
+ scrollPos + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0);
+ frm = 5;
+ redraw = true;
+ }
+
+ // Load
+ if (menuBarFlag & kMenubarRestore)
+ if (Common::Rect(buts[1][1],
+ scrollPos,
+ buts[1][0] + buts[1][1],
+ scrollPos + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0);
+ frm = 5;
+ redraw = true;
+ }
+
+ // Save
+ if (menuBarFlag & kMenubarSave)
+ if (Common::Rect(buts[0][1],
+ scrollPos,
+ buts[0][0] + buts[0][1],
+ scrollPos + 32).contains(Pos)) {
+ _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0);
+ frm = 5;
+ redraw = true;
+ }
+ }
+}
+
+void MenuNemesis::onMouseMove(const Common::Point &Pos) {
+ if (Pos.y < 40) {
+
+ inmenu = true;
+
+ if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2)
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2);
+
+ int lastItem = mouseOnItem;
+ mouseOnItem = -1;
+
+ // Exit
+ if (menuBarFlag & kMenubarExit)
+ if (Common::Rect(buts[3][1],
+ scrollPos,
+ buts[3][0] + buts[3][1],
+ scrollPos + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuExit;
+ }
+
+ // Settings
+ if (menuBarFlag & kMenubarSettings)
+ if (Common::Rect(buts[2][1],
+ scrollPos,
+ buts[2][0] + buts[2][1],
+ scrollPos + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuPrefs;
+ }
+
+ // Load
+ if (menuBarFlag & kMenubarRestore)
+ if (Common::Rect(buts[1][1],
+ scrollPos,
+ buts[1][0] + buts[1][1],
+ scrollPos + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuLoad;
+ }
+
+ // Save
+ if (menuBarFlag & kMenubarSave)
+ if (Common::Rect(buts[0][1],
+ scrollPos,
+ buts[0][0] + buts[0][1],
+ scrollPos + 32).contains(Pos)) {
+ mouseOnItem = kMainMenuSave;
+ }
+
+ if (lastItem != mouseOnItem) {
+ redraw = true;
+ frm = 0;
+ delay = 200;
+ }
+ } else {
+ inmenu = false;
+ if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0)
+ _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0);
+ mouseOnItem = -1;
+ }
+}
+
+void MenuNemesis::process(uint32 deltatime) {
+ if (inmenu) {
+ if (!scrolled) {
+ float scrl = 32.0 * 2.0 * (deltatime / 1000.0);
+
+ if (scrl == 0)
+ scrl = 1.0;
+
+ scrollPos += (int)scrl;
+ redraw = true;
+ }
+
+ if (scrollPos >= 0) {
+ scrolled = true;
+ scrollPos = 0;
+ }
+
+ if (mouseOnItem != -1) {
+ delay -= deltatime;
+ if (delay <= 0 && frm < 4) {
+ delay = 200;
+ frm++;
+ redraw = true;
+ }
+ }
+
+ if (redraw) {
+ _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos);
+
+ if (menuBarFlag & kMenubarExit)
+ if (mouseOnItem == kMainMenuExit)
+ _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos);
+
+ if (menuBarFlag & kMenubarSettings)
+ if (mouseOnItem == kMainMenuPrefs)
+ _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos);
+
+ if (menuBarFlag & kMenubarRestore)
+ if (mouseOnItem == kMainMenuLoad)
+ _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos);
+
+ if (menuBarFlag & kMenubarSave)
+ if (mouseOnItem == kMainMenuSave)
+ _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, (int16)(32 + scrollPos - scrl), 64 + 512, 32 + scrollPos + 1);
+ _engine->getRenderManager()->clearMenuSurface(cl);
+
+ scrollPos -= (int)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/scripting/menu.h b/engines/zvision/scripting/menu.h
new file mode 100644
index 0000000000..a88587966f
--- /dev/null
+++ b/engines/zvision/scripting/menu.h
@@ -0,0 +1,119 @@
+/* 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_MENU_H
+#define ZVISION_MENU_H
+
+#include "graphics/surface.h"
+#include "common/rect.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+
+namespace ZVision {
+
+enum menuBar {
+ kMenubarExit = 0x1,
+ kMenubarSettings = 0x2,
+ kMenubarRestore = 0x4,
+ kMenubarSave = 0x8,
+ kMenubarItems = 0x100,
+ kMenubarMagic = 0x200
+};
+
+class MenuHandler {
+public:
+ MenuHandler(ZVision *engine);
+ virtual ~MenuHandler() {};
+ virtual void onMouseMove(const Common::Point &Pos) {};
+ virtual void onMouseDown(const Common::Point &Pos) {};
+ virtual void onMouseUp(const Common::Point &Pos) {};
+ virtual void process(uint32 deltaTimeInMillis) {};
+
+ void setEnable(uint16 flags) {
+ menuBarFlag = flags;
+ }
+ uint16 getEnable() {
+ return menuBarFlag;
+ }
+protected:
+ uint16 menuBarFlag;
+ ZVision *_engine;
+};
+
+class MenuZGI: public MenuHandler {
+public:
+ MenuZGI(ZVision *engine);
+ ~MenuZGI();
+ void onMouseMove(const Common::Point &Pos);
+ void onMouseUp(const Common::Point &Pos);
+ void process(uint32 deltaTimeInMillis);
+private:
+ Graphics::Surface menuback[3][2];
+ Graphics::Surface menubar[4][2];
+ Graphics::Surface *items[50][2];
+ uint itemId[50];
+
+ Graphics::Surface *magic[12][2];
+ uint magicId[12];
+
+ int menuMouseFocus;
+ bool inmenu;
+
+ int mouseOnItem;
+
+ bool scrolled[3];
+ int16 scrollPos[3];
+
+ bool clean;
+ bool redraw;
+
+};
+
+class MenuNemesis: public MenuHandler {
+public:
+ MenuNemesis(ZVision *engine);
+ ~MenuNemesis();
+ void onMouseMove(const Common::Point &Pos);
+ void onMouseUp(const Common::Point &Pos);
+ void process(uint32 deltaTimeInMillis);
+private:
+ Graphics::Surface but[4][6];
+ Graphics::Surface menubar;
+
+ bool inmenu;
+
+ int mouseOnItem;
+
+ bool scrolled;
+ int16 scrollPos;
+
+ bool redraw;
+
+ int frm;
+ int16 delay;
+
+};
+
+}
+
+#endif
diff --git a/engines/zvision/scripting/puzzle.h b/engines/zvision/scripting/puzzle.h
index ee9ce521c9..7d64357b0a 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.
@@ -28,11 +28,10 @@
#include "common/list.h"
#include "common/ptr.h"
-
namespace ZVision {
struct Puzzle {
- Puzzle() : key(0) {}
+ Puzzle() : key(0), addedBySetState(false) {}
~Puzzle() {
for (Common::List<ResultAction *>::iterator iter = resultActions.begin(); iter != resultActions.end(); ++iter) {
@@ -63,10 +62,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..edc1b8622c 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,33 +22,38 @@
#include "common/scummsys.h"
+#include "zvision/zvision.h"
#include "zvision/scripting/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/scripting/controls/slot_control.h"
+#include "zvision/scripting/controls/save_control.h"
+#include "zvision/scripting/controls/input_control.h"
+#include "zvision/scripting/controls/safe_control.h"
+#include "zvision/scripting/controls/hotmov_control.h"
+#include "zvision/scripting/controls/fist_control.h"
+#include "zvision/scripting/controls/paint_control.h"
+#include "zvision/scripting/controls/titler_control.h"
#include "common/textconsole.h"
#include "common/file.h"
#include "common/tokenizer.h"
-
namespace ZVision {
-void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) {
+void ScriptManager::parseScrFile(const Common::String &fileName, ScriptScope &scope) {
Common::File file;
- if (!file.open(fileName)) {
- warning("Script file not found: %s", fileName.c_str());
- return;
+ if (!_engine->getSearchManager()->openFile(file, fileName)) {
+ error("Script file not found: %s", fileName.c_str());
}
- while(!file.eos()) {
+ while (!file.eos()) {
Common::String line = file.readLine();
if (file.err()) {
- warning("Error parsing scr file: %s", fileName.c_str());
- return;
+ error("Error parsing scr file: %s", fileName.c_str());
}
trimCommentsAndWhiteSpace(&line);
@@ -57,18 +62,19 @@ void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal)
if (line.matchString("puzzle:*", true)) {
Puzzle *puzzle = new Puzzle();
- sscanf(line.c_str(),"puzzle:%u",&(puzzle->key));
-
+ sscanf(line.c_str(), "puzzle:%u", &(puzzle->key));
+ if (getStateFlag(puzzle->key) & Puzzle::ONCE_PER_INST)
+ setStateValue(puzzle->key, 0);
parsePuzzle(puzzle, file);
- if (isGlobal) {
- _globalPuzzles.push_back(puzzle);
- } else {
- _activePuzzles.push_back(puzzle);
- }
+ scope.puzzles.push_back(puzzle);
+
} else if (line.matchString("control:*", true)) {
- parseControl(line, file);
+ Control *ctrl = parseControl(line, file);
+ if (ctrl)
+ scope.controls.push_back(ctrl);
}
}
+ scope.procCount = 0;
}
void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) {
@@ -77,23 +83,41 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre
while (!stream.eos() && !line.contains('}')) {
if (line.matchString("criteria {", true)) {
- parseCriteria(stream, puzzle->criteriaList);
+ parseCriteria(stream, puzzle->criteriaList, puzzle->key);
} else if (line.matchString("results {", true)) {
parseResults(stream, puzzle->resultActions);
+
+ // WORKAROUND for a script bug in Zork Nemesis, room ve5e (tuning
+ // fork box closeup). If the player leaves the screen while the
+ // box is open, puzzle 19398 shows the animation where the box
+ // closes, but the box state (state variable 19397) is not updated.
+ // We insert the missing assignment for the box state here.
+ // Fixes bug #6803.
+ if (_engine->getGameId() == GID_NEMESIS && puzzle->key == 19398)
+ puzzle->resultActions.push_back(new ActionAssign(_engine, 11, "19397, 0"));
} else if (line.matchString("flags {", true)) {
- setStateFlags(puzzle->key, parseFlags(stream));
+ setStateFlag(puzzle->key, parseFlags(stream));
}
line = stream.readLine();
trimCommentsAndWhiteSpace(&line);
}
+
+ puzzle->addedBySetState = false;
}
-bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const {
+bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const {
// Loop until we find the closing brace
Common::String line = stream.readLine();
trimCommentsAndWhiteSpace(&line);
+ // Skip any commented out criteria. If all the criteria are commented out,
+ // we might end up with an invalid criteria list (bug #6776).
+ while (line.empty()) {
+ line = stream.readLine();
+ trimCommentsAndWhiteSpace(&line);
+ }
+
// Criteria can be empty
if (line.contains('}')) {
return false;
@@ -102,6 +126,21 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li
// Create a new List to hold the CriteriaEntries
criteriaList.push_back(Common::List<Puzzle::CriteriaEntry>());
+ // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
+ // Since we patch the script that triggers when manipulating the left fist
+ // (below), we add an additional check for the left fist sound, so that it
+ // doesn't get killed immediately when the left fist animation starts.
+ // Together with the workaround below, it fixes bug #6783.
+ if (_engine->getGameId() == GID_NEMESIS && key == 3594) {
+ Puzzle::CriteriaEntry entry;
+ entry.key = 567;
+ entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
+ entry.argumentIsAKey = false;
+ entry.argument = 1;
+
+ criteriaList.back().push_back(entry);
+ }
+
while (!stream.eos() && !line.contains('}')) {
Puzzle::CriteriaEntry entry;
@@ -113,6 +152,13 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li
token = tokenizer.nextToken();
sscanf(token.c_str(), "[%u]", &(entry.key));
+ // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
+ // Check for the state of animation 567 (left fist) when manipulating
+ // the fingers of the left fist (puzzle slots 3582, 3583).
+ // Together with the workaround above, it fixes bug #6783.
+ if (_engine->getGameId() == GID_NEMESIS && (key == 3582 || key == 3583) && entry.key == 568)
+ entry.key = 567;
+
// Parse the operator out of the second token
token = tokenizer.nextToken();
if (token.c_str()[0] == '=')
@@ -124,9 +170,17 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li
else if (token.c_str()[0] == '<')
entry.criteriaOperator = Puzzle::LESS_THAN;
+ // There are supposed to be three tokens, but there is no
+ // guarantee that there will be a space between the second and
+ // the third one (bug #6774)
+ if (token.size() == 1) {
+ token = tokenizer.nextToken();
+ } else {
+ token.deleteChar(0);
+ }
+
// First determine if the last token is an id or a value
// Then parse it into 'argument'
- token = tokenizer.nextToken();
if (token.contains('[')) {
sscanf(token.c_str(), "[%u]", &(entry.argument));
entry.argumentIsAKey = true;
@@ -148,103 +202,154 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis
// Loop until we find the closing brace
Common::String line = stream.readLine();
trimCommentsAndWhiteSpace(&line);
+ line.toLowercase();
// TODO: Re-order the if-then statements in order of highest occurrence
while (!stream.eos() && !line.contains('}')) {
if (line.empty()) {
line = stream.readLine();
trimCommentsAndWhiteSpace(&line);
-
+ line.toLowercase();
continue;
}
- // Parse for the action type
- if (line.matchString("*:add*", true)) {
- actionList.push_back(new ActionAdd(line));
- } else if (line.matchString("*:animplay*", true)) {
- actionList.push_back(new ActionPlayAnimation(line));
- } else if (line.matchString("*:animpreload*", true)) {
- actionList.push_back(new ActionPreloadAnimation(line));
- } else if (line.matchString("*:animunload*", true)) {
- //actionList.push_back(new ActionUnloadAnimation(line));
- } else if (line.matchString("*:attenuate*", true)) {
- // TODO: Implement ActionAttenuate
- } else if (line.matchString("*:assign*", true)) {
- actionList.push_back(new ActionAssign(line));
- } else if (line.matchString("*:change_location*", true)) {
- actionList.push_back(new ActionChangeLocation(line));
- } else if (line.matchString("*:crossfade*", true)) {
- // TODO: Implement ActionCrossfade
- } else if (line.matchString("*:debug*", true)) {
- // TODO: Implement ActionDebug
- } else if (line.matchString("*:delay_render*", true)) {
- // TODO: Implement ActionDelayRender
- } else if (line.matchString("*:disable_control*", true)) {
- actionList.push_back(new ActionDisableControl(line));
- } else if (line.matchString("*:disable_venus*", true)) {
- // TODO: Implement ActionDisableVenus
- } else if (line.matchString("*:display_message*", true)) {
- // TODO: Implement ActionDisplayMessage
- } else if (line.matchString("*:dissolve*", true)) {
- // TODO: Implement ActionDissolve
- } else if (line.matchString("*:distort*", true)) {
- // TODO: Implement ActionDistort
- } else if (line.matchString("*:enable_control*", true)) {
- actionList.push_back(new ActionEnableControl(line));
- } else if (line.matchString("*:flush_mouse_events*", true)) {
- // TODO: Implement ActionFlushMouseEvents
- } else if (line.matchString("*:inventory*", true)) {
- // TODO: Implement ActionInventory
- } else if (line.matchString("*:kill*", true)) {
- // TODO: Implement ActionKill
- } else if (line.matchString("*:menu_bar_enable*", true)) {
- // TODO: Implement ActionMenuBarEnable
- } else if (line.matchString("*:music*", true)) {
- actionList.push_back(new ActionMusic(line));
- } else if (line.matchString("*:pan_track*", true)) {
- // TODO: Implement ActionPanTrack
- } else if (line.matchString("*:playpreload*", true)) {
- actionList.push_back(new ActionPlayPreloadAnimation(line));
- } else if (line.matchString("*:preferences*", true)) {
- // TODO: Implement ActionPreferences
- } else if (line.matchString("*:quit*", true)) {
- actionList.push_back(new ActionQuit());
- } else if (line.matchString("*:random*", true)) {
- actionList.push_back(new ActionRandom(line));
- } else if (line.matchString("*:region*", true)) {
- // TODO: Implement ActionRegion
- } else if (line.matchString("*:restore_game*", true)) {
- // TODO: Implement ActionRestoreGame
- } else if (line.matchString("*:rotate_to*", true)) {
- // TODO: Implement ActionRotateTo
- } else if (line.matchString("*:save_game*", true)) {
- // TODO: Implement ActionSaveGame
- } else if (line.matchString("*:set_partial_screen*", true)) {
- actionList.push_back(new ActionSetPartialScreen(line));
- } else if (line.matchString("*:set_screen*", true)) {
- actionList.push_back(new ActionSetScreen(line));
- } else if (line.matchString("*:set_venus*", true)) {
- // TODO: Implement ActionSetVenus
- } else if (line.matchString("*:stop*", true)) {
- // TODO: Implement ActionStop
- } else if (line.matchString("*:streamvideo*", true)) {
- actionList.push_back(new ActionStreamVideo(line));
- } else if (line.matchString("*:syncsound*", true)) {
- // TODO: Implement ActionSyncSound
- } else if (line.matchString("*:timer*", true)) {
- actionList.push_back(new ActionTimer(line));
- } else if (line.matchString("*:ttytext*", true)) {
- // TODO: Implement ActionTTYText
- } else if (line.matchString("*:universe_music*", true)) {
- // TODO: Implement ActionUniverseMusic
- } else if (line.matchString("*:copy_file*", true)) {
- // Not used. Purposely left empty
- } else {
- warning("Unhandled result action type: %s", line.c_str());
+ const char *chrs = line.c_str();
+ uint pos;
+ for (pos = 0; pos < line.size(); pos++)
+ if (chrs[pos] == ':')
+ break;
+
+ if (pos < line.size()) {
+
+ uint startpos = pos + 1;
+
+ for (pos = startpos; pos < line.size(); pos++)
+ if (chrs[pos] == ':' || chrs[pos] == '(')
+ break;
+
+ if (pos < line.size()) {
+ int32 slot = 11;
+ Common::String args = "";
+ Common::String act(chrs + startpos, chrs + pos);
+
+ startpos = pos + 1;
+
+ if (chrs[pos] == ':') {
+ for (pos = startpos; pos < line.size(); pos++)
+ if (chrs[pos] == '(')
+ break;
+ Common::String strSlot(chrs + startpos, chrs + pos);
+ slot = atoi(strSlot.c_str());
+
+ startpos = pos + 1;
+ }
+
+ if (pos < line.size()) {
+ for (pos = startpos; pos < line.size(); pos++)
+ if (chrs[pos] == ')')
+ break;
+
+ args = Common::String(chrs + startpos, chrs + pos);
+ }
+
+ // Parse for the action type
+ if (act.matchString("add", true)) {
+ actionList.push_back(new ActionAdd(_engine, slot, args));
+ } else if (act.matchString("animplay", true)) {
+ actionList.push_back(new ActionPlayAnimation(_engine, slot, args));
+ } else if (act.matchString("animpreload", true)) {
+ actionList.push_back(new ActionPreloadAnimation(_engine, slot, args));
+ } else if (act.matchString("animunload", true)) {
+ // Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
+ actionList.push_back(new ActionUnloadAnimation(_engine, slot, args));
+ } else if (act.matchString("attenuate", true)) {
+ actionList.push_back(new ActionAttenuate(_engine, slot, args));
+ } else if (act.matchString("assign", true)) {
+ actionList.push_back(new ActionAssign(_engine, slot, args));
+ } else if (act.matchString("change_location", true)) {
+ actionList.push_back(new ActionChangeLocation(_engine, slot, args));
+ } else if (act.matchString("crossfade", true)) {
+ actionList.push_back(new ActionCrossfade(_engine, slot, args));
+ } else if (act.matchString("cursor", true)) {
+ actionList.push_back(new ActionCursor(_engine, slot, args));
+ } else if (act.matchString("debug", true)) {
+ // Not used. Purposely left empty
+ } else if (act.matchString("delay_render", true)) {
+ actionList.push_back(new ActionDelayRender(_engine, slot, args));
+ } else if (act.matchString("disable_control", true)) {
+ actionList.push_back(new ActionDisableControl(_engine, slot, args));
+ } else if (act.matchString("disable_venus", true)) {
+ // Not used. Purposely left empty
+ } else if (act.matchString("display_message", true)) {
+ actionList.push_back(new ActionDisplayMessage(_engine, slot, args));
+ } else if (act.matchString("dissolve", true)) {
+ actionList.push_back(new ActionDissolve(_engine));
+ } else if (act.matchString("distort", true)) {
+ // Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30)
+ actionList.push_back(new ActionDistort(_engine, slot, args));
+ } else if (act.matchString("enable_control", true)) {
+ actionList.push_back(new ActionEnableControl(_engine, slot, args));
+ } else if (act.matchString("flush_mouse_events", true)) {
+ actionList.push_back(new ActionFlushMouseEvents(_engine, slot));
+ } else if (act.matchString("inventory", true)) {
+ actionList.push_back(new ActionInventory(_engine, slot, args));
+ } else if (act.matchString("kill", true)) {
+ // Only used by ZGI
+ actionList.push_back(new ActionKill(_engine, slot, args));
+ } else if (act.matchString("menu_bar_enable", true)) {
+ actionList.push_back(new ActionMenuBarEnable(_engine, slot, args));
+ } else if (act.matchString("music", true)) {
+ actionList.push_back(new ActionMusic(_engine, slot, args, false));
+ } else if (act.matchString("pan_track", true)) {
+ actionList.push_back(new ActionPanTrack(_engine, slot, args));
+ } else if (act.matchString("playpreload", true)) {
+ actionList.push_back(new ActionPlayPreloadAnimation(_engine, slot, args));
+ } else if (act.matchString("preferences", true)) {
+ actionList.push_back(new ActionPreferences(_engine, slot, args));
+ } else if (act.matchString("quit", true)) {
+ actionList.push_back(new ActionQuit(_engine, slot));
+ } else if (act.matchString("random", true)) {
+ actionList.push_back(new ActionRandom(_engine, slot, args));
+ } else if (act.matchString("region", true)) {
+ // Only used by Zork: Nemesis
+ actionList.push_back(new ActionRegion(_engine, slot, args));
+ } else if (act.matchString("restore_game", true)) {
+ // Only used by ZGI to load the restart game slot, r.svr.
+ // Used by the credits screen.
+ actionList.push_back(new ActionRestoreGame(_engine, slot, args));
+ } else if (act.matchString("rotate_to", true)) {
+ actionList.push_back(new ActionRotateTo(_engine, slot, args));
+ } else if (act.matchString("save_game", true)) {
+ // Not used. Purposely left empty
+ } else if (act.matchString("set_partial_screen", true)) {
+ actionList.push_back(new ActionSetPartialScreen(_engine, slot, args));
+ } else if (act.matchString("set_screen", true)) {
+ actionList.push_back(new ActionSetScreen(_engine, slot, args));
+ } else if (act.matchString("set_venus", true)) {
+ // Not used. Purposely left empty
+ } 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 +364,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 +378,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 +387,53 @@ 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;
+ // WORKAROUND for a script bug in ZGI: There is an invalid hotspot
+ // at scene em1h (bottom of tower), which points to a missing
+ // script em1n. This is a hotspot at the right of the screen.
+ // In the original, this hotspot doesn't lead anywhere anyway,
+ // so instead of moving to a missing scene, we just remove the
+ // hotspot altogether. The alternative would be to just process
+ // and ignore invalid scenes, but I don't think it's worth the
+ // effort. Fixes bug #6780.
+ if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 5653)
+ return NULL;
+ 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")) {
+ // Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
Control::parseTiltControl(_engine, stream);
- return;
+ return NULL;
+ } else if (controlType.equalsIgnoreCase("slot")) {
+ return new SlotControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("input")) {
+ return new InputControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("save")) {
+ return new SaveControl(_engine, key, stream);
} else if (controlType.equalsIgnoreCase("lever")) {
- _activeControls.push_back(new LeverControl(_engine, key, stream));
- return;
+ // Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
+ return new LeverControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("safe")) {
+ // Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
+ return new SafeControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("hotmovie")) {
+ // Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
+ return new HotMovControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("fist")) {
+ // Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
+ return new FistControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("paint")) {
+ // Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
+ return new PaintControl(_engine, key, stream);
+ } else if (controlType.equalsIgnoreCase("titler")) {
+ // Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde)
+ return new TitlerControl(_engine, key, stream);
}
+ return NULL;
}
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp
index 41b835e550..70eaab2a0a 100644
--- a/engines/zvision/scripting/script_manager.cpp
+++ b/engines/zvision/scripting/script_manager.cpp
@@ -1,24 +1,24 @@
/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
#include "common/scummsys.h"
@@ -26,172 +26,254 @@
#include "zvision/zvision.h"
#include "zvision/graphics/render_manager.h"
-#include "zvision/cursors/cursor_manager.h"
-#include "zvision/core/save_manager.h"
+#include "zvision/graphics/cursors/cursor_manager.h"
+#include "zvision/file/save_manager.h"
#include "zvision/scripting/actions.h"
-#include "zvision/utility/utility.h"
+#include "zvision/scripting/menu.h"
+#include "zvision/scripting/effects/timer_effect.h"
#include "common/algorithm.h"
#include "common/hashmap.h"
#include "common/debug.h"
#include "common/stream.h"
-
+#include "common/config-manager.h"
namespace ZVision {
ScriptManager::ScriptManager(ZVision *engine)
: _engine(engine),
- _currentlyFocusedControl(0) {
+ _currentlyFocusedControl(0),
+ _activeControls(NULL) {
}
ScriptManager::~ScriptManager() {
- for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) {
- delete (*iter);
- }
- for (PuzzleList::iterator iter = _globalPuzzles.begin(); iter != _globalPuzzles.end(); ++iter) {
- delete (*iter);
- }
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- delete (*iter);
- }
+ cleanScriptScope(universe);
+ cleanScriptScope(world);
+ cleanScriptScope(room);
+ cleanScriptScope(nodeview);
+ _controlEvents.clear();
}
void ScriptManager::initialize() {
- parseScrFile("universe.scr", true);
+ cleanScriptScope(universe);
+ cleanScriptScope(world);
+ cleanScriptScope(room);
+ cleanScriptScope(nodeview);
+
+ _currentLocation.node = 0;
+ _currentLocation.world = 0;
+ _currentLocation.room = 0;
+ _currentLocation.view = 0;
+
+ parseScrFile("universe.scr", universe);
changeLocation('g', 'a', 'r', 'y', 0);
+
+ _controlEvents.clear();
}
void ScriptManager::update(uint deltaTimeMillis) {
+ if (_currentLocation != _nextLocation) {
+ ChangeLocationReal(false);
+ }
+
updateNodes(deltaTimeMillis);
- checkPuzzleCriteria();
+ if (!execScope(nodeview)) {
+ return;
+ }
+ if (!execScope(room)) {
+ return;
+ }
+ if (!execScope(world)) {
+ return;
+ }
+ if (!execScope(universe)) {
+ return;
+ }
+ updateControls(deltaTimeMillis);
}
-void ScriptManager::createReferenceTable() {
- // Iterate through each local Puzzle
- for (PuzzleList::iterator activePuzzleIter = _activePuzzles.begin(); activePuzzleIter != _activePuzzles.end(); ++activePuzzleIter) {
- Puzzle *puzzlePtr = (*activePuzzleIter);
+bool ScriptManager::execScope(ScriptScope &scope) {
+ // Swap queues
+ PuzzleList *tmp = scope.execQueue;
+ scope.execQueue = scope.scopeQueue;
+ scope.scopeQueue = tmp;
+ scope.scopeQueue->clear();
- // 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);
+ for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
+ (*PuzzleIter)->addedBySetState = false;
+ }
- // If the argument is a key, add a reference to it as well
- if (entryIter->argumentIsAKey) {
- _referenceTable[entryIter->argument].push_back(puzzlePtr);
- }
+ if (scope.procCount < 2 || getStateValue(StateKey_ExecScopeStyle)) {
+ for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
+ if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) {
+ return false;
+ }
+ }
+ } else {
+ for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) {
+ if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) {
+ return false;
}
}
}
- // Iterate through each global Puzzle
- for (PuzzleList::iterator globalPuzzleIter = _globalPuzzles.begin(); globalPuzzleIter != _globalPuzzles.end(); ++globalPuzzleIter) {
- Puzzle *puzzlePtr = (*globalPuzzleIter);
-
- // 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 (scope.procCount < 2) {
+ scope.procCount++;
+ }
+ return true;
+}
- // If the argument is a key, add a reference to it as well
- if (entryIter->argumentIsAKey) {
- _referenceTable[entryIter->argument].push_back(puzzlePtr);
- }
+void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) {
+ if (_referenceTable.contains(key)) {
+ Common::Array<PuzzleRef> *arr = &_referenceTable[key];
+ for (uint32 i = 0; i < arr->size(); i++) {
+ if ((*arr)[i].puz == ref.puz) {
+ return;
}
}
}
- // Remove duplicate entries
- for (PuzzleMap::iterator referenceTableIter = _referenceTable.begin(); referenceTableIter != _referenceTable.end(); ++referenceTableIter) {
- removeDuplicateEntries(referenceTableIter->_value);
+ _referenceTable[key].push_back(ref);
+}
+
+void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) {
+ // Iterate through each local Puzzle
+ for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
+ Puzzle *puzzlePtr = (*PuzzleIter);
+
+ PuzzleRef ref;
+ ref.scope = &scope;
+ ref.puz = puzzlePtr;
+
+ referenceTableAddPuzzle(puzzlePtr->key, ref);
+
+ // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle
+ for (Common::List<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;
+ }
- // 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;
+ // Process only one event
+ if (!_controlEvents.empty()) {
+ Common::Event _event = _controlEvents.front();
+ Common::Point imageCoord;
+ switch (_event.type) {
+ case Common::EVENT_LBUTTONDOWN:
+ imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
+ onMouseDown(_event.mouse, imageCoord);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
+ onMouseUp(_event.mouse, imageCoord);
+ break;
+ case Common::EVENT_KEYDOWN:
+ onKeyDown(_event.kbd);
+ break;
+ case Common::EVENT_KEYUP:
+ onKeyUp(_event.kbd);
+ break;
+ default:
+ break;
}
+ _controlEvents.pop_front();
+ }
+
+ for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) {
+ if ((*iter)->process(deltaTimeMillis)) {
+ break;
+ }
+ }
+}
- // Check each Criteria
+bool 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)) {
+ return true;
+ }
- bool criteriaMet = false;
- for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) {
- criteriaMet = false;
+ // Check each Criteria
+ if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) {
+ return true;
+ }
- 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;
- }
+ 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);
- if (!shouldContinue) {
- break;
+ for (Common::List<ResultAction *>::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) {
+ if (!(*resultIter)->execute()) {
+ return false;
}
}
}
+
+ return true;
}
void ScriptManager::cleanStateTable() {
@@ -205,52 +287,102 @@ void ScriptManager::cleanStateTable() {
}
}
-uint ScriptManager::getStateValue(uint32 key) {
- if (_globalState.contains(key))
+void ScriptManager::cleanScriptScope(ScriptScope &scope) {
+ scope.privQueueOne.clear();
+ scope.privQueueTwo.clear();
+ scope.scopeQueue = &scope.privQueueOne;
+ scope.execQueue = &scope.privQueueTwo;
+ for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) {
+ delete(*iter);
+ }
+
+ scope.puzzles.clear();
+
+ for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) {
+ delete(*iter);
+ }
+
+ scope.controls.clear();
+
+ scope.procCount = 0;
+}
+
+int ScriptManager::getStateValue(uint32 key) {
+ if (_globalState.contains(key)) {
return _globalState[key];
- else
+ } 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<PuzzleRef> *arr = &_referenceTable[key];
+ for (int32 i = arr->size() - 1; i >= 0; i--) {
+ if (!(*arr)[i].puz->addedBySetState) {
+ (*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz);
+ (*arr)[i].puz->addedBySetState = true;
+ }
}
}
}
-uint ScriptManager::getStateFlags(uint32 key) {
- if (_globalStateFlags.contains(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
+ } 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) {
+ for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
if ((*iter)->getKey() == key) {
- return (*iter);
+ return *iter;
}
}
@@ -258,9 +390,15 @@ Control *ScriptManager::getControl(uint32 key) {
}
void ScriptManager::focusControl(uint32 key) {
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
+ if (!_activeControls) {
+ return;
+ }
+ if (_currentlyFocusedControl == key) {
+ return;
+ }
+ for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
uint32 controlKey = (*iter)->getKey();
-
+
if (controlKey == key) {
(*iter)->focus();
} else if (controlKey == _currentlyFocusedControl) {
@@ -271,167 +409,415 @@ void ScriptManager::focusControl(uint32 key) {
_currentlyFocusedControl = key;
}
+void ScriptManager::setFocusControlKey(uint32 key) {
+ _currentlyFocusedControl = key;
+}
+
+void ScriptManager::addSideFX(ScriptingEffect *fx) {
+ _activeSideFx.push_back(fx);
+}
+
+ScriptingEffect *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(ScriptingEffect::ScriptingEffectType 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;
}
- return cursorWasChanged;
+ for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) {
+ if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) {
+ return true;
+ }
+ }
+
+ return false;
}
void ScriptManager::onKeyDown(Common::KeyState keyState) {
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- (*iter)->onKeyDown(keyState);
+ if (!_activeControls) {
+ return;
+ }
+ for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
+ if ((*iter)->onKeyDown(keyState)) {
+ return;
+ }
}
}
void ScriptManager::onKeyUp(Common::KeyState keyState) {
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- (*iter)->onKeyUp(keyState);
+ if (!_activeControls) {
+ return;
+ }
+ for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
+ if ((*iter)->onKeyUp(keyState)) {
+ return;
+ }
}
}
-void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) {
- assert(world != 0);
- debug(1, "Changing location to: %c %c %c %c %u", world, room, node, view, offset);
+void ScriptManager::changeLocation(const Location &_newLocation) {
+ changeLocation(_newLocation.world, _newLocation.room, _newLocation.node, _newLocation.view, _newLocation.offset);
+}
- // Auto save
- _engine->getSaveManager()->autoSave();
+void ScriptManager::changeLocation(char _world, char _room, char _node, char _view, uint32 offset) {
+ _nextLocation.world = _world;
+ _nextLocation.room = _room;
+ _nextLocation.node = _node;
+ _nextLocation.view = _view;
+ _nextLocation.offset = offset;
+ // If next location is 0000, return to the previous location.
+ if (_nextLocation == "0000") {
+ if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') {
+ _nextLocation.world = getStateValue(StateKey_LastWorld);
+ _nextLocation.room = getStateValue(StateKey_LastRoom);
+ _nextLocation.node = getStateValue(StateKey_LastNode);
+ _nextLocation.view = getStateValue(StateKey_LastView);
+ _nextLocation.offset = getStateValue(StateKey_LastViewPos);
+ } else {
+ _nextLocation.world = getStateValue(StateKey_Menu_LastWorld);
+ _nextLocation.room = getStateValue(StateKey_Menu_LastRoom);
+ _nextLocation.node = getStateValue(StateKey_Menu_LastNode);
+ _nextLocation.view = getStateValue(StateKey_Menu_LastView);
+ _nextLocation.offset = getStateValue(StateKey_Menu_LastViewPos);
+ }
+ }
+}
- // Clear all the containers
- _referenceTable.clear();
- _puzzlesToCheck.clear();
- for (PuzzleList::iterator iter = _activePuzzles.begin(); iter != _activePuzzles.end(); ++iter) {
- delete (*iter);
+void ScriptManager::ChangeLocationReal(bool isLoading) {
+ assert(_nextLocation.world != 0);
+ debug(1, "Changing location to: %c %c %c %c %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset);
+
+ const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j');
+ const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j');
+ const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e');
+ const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e');
+
+ if (enteringMenu && !ConfMan.getBool("originalsaveload")) {
+ if (isSaveScreen || isRestoreScreen) {
+ // Hook up the ScummVM save/restore dialog
+ bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen);
+ if (!gameSavedOrLoaded || isSaveScreen) {
+ // Reload the current room
+ _nextLocation.world = _currentLocation.world;
+ _nextLocation.room = _currentLocation.room;
+ _nextLocation.node = _currentLocation.node;
+ _nextLocation.view = _currentLocation.view;
+ _nextLocation.offset = _currentLocation.offset;
+
+ return;
+ } else {
+ _currentLocation.world = 'g';
+ _currentLocation.room = '0';
+ _currentLocation.node = '0';
+ _currentLocation.view = '0';
+ _currentLocation.offset = 0;
+ }
+ }
}
- _activePuzzles.clear();
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- delete (*iter);
+
+ _engine->setRenderDelay(2);
+
+ if (!leavingMenu) {
+ if (!isLoading && !enteringMenu) {
+ 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));
+ }
}
- _activeControls.clear();
- // Revert to the idle cursor
- _engine->getCursorManager()->revertToIdle();
+ if (enteringMenu) {
+ if (isSaveScreen && !leavingMenu) {
+ _engine->getSaveManager()->prepareSaveBuffer();
+ }
+ } else {
+ if (leavingMenu) {
+ _engine->getSaveManager()->flushSaveBuffer();
+ }
+ }
- // Reset the background velocity
- _engine->getRenderManager()->setBackgroundVelocity(0);
+ 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);
- // Remove any alphaEntries
- _engine->getRenderManager()->clearAlphaEntries();
+ _referenceTable.clear();
+ addPuzzlesToReferenceTable(universe);
- // Clean the global state table
- cleanStateTable();
+ _engine->getMenuHandler()->setEnable(0xFFFF);
- // Parse into puzzles and controls
- Common::String fileName = Common::String::format("%c%c%c%c.scr", world, room, node, view);
- parseScrFile(fileName);
+ if (_nextLocation.world != _currentLocation.world) {
+ cleanScriptScope(nodeview);
+ cleanScriptScope(room);
+ cleanScriptScope(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);
+
+ fileName = Common::String::format("%c.scr", _nextLocation.world);
+ parseScrFile(fileName, world);
+ addPuzzlesToReferenceTable(world);
+ } else if (_nextLocation.room != _currentLocation.room) {
+ cleanScriptScope(nodeview);
+ cleanScriptScope(room);
+
+ addPuzzlesToReferenceTable(world);
+
+ Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view);
+ parseScrFile(fileName, nodeview);
+ addPuzzlesToReferenceTable(nodeview);
+
+ fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room);
+ parseScrFile(fileName, room);
+ addPuzzlesToReferenceTable(room);
+
+ } else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) {
+ cleanScriptScope(nodeview);
+
+ addPuzzlesToReferenceTable(room);
+ addPuzzlesToReferenceTable(world);
+
+ Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view);
+ parseScrFile(fileName, nodeview);
+ addPuzzlesToReferenceTable(nodeview);
}
- // 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);
- }
+ _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 == "0000") {
+ _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);
}
- // 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);
- }
+ _engine->getRenderManager()->checkBorders();
+}
- _puzzlesToCheck.push((*iter));
+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);
}
- // Create the puzzle reference table
- createReferenceTable();
+ stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G'));
- // Update _currentLocation
- _currentLocation.world = world;
- _currentLocation.room = room;
- _currentLocation.node = node;
- _currentLocation.view = view;
- _currentLocation.offset = offset;
-}
+ int32 slots = 20000;
+ if (_engine->getGameId() == GID_NEMESIS) {
+ slots = 30000;
+ }
-void ScriptManager::serializeStateTable(Common::WriteStream *stream) {
- // Write the number of state value entries
- stream->writeUint32LE(_globalState.size());
+ stream->writeUint32LE(slots * 2);
- for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) {
- // Write out the key/value pair
- stream->writeUint32LE(iter->_key);
- stream->writeUint32LE(iter->_value);
+ 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++;
- }
+ for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) {
+ delete(*iter);
}
- stream->writeUint32LE(numberOfControlsNeedingSerialization);
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- (*iter)->serialize(stream);
+ _activeSideFx.clear();
+
+ _referenceTable.clear();
+
+ if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) {
+ changeLocation('g', 'a', 'r', 'y', 0);
+ return;
}
-}
-void ScriptManager::deserializeControls(Common::SeekableReadStream *stream) {
- uint32 numberOfControls = stream->readUint32LE();
+ stream->seek(4, SEEK_CUR);
- for (uint32 i = 0; i < numberOfControls; ++i) {
- uint32 key = stream->readUint32LE();
- for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) {
- if ((*iter)->getKey() == key) {
- (*iter)->deserialize(stream);
- break;
+ if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) {
+ changeLocation('g', 'a', 'r', 'y', 0);
+ return;
+ }
+
+ Location nextLocation;
+
+ nextLocation.world = stream->readByte();
+ nextLocation.room = stream->readByte();
+ nextLocation.node = stream->readByte();
+ nextLocation.view = stream->readByte();
+ nextLocation.offset = stream->readUint32LE() & 0x0000FFFF;
+
+ while (stream->pos() < stream->size()) {
+ uint32 tag = stream->readUint32BE();
+ uint32 tagSize = stream->readUint32LE();
+ switch (tag) {
+ case MKTAG('T', 'I', 'M', 'R'): {
+ uint32 key = stream->readUint32LE();
+ uint32 time = stream->readUint32LE();
+ if (_engine->getGameId() == GID_GRANDINQUISITOR) {
+ time /= 100;
+ } else if (_engine->getGameId() == GID_NEMESIS) {
+ time /= 1000;
+ }
+ addSideFX(new TimerNode(_engine, key, time));
+ }
+ break;
+ case MKTAG('F', 'L', 'A', 'G'):
+ for (uint32 i = 0; i < tagSize / 2; i++) {
+ setStateFlagSilent(i, stream->readUint16LE());
+ }
+ break;
+ case MKTAG('P', 'U', 'Z', 'Z'):
+ for (uint32 i = 0; i < tagSize / 2; i++) {
+ setStateValueSilent(i, stream->readUint16LE());
}
+ break;
+ default:
+ stream->seek(tagSize, SEEK_CUR);
}
}
+
+ _nextLocation = nextLocation;
+
+ ChangeLocationReal(true);
+
+ _engine->setRenderDelay(10);
+ setStateValue(StateKey_RestoreFlag, 1);
+
+ _engine->loadSettings();
}
Location ScriptManager::getCurrentLocation() const {
@@ -441,4 +827,78 @@ Location ScriptManager::getCurrentLocation() const {
return location;
}
+Location ScriptManager::getLastLocation() {
+ Location location;
+ location.world = getStateValue(StateKey_LastWorld);
+ location.room = getStateValue(StateKey_LastRoom);
+ location.node = getStateValue(StateKey_LastNode);
+ location.view = getStateValue(StateKey_LastView);
+ location.offset = getStateValue(StateKey_LastViewPos);
+
+ return location;
+}
+
+Location ScriptManager::getLastMenuLocation() {
+ Location location;
+ location.world = getStateValue(StateKey_Menu_LastWorld);
+ location.room = getStateValue(StateKey_Menu_LastRoom);
+ location.node = getStateValue(StateKey_Menu_LastNode);
+ location.view = getStateValue(StateKey_Menu_LastView);
+ location.offset = getStateValue(StateKey_Menu_LastViewPos);
+
+ return location;
+}
+
+void ScriptManager::addEvent(Common::Event event) {
+ _controlEvents.push_back(event);
+}
+
+void ScriptManager::flushEvent(Common::EventType type) {
+ EventList::iterator it = _controlEvents.begin();
+ while (it != _controlEvents.end()) {
+
+ if ((*it).type == type) {
+ it = _controlEvents.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void ScriptManager::trimCommentsAndWhiteSpace(Common::String *string) const {
+ for (int i = string->size() - 1; i >= 0; i--) {
+ if ((*string)[i] == '#') {
+ string->erase(i);
+ }
+ }
+
+ string->trim();
+}
+
+ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue):
+ _scriptManager(scriptManager) {
+ value = 0;
+ slot = false;
+ const char *isSlot = strstr(slotValue, "[");
+ if (isSlot) {
+ slot = true;
+ value = atoi(isSlot + 1);
+ } else {
+ slot = false;
+ value = atoi(slotValue);
+ }
+}
+int16 ValueSlot::getValue() {
+ if (slot) {
+ if (value >= 0) {
+ return _scriptManager->getStateValue(value);
+ }
+ else {
+ return 0;
+ }
+ } else {
+ return value;
+ }
+}
+
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h
index 4d1b1f359b..7c276bf917 100644
--- a/engines/zvision/scripting/script_manager.h
+++ b/engines/zvision/scripting/script_manager.h
@@ -8,12 +8,12 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@@ -25,10 +25,11 @@
#include "zvision/scripting/puzzle.h"
#include "zvision/scripting/control.h"
+#include "zvision/scripting/scripting_effect.h"
#include "common/hashmap.h"
#include "common/queue.h"
-
+#include "common/events.h"
namespace Common {
class String;
@@ -39,6 +40,69 @@ namespace ZVision {
class ZVision;
+enum StateKey {
+ StateKey_World = 3,
+ StateKey_Room = 4,
+ StateKey_Node = 5,
+ StateKey_View = 6,
+ StateKey_ViewPos = 7,
+ StateKey_KeyPress = 8,
+ StateKey_InventoryItem = 9,
+ StateKey_LMouse = 10,
+ StateKey_NotSet = 11, // This key doesn't set
+ StateKey_Rounds = 12,
+ StateKey_Venus = 13,
+ StateKey_RMouse = 18,
+ StateKey_MenuState = 19,
+ StateKey_RestoreFlag = 20,
+ StateKey_Quitting = 39,
+ StateKey_LastWorld = 40,
+ StateKey_LastRoom = 41,
+ StateKey_LastNode = 42,
+ StateKey_LastView = 43,
+ StateKey_LastViewPos = 44,
+ StateKey_Menu_LastWorld = 45,
+ StateKey_Menu_LastRoom = 46,
+ StateKey_Menu_LastNode = 47,
+ StateKey_Menu_LastView = 48,
+ StateKey_Menu_LastViewPos = 49,
+ StateKey_KbdRotateSpeed = 50,
+ StateKey_Subtitles = 51,
+ StateKey_StreamSkipKey = 52,
+ StateKey_RotateSpeed = 53,
+ StateKey_Volume = 56,
+ StateKey_Qsound = 57,
+ StateKey_VenusEnable = 58,
+ StateKey_HighQuality = 59,
+ StateKey_VideoLineSkip = 65,
+ StateKey_Platform = 66,
+ StateKey_InstallLevel = 67,
+ StateKey_CountryCode = 68,
+ StateKey_CPU = 69,
+ StateKey_MovieCursor = 70,
+ StateKey_NoTurnAnim = 71,
+ StateKey_WIN958 = 72,
+ StateKey_ShowErrorDlg = 73,
+ StateKey_DebugCheats = 74,
+ StateKey_JapanFonts = 75,
+ StateKey_ExecScopeStyle = 76,
+ StateKey_Brightness = 77,
+ StateKey_MPEGMovies = 78,
+ StateKey_EF9_R = 91,
+ StateKey_EF9_G = 92,
+ StateKey_EF9_B = 93,
+ StateKey_EF9_Speed = 94,
+ StateKey_Inv_Cnt_Slot = 100,
+ StateKey_Inv_1_Slot = 101,
+ StateKey_Inv_49_Slot = 149,
+ // ZGI only
+ StateKey_Inv_TotalSlots = 150,
+ StateKey_Inv_StartSlot = 151,
+ StateKey_Spell_1 = 191,
+ StateKey_Active_Spell = 205,
+ StateKey_Reversed_Spellbooc = 206
+};
+
struct Location {
Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {}
@@ -49,68 +113,121 @@ struct Location {
uint32 offset;
};
-typedef Common::HashMap<uint32, Common::Array<Puzzle *> > PuzzleMap;
+inline bool operator==(const Location& lhs, const Location& rhs) {
+ return (
+ lhs.world == rhs.world &&
+ lhs.room == rhs.room &&
+ lhs.node == rhs.node &&
+ lhs.view == rhs.view
+ );
+}
+
+inline bool operator==(const Location& lhs, const char* rhs) {
+ Common::String lhsStr = Common::String::format("%c%c%c%c", lhs.world, lhs.room, lhs.node, lhs.view);
+ return lhsStr == rhs;
+}
+
+inline bool operator!=(const Location& lhs, const Location& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const Location& lhs, const char* rhs) {
+ return !(lhs == rhs);
+}
+
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<ScriptingEffect *> SideFXList;
+typedef Common::List<Common::Event> EventList;
class ScriptManager {
public:
ScriptManager(ZVision *engine);
~ScriptManager();
-public:
- enum StateFlags {
- ONCE_PER_INST = 0x01,
- DO_ME_NOW = 0x02, // Somewhat useless flag since anything that needs to be done immediately has no criteria
- DISABLED = 0x04
- };
-
private:
ZVision *_engine;
+
+ struct ScriptScope {
+ uint32 procCount;
+
+ PuzzleList *scopeQueue; // For adding puzzles to queue
+ PuzzleList *execQueue; // Switch to it when execute
+ PuzzleList privQueueOne;
+ PuzzleList privQueueTwo;
+
+ PuzzleList puzzles;
+ ControlList controls;
+ };
+
+ struct PuzzleRef {
+ Puzzle *puz;
+ ScriptScope *scope;
+ };
+
+ typedef Common::HashMap<uint32, Common::Array<PuzzleRef> > PuzzleMap;
+
/**
* Holds the global state variable. Do NOT directly modify this. Use the accessors and
* mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a
* particular state key are checked after the key is modified.
*/
StateMap _globalState;
- /**
- * Holds the flags for the global states. This is used to enable/disable puzzles and/or
- * controls as well as which puzzles should are allowed to be re-executed
- */
- StateFlagMap _globalStateFlags;
+ /** Holds execute flags */
+ StateMap _globalStateFlags;
/** References _globalState keys to Puzzles */
PuzzleMap _referenceTable;
- /** Holds the Puzzles that should be checked this frame */
- PuzzleQueue _puzzlesToCheck;
- /** Holds the currently active puzzles */
- PuzzleList _activePuzzles;
- /** Holds the global puzzles */
- PuzzleList _globalPuzzles;
/** Holds the currently active controls */
- ControlList _activeControls;
+ ControlList *_activeControls;
+
+ EventList _controlEvents;
+
+ ScriptScope universe;
+ ScriptScope world;
+ ScriptScope room;
+ ScriptScope nodeview;
+
+ /** Holds the currently active timers, musics, other */
+ SideFXList _activeSideFx;
Location _currentLocation;
+ Location _nextLocation;
uint32 _currentlyFocusedControl;
public:
void initialize();
void update(uint deltaTimeMillis);
+ void queuePuzzles(uint32 key);
- uint getStateValue(uint32 key);
- void setStateValue(uint32 key, uint value);
- void addToStateValue(uint32 key, uint valueToAdd);
+ int getStateValue(uint32 key);
+ void setStateValue(uint32 key, int value);
- uint getStateFlags(uint32 key);
- void setStateFlags(uint32 key, uint flags);
+ uint getStateFlag(uint32 key);
+ void setStateFlag(uint32 key, uint value);
+ void unsetStateFlag(uint32 key, uint value);
void addControl(Control *control);
Control *getControl(uint32 key);
+ void enableControl(uint32 key);
+ void disableControl(uint32 key);
+
void focusControl(uint32 key);
+ // Only change focus control without call focus/unfocus.
+ void setFocusControlKey(uint32 key);
+
+ void addSideFX(ScriptingEffect *fx);
+ ScriptingEffect *getSideFX(uint32 key);
+ void deleteSideFx(uint32 key);
+ void stopSideFx(uint32 key);
+ void killSideFx(uint32 key);
+ void killSideFxType(ScriptingEffect::ScriptingEffectType type);
+
+ void addEvent(Common::Event);
+ void flushEvent(Common::EventType type);
/**
* Called when LeftMouse is pushed.
@@ -147,32 +264,61 @@ public:
*/
void onKeyUp(Common::KeyState keyState);
+ /** Mark next location */
void changeLocation(char world, char room, char node, char view, uint32 offset);
+ void changeLocation(const Location &_newLocation);
- void serializeStateTable(Common::WriteStream *stream);
- void deserializeStateTable(Common::SeekableReadStream *stream);
- void serializeControls(Common::WriteStream *stream);
- void deserializeControls(Common::SeekableReadStream *stream);
+ void serialize(Common::WriteStream *stream);
+ void deserialize(Common::SeekableReadStream *stream);
Location getCurrentLocation() const;
+ Location getLastLocation();
+ Location getLastMenuLocation();
+
+ /**
+ * Removes any line comments using '#' as a sequence start.
+ * Then removes any trailing and leading 'whitespace' using String::trim()
+ * Note: String::trim uses isspace() to determine what is whitespace and what is not.
+ *
+ * @param string The string to modify. It is modified in place
+ */
+ void trimCommentsAndWhiteSpace(Common::String *string) const;
private:
- void createReferenceTable();
+ void referenceTableAddPuzzle(uint32 key, PuzzleRef ref);
+ void addPuzzlesToReferenceTable(ScriptScope &scope);
void updateNodes(uint deltaTimeMillis);
- void checkPuzzleCriteria();
+ void updateControls(uint deltaTimeMillis);
+ bool checkPuzzleCriteria(Puzzle *puzzle, uint counter);
void cleanStateTable();
+ void cleanScriptScope(ScriptScope &scope);
+ bool execScope(ScriptScope &scope);
+
+ /** Perform change location */
+ void ChangeLocationReal(bool isLoading);
+
+ int8 inventoryGetCount();
+ void inventorySetCount(int8 cnt);
+ int16 inventoryGetItem(int8 id);
+ void inventorySetItem(int8 id, int16 item);
+
+ void setStateFlagSilent(uint32 key, uint value);
+ void setStateValueSilent(uint32 key, int value);
-// TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it
public:
+ void inventoryAdd(int16 item);
+ void inventoryDrop(int16 item);
+ void inventoryCycle();
+
+private:
/**
* Parses a script file into triggers and events
*
* @param fileName Name of the .scr file
* @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes
*/
- void parseScrFile(const Common::String &fileName, bool isGlobal = false);
+ void parseScrFile(const Common::String &fileName, ScriptScope &scope);
-private:
/**
* Parses the stream into a Puzzle object
* Helper method for parseScrFile.
@@ -188,9 +334,10 @@ private:
*
* @param criteria Pointer to the Criteria object to fill
* @param stream Scr file stream
+ * @param key Puzzle key (for workarounds)
* @return Whether any criteria were read
*/
- bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const;
+ bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const;
/**
* Parses the stream into a ResultAction objects
@@ -216,9 +363,18 @@ 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 *scriptManager, const char *slotValue);
+ int16 getValue();
+private:
+ int16 value;
+ bool slot;
+ ScriptManager *_scriptManager;
+};
} // End of namespace ZVision
diff --git a/engines/zvision/scripting/scripting_effect.h b/engines/zvision/scripting/scripting_effect.h
new file mode 100644
index 0000000000..2a2153204f
--- /dev/null
+++ b/engines/zvision/scripting/scripting_effect.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 SCRIPTING_EFFECT_H_INCLUDED
+#define SCRIPTING_EFFECT_H_INCLUDED
+
+namespace Common {
+class SeekableReadStream;
+struct Point;
+class WriteStream;
+}
+
+namespace ZVision {
+
+class ZVision;
+
+/**
+ * The base class that represents effects created from Actions.
+ * This class is virtual.
+ *
+ * Detailed Description:
+ * A scene has Controls. By interacting with the controls, the user
+ * causes Actions to execute. Certain Actions create 'effects', for
+ * example, a sound or an animation. This is the base class for
+ * those effects.
+ */
+class ScriptingEffect {
+public:
+
+ enum ScriptingEffectType {
+ SCRIPTING_EFFECT_ANIM = 1,
+ SCRIPTING_EFFECT_AUDIO = 2,
+ SCRIPTING_EFFECT_DISTORT = 4,
+ SCRIPTING_EFFECT_PANTRACK = 8,
+ SCRIPTING_EFFECT_REGION = 16,
+ SCRIPTING_EFFECT_TIMER = 32,
+ SCRIPTING_EFFECT_TTYTXT = 64,
+ SCRIPTING_EFFECT_UNKNOWN = 128,
+ SCRIPTING_EFFECT_ALL = 255
+ };
+
+ ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {}
+ ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {}
+ virtual ~ScriptingEffect() {}
+
+ uint32 getKey() {
+ return _key;
+ }
+ ScriptingEffectType 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;
+ ScriptingEffectType _type;
+
+// Static member functions
+public:
+
+};
+} // End of namespace ZVision
+
+#endif // SCRIPTING_EFFECT_H_INCLUDED
diff --git a/engines/zvision/sound/midi.cpp b/engines/zvision/sound/midi.cpp
new file mode 100644
index 0000000000..3dd66ff2d4
--- /dev/null
+++ b/engines/zvision/sound/midi.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/textconsole.h"
+
+#include "zvision/sound/midi.h"
+
+namespace ZVision {
+
+MidiManager::MidiManager() {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
+ _driver = MidiDriver::createMidi(dev);
+ if (_driver->open())
+ warning("Can't open MIDI, no MIDI output!");
+}
+
+MidiManager::~MidiManager() {
+ stop();
+ _driver->close();
+ delete _driver;
+}
+
+void MidiManager::stop() {
+ for (int8 i = 0; i < 16; i++)
+ if (_playChannels[i].playing)
+ noteOff(i);
+}
+
+void MidiManager::noteOn(int8 channel, int8 note, int8 velocity) {
+ assert(channel <= 15);
+
+ _playChannels[channel].playing = true;
+ _playChannels[channel].note = note;
+ _driver->send(channel | (velocity << 16) | (note << 8) | 0x90);
+}
+
+void MidiManager::noteOff(int8 channel) {
+ assert(channel <= 15);
+
+ if (_playChannels[channel].playing) {
+ _playChannels[channel].playing = false;
+ _driver->send(channel | (_playChannels[channel].note << 8) | 0x80);
+ }
+}
+
+int8 MidiManager::getFreeChannel() {
+ for (int8 i = 0; i < 16; i++)
+ if (!_playChannels[i].playing)
+ return i;
+ return -1;
+}
+
+void MidiManager::setPan(int8 channel, int8 pan) {
+ assert(channel <= 15);
+
+ _driver->send(channel | (pan << 16) | 0xAB0);
+}
+
+void MidiManager::setVolume(int8 channel, int8 volume) {
+ assert(channel <= 15);
+
+ _driver->send(channel | (volume << 16) | 0x7B0);
+}
+
+void MidiManager::setProgram(int8 channel, int8 prog) {
+ assert(channel <= 15);
+
+ _driver->send(channel | (prog << 8) | 0xC0);
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/inventory/inventory_manager.h b/engines/zvision/sound/midi.h
index f9d2ff294a..a3bac19636 100644
--- a/engines/zvision/inventory/inventory_manager.h
+++ b/engines/zvision/sound/midi.h
@@ -20,9 +20,40 @@
*
*/
-#ifndef ZVISION_INVENTORY_MANAGER_H
-#define ZVISION_INVENTORY_MANAGER_H
+#ifndef ZVISION_MIDI_H
+#define ZVISION_MIDI_H
-// TODO: Implement InventoryManager
+#include "audio/mididrv.h"
+
+namespace ZVision {
+
+class MidiManager {
+public:
+ MidiManager();
+ ~MidiManager();
+
+ void stop();
+ void noteOn(int8 channel, int8 noteNumber, int8 velocity);
+ void noteOff(int8 channel);
+ void setPan(int8 channel, int8 pan);
+ void setVolume(int8 channel, int8 volume);
+ void setProgram(int8 channel, int8 prog);
+
+ int8 getFreeChannel();
+
+protected:
+
+ struct chan {
+ bool playing;
+ int8 note;
+
+ chan() : playing(false), note(0) {};
+ };
+
+ MidiDriver *_driver;
+ chan _playChannels[16];
+};
+
+}
#endif
diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp
index edee1fd16e..7bdd4875fc 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.
@@ -21,85 +21,78 @@
*/
#include "common/scummsys.h"
-
-#include "zvision/sound/zork_raw.h"
-
-#include "zvision/zvision.h"
-#include "zvision/detection.h"
-#include "zvision/utility/utility.h"
-
#include "common/file.h"
#include "common/str.h"
#include "common/stream.h"
#include "common/memstream.h"
#include "common/bufferedstream.h"
#include "common/util.h"
-
+#include "common/tokenizer.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
+#include "zvision/sound/zork_raw.h"
+#include "zvision/zvision.h"
+#include "zvision/detection.h"
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 +133,90 @@ 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,69 +225,78 @@ 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);
return new RawZorkStream(rate, stereo, disposeAfterUse, stream);
}
-Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size,
- 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));
+ Common::String actualName = filePath;
+ bool found = engine->getSearchManager()->openFile(*file, actualName);
+ bool isRaw = actualName.hasSuffix(".raw");
+
+ if ((!found && isRaw) || (found && isRaw && file->size() < 10)) {
+ if (found)
+ file->close();
+
+ // Check for an audio patch (.src)
+ actualName.setChar('s', actualName.size() - 3);
+ actualName.setChar('r', actualName.size() - 2);
+ actualName.setChar('c', actualName.size() - 1);
+
+ if (!engine->getSearchManager()->openFile(*file, actualName))
+ return NULL;
+ } else if (!found && !isRaw) {
+ return NULL;
+ }
+
+ // Get the file name
+ Common::StringTokenizer tokenizer(actualName, "/\\");
+ Common::String fileName;
+ while (!tokenizer.empty()) {
+ fileName = tokenizer.nextToken();
+ }
- 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];
+ const SoundParams *soundParams = NULL;
if (engine->getGameId() == GID_NEMESIS) {
- for (uint i = 0; i < ARRAYSIZE(RawZorkStream::_zNemSoundParamLookupTable); ++i) {
- if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == fileIdentifier) {
- soundParams = RawZorkStream::_zNemSoundParamLookupTable[i];
- foundParams = true;
- }
+ for (int i = 0; i < 32; ++i) {
+ if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == (fileName[6]))
+ soundParams = &RawZorkStream::_zNemSoundParamLookupTable[i];
}
} else if (engine->getGameId() == GID_GRANDINQUISITOR) {
- for (uint i = 0; i < ARRAYSIZE(RawZorkStream::_zgiSoundParamLookupTable); ++i) {
- if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == fileIdentifier) {
- soundParams = RawZorkStream::_zgiSoundParamLookupTable[i];
- foundParams = true;
- }
+ for (int i = 0; i < 24; ++i) {
+ if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == (fileName[7]))
+ soundParams = &RawZorkStream::_zgiSoundParamLookupTable[i];
}
}
- if (!foundParams)
- error("Unable to find sound params for file '%s'. File identifier is '%c'", filePath.c_str(), fileIdentifier);
+ if (soundParams == NULL)
+ return NULL;
- if (soundParams.packed) {
- return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams.rate, soundParams.stereo, DisposeAfterUse::YES);
+ if (soundParams->packed) {
+ return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams->rate, soundParams->stereo, DisposeAfterUse::YES);
} else {
byte flags = 0;
- if (soundParams.stereo)
+ if (soundParams->bits16)
+ flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
+ if (soundParams->stereo)
flags |= Audio::FLAG_STEREO;
- return Audio::makeRawStream(file, soundParams.rate, flags, DisposeAfterUse::YES);
+ return Audio::makeRawStream(file, soundParams->rate, flags, DisposeAfterUse::YES);
}
}
diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h
index ef98e3e1ef..892bad4d5f 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.
@@ -25,7 +25,6 @@
#include "audio/audiostream.h"
-
namespace Common {
class SeekableReadStream;
}
@@ -39,27 +38,19 @@ 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,30 +66,61 @@ private:
static const int32 _amplitudeLookupTable[89];
public:
- int readBuffer(int16 *buffer, const int numSamples);
- bool isStereo() const { return true; }
- bool endOfData() const { return _endOfData; }
+ struct RawChunk {
+ int16 *data;
+ uint32 size;
+ };
- int getRate() const { return _rate; }
- Audio::Timestamp getLength() const { return _playtime; }
-
- bool rewind();
+ 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);
};
/**
- * Creates an audio stream, which plays from the given buffer.
- *
- * @param buffer Buffer to play from.
- * @param size Size of the buffer in bytes.
- * @param rate Rate of the sound data.
- * @param dispose AfterUse Whether to free the buffer after use (with free!).
- * @return The new SeekableAudioStream (or 0 on failure).
+ * This is a stream, which allows for playing raw ADPCM data from a stream.
*/
-Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size,
- int rate,
- bool stereo,
- DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+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 _stereo;
+ }
+ bool endOfData() const {
+ return _endOfData;
+ }
+
+ int getRate() const {
+ return _rate;
+ }
+ Audio::Timestamp getLength() const {
+ return _playtime;
+ }
+
+ bool rewind();
+};
/**
* Creates an audio stream, which plays from the given stream.
@@ -109,9 +131,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/strings/string_manager.cpp b/engines/zvision/strings/string_manager.cpp
deleted file mode 100644
index 22331d8a24..0000000000
--- a/engines/zvision/strings/string_manager.cpp
+++ /dev/null
@@ -1,255 +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/strings/string_manager.h"
-
-#include "zvision/fonts/truetype_font.h"
-
-#include "common/file.h"
-#include "common/tokenizer.h"
-#include "common/debug.h"
-
-#include "graphics/fontman.h"
-#include "graphics/colormasks.h"
-
-
-namespace ZVision {
-
-StringManager::StringManager(ZVision *engine)
- : _engine(engine) {
-}
-
-StringManager::~StringManager() {
- for (Common::HashMap<Common::String, TruetypeFont *>::iterator iter = _fonts.begin(); iter != _fonts.end(); ++iter) {
- delete iter->_value;
- }
-}
-
-void StringManager::initialize(ZVisionGameId gameId) {
- if (gameId == GID_NEMESIS) {
- // TODO: Check this hardcoded filename against all versions of Nemesis
- parseStrFile("nemesis.str");
- } else if (gameId == GID_GRANDINQUISITOR) {
- // TODO: Check this hardcoded filename against all versions of Grand Inquisitor
- parseStrFile("inquis.str");
- }
-}
-
-void StringManager::parseStrFile(const Common::String &fileName) {
- Common::File file;
- if (!file.open(fileName)) {
- warning("%s does not exist. String parsing failed", fileName.c_str());
- return;
- }
-
- uint lineNumber = 0;
- while (!file.eos()) {
- _lastStyle.align = Graphics::kTextAlignLeft;
- _lastStyle.color = 0;
- _lastStyle.font = nullptr;
-
- Common::String asciiLine = readWideLine(file);
- if (asciiLine.empty()) {
- continue;
- }
-
- char tagString[150];
- uint tagStringCursor = 0;
- char textString[150];
- uint textStringCursor = 0;
- bool inTag = false;
-
- for (uint i = 0; i < asciiLine.size(); ++i) {
- switch (asciiLine[i]) {
- case '<':
- inTag = true;
- if (!_inGameText[lineNumber].fragments.empty()) {
- _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
- textStringCursor = 0;
- }
- break;
- case '>':
- inTag = false;
- parseTag(Common::String(tagString, tagStringCursor), lineNumber);
- tagStringCursor = 0;
- break;
- default:
- if (inTag) {
- tagString[tagStringCursor] = asciiLine[i];
- tagStringCursor++;
- } else {
- textString[textStringCursor] = asciiLine[i];
- textStringCursor++;
- }
- break;
- }
- }
-
- if (textStringCursor > 0) {
- _inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
- }
-
- lineNumber++;
- assert(lineNumber <= NUM_TEXT_LINES);
- }
-}
-
-void StringManager::parseTag(const Common::String &tagString, uint lineNumber) {
- Common::StringTokenizer tokenizer(tagString);
-
- Common::String token = tokenizer.nextToken();
-
- Common::String fontName;
- bool bold = false;
- Graphics::TextAlign align = _lastStyle.align;
- int point = _lastStyle.font != nullptr ? _lastStyle.font->_fontHeight : 12;
- int red = 0;
- int green = 0;
- int blue = 0;
-
- while (!token.empty()) {
- if (token.matchString("font", true)) {
- fontName = tokenizer.nextToken();
- } else if (token.matchString("bold", true)) {
- token = tokenizer.nextToken();
- if (token.matchString("on", false)) {
- bold = true;
- }
- } else if (token.matchString("justify", true)) {
- token = tokenizer.nextToken();
- if (token.matchString("center", false)) {
- align = Graphics::kTextAlignCenter;
- } else if (token.matchString("right", false)) {
- align = Graphics::kTextAlignRight;
- }
- } else if (token.matchString("point", true)) {
- point = atoi(tokenizer.nextToken().c_str());
- } else if (token.matchString("red", true)) {
- red = atoi(tokenizer.nextToken().c_str());
- } else if (token.matchString("green", true)) {
- green = atoi(tokenizer.nextToken().c_str());
- } else if (token.matchString("blue", true)) {
- blue = atoi(tokenizer.nextToken().c_str());
- }
-
- token = tokenizer.nextToken();
- }
-
- TextFragment fragment;
-
- if (fontName.empty()) {
- fragment.style.font = _lastStyle.font;
- } else {
- Common::String newFontName;
- if (fontName.matchString("*times new roman*", true)) {
- if (bold) {
- newFontName = "timesbd.ttf";
- } else {
- newFontName = "times.ttf";
- }
- } else if (fontName.matchString("*courier new*", true)) {
- if (bold) {
- newFontName = "courbd.ttf";
- } else {
- newFontName = "cour.ttf";
- }
- } else if (fontName.matchString("*century schoolbook*", true)) {
- if (bold) {
- newFontName = "censcbkbd.ttf";
- } else {
- newFontName = "censcbk.ttf";
- }
- } else if (fontName.matchString("*garamond*", true)) {
- if (bold) {
- newFontName = "garabd.ttf";
- } else {
- newFontName = "gara.ttf";
- }
- } else {
- debug("Could not identify font: %s. Reverting to Arial", fontName.c_str());
- if (bold) {
- newFontName = "zorknorm.ttf";
- } else {
- newFontName = "arial.ttf";
- }
- }
-
- Common::String fontKey = Common::String::format("%s-%d", newFontName.c_str(), point);
- if (_fonts.contains(fontKey)) {
- fragment.style.font = _fonts[fontKey];
- } else {
- fragment.style.font = new TruetypeFont(_engine, point);
- fragment.style.font->loadFile(newFontName);
- _fonts[fontKey] = fragment.style.font;
- }
- }
-
- fragment.style.align = align;
- fragment.style.color = Graphics::ARGBToColor<Graphics::ColorMasks<565> >(0, red, green, blue);
- _inGameText[lineNumber].fragments.push_back(fragment);
-
- _lastStyle = fragment.style;
-}
-
-Common::String StringManager::readWideLine(Common::SeekableReadStream &stream) {
- Common::String asciiString;
-
- // Don't spam the user with warnings about UTF-16 support.
- // Just do one warning per String
- bool charOverflowWarning = false;
-
- uint16 value = stream.readUint16LE();
- while (!stream.eos()) {
- // 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 > 255) {
- charOverflowWarning = true;
- value = '?';
- }
- char charValue = (char)value;
-
- asciiString += charValue;
-
- value = stream.readUint16LE();
- }
-
- if (charOverflowWarning) {
- warning("UTF-16 is not supported. Characters greater than 255 are replaced with '?'");
- }
-
- return asciiString;
-}
-
-StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) {
- return _inGameText[stringNumber].fragments.front().style;
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/text/string_manager.cpp b/engines/zvision/text/string_manager.cpp
new file mode 100644
index 0000000000..c62e18f4b0
--- /dev/null
+++ b/engines/zvision/text/string_manager.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/tokenizer.h"
+#include "common/debug.h"
+#include "graphics/fontman.h"
+#include "graphics/colormasks.h"
+
+#include "zvision/zvision.h"
+#include "zvision/file/search_manager.h"
+#include "zvision/text/string_manager.h"
+#include "zvision/text/text.h"
+
+namespace ZVision {
+
+StringManager::StringManager(ZVision *engine)
+ : _engine(engine) {
+}
+
+StringManager::~StringManager() {
+
+}
+
+void StringManager::initialize(ZVisionGameId gameId) {
+ if (gameId == GID_NEMESIS)
+ loadStrFile("nemesis.str");
+ else if (gameId == GID_GRANDINQUISITOR)
+ loadStrFile("inquis.str");
+}
+
+void StringManager::loadStrFile(const Common::String &fileName) {
+ Common::File file;
+ if (!_engine->getSearchManager()->openFile(file, fileName))
+ error("%s does not exist. String parsing failed", fileName.c_str());
+
+ uint lineNumber = 0;
+ while (!file.eos()) {
+ _lines[lineNumber] = readWideLine(file);
+
+ lineNumber++;
+ assert(lineNumber <= NUM_TEXT_LINES);
+ }
+}
+
+const Common::String StringManager::getTextLine(uint stringNumber) {
+ return _lines[stringNumber];
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/strings/string_manager.h b/engines/zvision/text/string_manager.h
index af8324b890..f4564ee1ec 100644
--- a/engines/zvision/strings/string_manager.h
+++ b/engines/zvision/text/string_manager.h
@@ -24,8 +24,7 @@
#define ZVISION_STRING_MANAGER_H
#include "zvision/detection.h"
-#include "zvision/fonts/truetype_font.h"
-
+#include "zvision/text/truetype_font.h"
namespace Graphics {
class FontManager;
@@ -41,42 +40,28 @@ public:
~StringManager();
public:
- struct TextStyle {
- TruetypeFont *font;
- uint16 color; // In RBG 565
- Graphics::TextAlign align;
- };
-
- struct TextFragment {
- TextStyle style;
- Common::String text;
+ enum {
+ ZVISION_STR_SAVEEXIST = 23,
+ ZVISION_STR_SAVED = 4,
+ ZVISION_STR_SAVEEMPTY = 21,
+ ZVISION_STR_EXITPROMT = 6
};
private:
- struct InGameText {
- Common::List<TextFragment> fragments;
- };
-
enum {
NUM_TEXT_LINES = 56 // Max number of lines in a .str file. We hardcode this number because we know ZNem uses 42 strings and ZGI uses 56
};
private:
ZVision *_engine;
- InGameText _inGameText[NUM_TEXT_LINES];
- Common::HashMap<Common::String, TruetypeFont *> _fonts;
-
- TextStyle _lastStyle;
+ Common::String _lines[NUM_TEXT_LINES];
public:
void initialize(ZVisionGameId gameId);
- StringManager::TextStyle getTextStyle(uint stringNumber);
+ const Common::String getTextLine(uint stringNumber);
private:
- void parseStrFile(const Common::String &fileName);
- void parseTag(const Common::String &tagString, uint lineNumber);
-
- static Common::String readWideLine(Common::SeekableReadStream &stream);
+ void loadStrFile(const Common::String &fileName);
};
} // End of namespace ZVision
diff --git a/engines/zvision/text/subtitles.cpp b/engines/zvision/text/subtitles.cpp
new file mode 100644
index 0000000000..ffc9e2b808
--- /dev/null
+++ b/engines/zvision/text/subtitles.cpp
@@ -0,0 +1,115 @@
+/* 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/graphics/render_manager.h"
+#include "zvision/text/subtitles.h"
+#include "zvision/file/search_manager.h"
+#include "zvision/text/text.h"
+
+namespace ZVision {
+
+Subtitle::Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires) :
+ _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);
+ if (upscaleToHires)
+ _engine->getRenderManager()->upscaleRect(rct);
+ _areaId = _engine->getRenderManager()->createSubArea(rct);
+ } else if (str.matchString("*TextFile*", true)) {
+ char filename[64];
+ sscanf(str.c_str(), "%*[^:]:%s", filename);
+ Common::File txt;
+ if (_engine->getSearchManager()->openFile(txt, filename)) {
+ while (!txt.eos()) {
+ Common::String txtline = readWideLine(txt);
+ sub curSubtitle;
+ curSubtitle.start = -1;
+ curSubtitle.stop = -1;
+ curSubtitle.subStr = txtline;
+
+ _subs.push_back(curSubtitle);
+ }
+ txt.close();
+ }
+ } else {
+ int32 st;
+ int32 en;
+ int32 sb;
+ if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) {
+ if (sb <= (int32)_subs.size()) {
+ if (upscaleToHires) {
+ // Convert from 15FPS (AVI) to 29.97FPS (VOB)
+ st = st * 2997 / 1500;
+ en = en * 2997 / 1500;
+ }
+ _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].subStr.size())
+ if (_areaId != -1)
+ _engine->getRenderManager()->updateSubArea(_areaId, _subs[j].subStr);
+ _subId = j;
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/subtitles/subtitles.h b/engines/zvision/text/subtitles.h
index 776ddd3a97..329339be55 100644
--- a/engines/zvision/subtitles/subtitles.h
+++ b/engines/zvision/text/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, bool upscaleToHires = false);
+ ~Subtitle();
+
+ void process(int32 time);
+private:
+ ZVision *_engine;
+ int32 _areaId;
+ int16 _subId;
+
+ struct sub {
+ int start;
+ int stop;
+ Common::String subStr;
+ };
+
+ Common::Array<sub> _subs;
+};
+
+}
#endif
diff --git a/engines/zvision/text/text.cpp b/engines/zvision/text/text.cpp
new file mode 100644
index 0000000000..868ee4f1ae
--- /dev/null
+++ b/engines/zvision/text/text.cpp
@@ -0,0 +1,578 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/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/text/text.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/text/truetype_font.h"
+#include "zvision/scripting/script_manager.h"
+
+namespace ZVision {
+
+TextStyleState::TextStyleState() {
+ _fontname = "Arial";
+ _blue = 255;
+ _green = 255;
+ _red = 255;
+ _bold = false;
+#if 0
+ _newline = false;
+ _escapement = 0;
+#endif
+ _italic = false;
+ _justification = TEXT_JUSTIFY_LEFT;
+ _size = 12;
+#if 0
+ _skipcolor = false;
+#endif
+ _strikeout = false;
+ _underline = false;
+ _statebox = 0;
+ _sharp = false;
+}
+
+TextChange TextStyleState::parseStyle(const Common::String &str, int16 len) {
+ Common::String buf = Common::String(str.c_str(), len);
+
+ uint retval = TEXT_CHANGE_NONE;
+
+ 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 |= TEXT_CHANGE_FONT_TYPE;
+
+ } else if (token.matchString("blue", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ int32 tmp = atoi(token.c_str());
+ if (_blue != tmp) {
+ _blue = tmp;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ } else if (token.matchString("red", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ int32 tmp = atoi(token.c_str());
+ if (_red != tmp) {
+ _red = tmp;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ } else if (token.matchString("green", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ int32 tmp = atoi(token.c_str());
+ if (_green != tmp) {
+ _green = tmp;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ } else if (token.matchString("newline", true)) {
+#if 0
+ if ((retval & TXT_RET_NEWLN) == 0)
+ _newline = 0;
+
+ _newline++;
+#endif
+ retval |= TEXT_CHANGE_NEWLINE;
+ } else if (token.matchString("point", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ int32 tmp = atoi(token.c_str());
+ if (_size != tmp) {
+ _size = tmp;
+ retval |= TEXT_CHANGE_FONT_TYPE;
+ }
+ }
+ } else if (token.matchString("escapement", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+#if 0
+ int32 tmp = atoi(token.c_str());
+ _escapement = tmp;
+#endif
+ }
+ } else if (token.matchString("italic", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ if (token.matchString("on", true)) {
+ if (_italic != true) {
+ _italic = true;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ } else if (token.matchString("off", true)) {
+ if (_italic != false) {
+ _italic = false;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ }
+ } else if (token.matchString("underline", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ if (token.matchString("on", true)) {
+ if (_underline != true) {
+ _underline = true;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ } else if (token.matchString("off", true)) {
+ if (_underline != false) {
+ _underline = false;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ }
+ } else if (token.matchString("strikeout", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ if (token.matchString("on", true)) {
+ if (_strikeout != true) {
+ _strikeout = true;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ } else if (token.matchString("off", true)) {
+ if (_strikeout != false) {
+ _strikeout = false;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ }
+ } else if (token.matchString("bold", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ if (token.matchString("on", true)) {
+ if (_bold != true) {
+ _bold = true;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ } else if (token.matchString("off", true)) {
+ if (_bold != false) {
+ _bold = false;
+ retval |= TEXT_CHANGE_FONT_STYLE;
+ }
+ }
+ }
+ } else if (token.matchString("skipcolor", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+#if 0
+ if (token.matchString("on", true)) {
+ _skipcolor = true;
+ } else if (token.matchString("off", true)) {
+ _skipcolor = false;
+ }
+#endif
+ }
+ } 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 |= TEXT_CHANGE_HAS_STATE_BOX;
+ }
+ } else if (token.matchString("justify", true)) {
+ if (!tokenizer.empty()) {
+ token = tokenizer.nextToken();
+ if (token.matchString("center", true))
+ _justification = TEXT_JUSTIFY_CENTER;
+ else if (token.matchString("left", true))
+ _justification = TEXT_JUSTIFY_LEFT;
+ else if (token.matchString("right", true))
+ _justification = TEXT_JUSTIFY_RIGHT;
+ }
+ }
+ }
+ return (TextChange)retval;
+}
+
+void TextStyleState::readAllStyles(const Common::String &txt) {
+ int16 startTextPosition = -1;
+ int16 endTextPosition = -1;
+
+ for (uint16 i = 0; i < txt.size(); i++) {
+ if (txt[i] == '<')
+ startTextPosition = i;
+ else if (txt[i] == '>') {
+ endTextPosition = i;
+ if (startTextPosition != -1) {
+ if ((endTextPosition - startTextPosition - 1) > 0) {
+ parseStyle(Common::String(txt.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1);
+ }
+ }
+ }
+
+ }
+}
+
+void TextStyleState::updateFontWithTextState(StyledTTFont &font) {
+ uint tempStyle = 0;
+
+ if (_bold) {
+ tempStyle |= StyledTTFont::TTF_STYLE_BOLD;
+ }
+ if (_italic) {
+ tempStyle |= StyledTTFont::TTF_STYLE_ITALIC;
+ }
+ if (_underline) {
+ tempStyle |= StyledTTFont::TTF_STYLE_UNDERLINE;
+ }
+ if (_strikeout) {
+ tempStyle |= StyledTTFont::TTF_STYLE_STRIKETHROUGH;
+ }
+ if (_sharp) {
+ tempStyle |= StyledTTFont::TTF_STYLE_SHARP;
+ }
+
+ font.loadFont(_fontname, _size, tempStyle);
+}
+
+void TextRenderer::drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification justify) {
+ if (justify == TEXT_JUSTIFY_LEFT)
+ font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignLeft);
+ else if (justify == TEXT_JUSTIFY_CENTER)
+ font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignCenter);
+ else if (justify == TEXT_JUSTIFY_RIGHT)
+ font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignRight);
+}
+
+int32 TextRenderer::drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest) {
+ StyledTTFont font(_engine);
+ state.updateFontWithTextState(font);
+
+ uint32 color = _engine->_resourcePixelFormat.RGBToColor(state._red, state._green, state._blue);
+ drawTextWithJustification(text, font, color, dest, 0, state._justification);
+
+ return font.getStringWidth(text);
+}
+
+struct TextSurface {
+ TextSurface(Graphics::Surface *surface, Common::Point surfaceOffset, uint lineNumber)
+ : _surface(surface),
+ _surfaceOffset(surfaceOffset),
+ _lineNumber(lineNumber) {
+ }
+
+ Graphics::Surface *_surface;
+ Common::Point _surfaceOffset;
+ uint _lineNumber;
+};
+
+void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest) {
+ Common::Array<TextSurface> textSurfaces;
+ Common::Array<uint> lineWidths;
+ Common::Array<TextJustification> lineJustifications;
+
+ // Create the initial text state
+ TextStyleState currentState;
+
+ // Create an empty font and bind it to the state
+ StyledTTFont font(_engine);
+ currentState.updateFontWithTextState(font);
+
+ Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words
+ Common::String currentWord;
+ int sentenceWidth = 0;
+ int wordWidth = 0;
+ int lineWidth = 0;
+ int lineHeight = font.getFontHeight();
+
+ uint currentLineNumber = 0u;
+
+ uint numSpaces = 0u;
+ int spaceWidth = 0;
+
+ // The pixel offset to the currentSentence
+ Common::Point sentencePixelOffset;
+
+ uint i = 0u;
+ uint stringlen = text.size();
+
+ while (i < stringlen) {
+ if (text[i] == '<') {
+ // Flush the currentWord to the currentSentence
+ currentSentence += currentWord;
+ sentenceWidth += wordWidth;
+
+ // Reset the word variables
+ currentWord.clear();
+ wordWidth = 0;
+
+ // Parse the style tag
+ uint startTextPosition = i;
+ while (i < stringlen && text[i] != '>') {
+ ++i;
+ }
+ uint endTextPosition = i;
+
+ uint32 textColor = currentState.getTextColor(_engine);
+
+ uint stateChanges = 0u;
+ if ((endTextPosition - startTextPosition - 1) > 0) {
+ stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1);
+ }
+
+ if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
+ // Use the last state to render out the current sentence
+ // Styles apply to the text 'after' them
+ if (!currentSentence.empty()) {
+ textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
+
+ lineWidth += sentenceWidth;
+ sentencePixelOffset.x += sentenceWidth;
+
+ // Reset the sentence variables
+ currentSentence.clear();
+ sentenceWidth = 0;
+ }
+
+ // Update the current state with the style information
+ currentState.updateFontWithTextState(font);
+
+ lineHeight = MAX(lineHeight, font.getFontHeight());
+ spaceWidth = font.getCharWidth(' ');
+ }
+ if (stateChanges & TEXT_CHANGE_NEWLINE) {
+ // If the current sentence has content, render it out
+ if (!currentSentence.empty()) {
+ textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
+ }
+
+ // Set line width
+ lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
+
+ currentSentence.clear();
+ sentenceWidth = 0;
+
+ // Update the offsets
+ sentencePixelOffset.x = 0u;
+ sentencePixelOffset.y += lineHeight;
+
+ // Reset the line variables
+ lineHeight = font.getFontHeight();
+ lineWidth = 0;
+ ++currentLineNumber;
+ lineJustifications.push_back(currentState._justification);
+ }
+ if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) {
+ Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox));
+ wordWidth += font.getStringWidth(temp);
+
+ // If the word causes the line to overflow, render the sentence and start a new line
+ if (lineWidth + sentenceWidth + wordWidth > dest.w) {
+ textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
+
+ // Set line width
+ lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
+
+ currentSentence.clear();
+ sentenceWidth = 0;
+
+ // Update the offsets
+ sentencePixelOffset.x = 0u;
+ sentencePixelOffset.y += lineHeight;
+
+ // Reset the line variables
+ lineHeight = font.getFontHeight();
+ lineWidth = 0;
+ ++currentLineNumber;
+ lineJustifications.push_back(currentState._justification);
+ }
+ }
+ } else {
+ currentWord += text[i];
+ wordWidth += font.getCharWidth(text[i]);
+
+ if (text[i] == ' ') {
+ // When we hit the first space, flush the current word to the sentence
+ if (!currentWord.empty()) {
+ currentSentence += currentWord;
+ sentenceWidth += wordWidth;
+
+ currentWord.clear();
+ wordWidth = 0;
+ }
+
+ // We track the number of spaces so we can disregard their width in lineWidth calculations
+ ++numSpaces;
+ } else {
+ // If the word causes the line to overflow, render the sentence and start a new line
+ if (lineWidth + sentenceWidth + wordWidth > dest.w) {
+ // Only render out content
+ if (!currentSentence.empty()) {
+ textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
+ }
+
+ // Set line width
+ lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));
+
+ currentSentence.clear();
+ sentenceWidth = 0;
+
+ // Update the offsets
+ sentencePixelOffset.x = 0u;
+ sentencePixelOffset.y += lineHeight;
+
+ // Reset the line variables
+ lineHeight = font.getFontHeight();
+ lineWidth = 0;
+ ++currentLineNumber;
+ lineJustifications.push_back(currentState._justification);
+ }
+
+ numSpaces = 0u;
+ }
+ }
+
+ i++;
+ }
+
+ // Render out any remaining words/sentences
+ if (!currentWord.empty() || !currentSentence.empty()) {
+ currentSentence += currentWord;
+ sentenceWidth += wordWidth;
+
+ textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
+ }
+
+ lineWidths.push_back(lineWidth + sentenceWidth);
+ lineJustifications.push_back(currentState._justification);
+
+ for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) {
+ Common::Rect empty;
+
+ if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_LEFT) {
+ _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
+ } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_CENTER) {
+ _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, ((dest.w - lineWidths[iter->_lineNumber]) / 2) + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
+ } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_RIGHT) {
+ _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, dest.w - lineWidths[iter->_lineNumber] + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
+ }
+
+ // Release memory
+ iter->_surface->free();
+ delete iter->_surface;
+ }
+}
+
+Common::String readWideLine(Common::SeekableReadStream &stream) {
+ Common::String asciiString;
+
+ while (true) {
+ uint32 value = stream.readUint16LE();
+ if (stream.eos())
+ break;
+ // Check for CRLF
+ if (value == 0x0A0D) {
+ // Read in the extra NULL char
+ stream.readByte(); // \0
+ // End of the line. Break
+ break;
+ }
+
+ // Crush each octet pair to a UTF-8 sequence
+ if (value < 0x80) {
+ asciiString += (char)(value & 0x7F);
+ } else if (value >= 0x80 && value < 0x800) {
+ asciiString += (char)(0xC0 | ((value >> 6) & 0x1F));
+ asciiString += (char)(0x80 | (value & 0x3F));
+ } else if (value >= 0x800 && value < 0x10000 && value != 0xCCCC) {
+ asciiString += (char)(0xE0 | ((value >> 12) & 0xF));
+ asciiString += (char)(0x80 | ((value >> 6) & 0x3F));
+ asciiString += (char)(0x80 | (value & 0x3F));
+ } else if (value == 0xCCCC) {
+ // Ignore, this character is used as newline sometimes
+ } else if (value >= 0x10000 && value < 0x200000) {
+ asciiString += (char)(0xF0);
+ asciiString += (char)(0x80 | ((value >> 12) & 0x3F));
+ asciiString += (char)(0x80 | ((value >> 6) & 0x3F));
+ asciiString += (char)(0x80 | (value & 0x3F));
+ }
+ }
+
+ return asciiString;
+}
+
+int8 getUtf8CharSize(char chr) {
+ if ((chr & 0x80) == 0)
+ return 1;
+ else if ((chr & 0xE0) == 0xC0)
+ return 2;
+ else if ((chr & 0xF0) == 0xE0)
+ return 3;
+ else if ((chr & 0xF8) == 0xF0)
+ return 4;
+ else if ((chr & 0xFC) == 0xF8)
+ return 5;
+ else if ((chr & 0xFE) == 0xFC)
+ return 6;
+
+ return 1;
+}
+
+uint16 readUtf8Char(const char *chr) {
+ uint16 result = 0;
+ if ((chr[0] & 0x80) == 0)
+ result = chr[0];
+ else if ((chr[0] & 0xE0) == 0xC0)
+ result = ((chr[0] & 0x1F) << 6) | (chr[1] & 0x3F);
+ else if ((chr[0] & 0xF0) == 0xE0)
+ result = ((chr[0] & 0x0F) << 12) | ((chr[1] & 0x3F) << 6) | (chr[2] & 0x3F);
+ else
+ result = chr[0];
+
+ return result;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/text/text.h b/engines/zvision/text/text.h
new file mode 100644
index 0000000000..d35b90499d
--- /dev/null
+++ b/engines/zvision/text/text.h
@@ -0,0 +1,93 @@
+/* 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/text/truetype_font.h"
+#include "zvision/zvision.h"
+
+namespace ZVision {
+
+class ZVision;
+
+enum TextJustification {
+ TEXT_JUSTIFY_CENTER = 0,
+ TEXT_JUSTIFY_LEFT = 1,
+ TEXT_JUSTIFY_RIGHT = 2
+};
+
+enum TextChange {
+ TEXT_CHANGE_NONE = 0x0,
+ TEXT_CHANGE_FONT_TYPE = 0x1,
+ TEXT_CHANGE_FONT_STYLE = 0x2,
+ TEXT_CHANGE_NEWLINE = 0x4,
+ TEXT_CHANGE_HAS_STATE_BOX = 0x8
+};
+
+class TextStyleState {
+public:
+ TextStyleState();
+ TextChange parseStyle(const Common::String &str, int16 len);
+ void readAllStyles(const Common::String &txt);
+ void updateFontWithTextState(StyledTTFont &font);
+
+ uint32 getTextColor(ZVision *engine) {
+ return engine->_resourcePixelFormat.RGBToColor(_red, _green, _blue);
+ }
+
+public:
+ Common::String _fontname;
+ TextJustification _justification; // 0 - center, 1-left, 2-right
+ int16 _size;
+ uint8 _red; // 0-255
+ uint8 _green; // 0-255
+ uint8 _blue; // 0-255
+ bool _italic;
+ bool _bold;
+ bool _underline;
+ bool _strikeout;
+ int32 _statebox;
+ bool _sharp;
+};
+
+class TextRenderer {
+public:
+ TextRenderer(ZVision *engine): _engine(engine) {};
+
+ void drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification jusification);
+ int32 drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest);
+ void drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest);
+
+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/text/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp
new file mode 100644
index 0000000000..acb053ea8d
--- /dev/null
+++ b/engines/zvision/text/truetype_font.cpp
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/unzip.h"
+#include "common/ustr.h"
+#include "graphics/font.h"
+#include "graphics/fonts/ttf.h"
+#include "graphics/surface.h"
+
+#include "zvision/zvision.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/text/truetype_font.h"
+
+namespace ZVision {
+
+const FontStyle systemFonts[] = {
+ { "*times new roman*", "times", "FreeSerif", "Italic", "LiberationSerif" },
+ { "*times*", "times", "FreeSerif", "Italic", "LiberationSerif" },
+ { "*century schoolbook*", "censcbk", "FreeSerif", "Italic", "LiberationSerif" },
+ { "*garamond*", "gara", "FreeSerif", "Italic", "LiberationSerif" },
+ { "*courier new*", "cour", "FreeMono", "Oblique", "LiberationMono" },
+ { "*courier*", "cour", "FreeMono", "Oblique", "LiberationMono" },
+ { "*ZorkDeath*", "cour", "FreeMono", "Oblique", "LiberationMono" },
+ { "*arial*", "arial", "FreeSans", "Oblique", "LiberationSans" },
+ { "*ZorkNormal*", "arial", "FreeSans", "Oblique", "LiberationSans" }
+};
+
+const FontStyle getSystemFont(int fontIndex) {
+ return systemFonts[fontIndex];
+}
+
+StyledTTFont::StyledTTFont(ZVision *engine) {
+ _engine = engine;
+ _style = 0;
+ _font = nullptr;
+ _lineHeight = 0;
+}
+
+StyledTTFont::~StyledTTFont() {
+ delete _font;
+}
+
+bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) {
+ // Don't re-load the font if we've already loaded it
+ // We have to check for empty so we can default to Arial
+ if (!fontName.empty() && _fontName.equalsIgnoreCase(fontName) && _lineHeight == point && _style == style) {
+ return true;
+ }
+
+ _style = style;
+
+ Common::String newFontName;
+ Common::String freeFontName;
+ Common::String liberationFontName;
+
+ for (int i = 0; i < FONT_COUNT; i++) {
+ FontStyle curFont = getSystemFont(i);
+ if (fontName.matchString(curFont.zorkFont, true)) {
+ newFontName = curFont.fontBase;
+ freeFontName = curFont.freeFontBase;
+ liberationFontName = curFont.liberationFontBase;
+
+ if ((_style & TTF_STYLE_BOLD) && (_style & TTF_STYLE_ITALIC)) {
+ newFontName += "bi";
+ freeFontName += "Bold";
+ freeFontName += curFont.freeFontItalicName;
+ liberationFontName += "-BoldItalic";
+ } else if (_style & TTF_STYLE_BOLD) {
+ newFontName += "bd";
+ freeFontName += "Bold";
+ liberationFontName += "-Bold";
+ } else if (_style & TTF_STYLE_ITALIC) {
+ newFontName += "i";
+ freeFontName += curFont.freeFontItalicName;
+ liberationFontName += "-Italic";
+ } else {
+ liberationFontName += "-Regular";
+ }
+
+ newFontName += ".ttf";
+ freeFontName += ".ttf";
+ liberationFontName += ".ttf";
+ break;
+ }
+ }
+
+ if (newFontName.empty()) {
+ debug("Could not identify font: %s. Reverting to Arial", fontName.c_str());
+ newFontName = "arial.ttf";
+ freeFontName = "FreeSans.ttf";
+ liberationFontName = "LiberationSans-Regular.ttf";
+ }
+
+ bool sharp = (_style & TTF_STYLE_SHARP) == TTF_STYLE_SHARP;
+
+ Common::File file;
+ if (!file.open(newFontName) && !_engine->getSearchManager()->openFile(file, newFontName) &&
+ !file.open(liberationFontName) && !_engine->getSearchManager()->openFile(file, liberationFontName) &&
+ !file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, freeFontName))
+ error("Unable to open font file %s (Liberation Font alternative: %s, FreeFont alternative: %s)", newFontName.c_str(), liberationFontName.c_str(), freeFontName.c_str());
+
+ Graphics::Font *newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display
+ if (newFont == nullptr) {
+ return false;
+ }
+
+ delete _font;
+ _font = newFont;
+
+ _fontName = fontName;
+ _lineHeight = point;
+
+ return true;
+}
+
+int StyledTTFont::getFontHeight() {
+ if (_font)
+ return _font->getFontHeight();
+
+ return 0;
+}
+
+int StyledTTFont::getMaxCharWidth() {
+ if (_font)
+ return _font->getMaxCharWidth();
+
+ return 0;
+}
+
+int StyledTTFont::getCharWidth(byte chr) {
+ if (_font)
+ return _font->getCharWidth(chr);
+
+ return 0;
+}
+
+int StyledTTFont::getKerningOffset(byte left, byte right) {
+ if (_font)
+ return _font->getKerningOffset(left, right);
+
+ return 0;
+}
+
+Common::U32String StyledTTFont::convertUtf8ToUtf32(const Common::String &str) {
+ // The String class, and therefore the Font class as well, assume one
+ // character is one byte, but in this case it's actually an UTF-8
+ // string with up to 4 bytes per character. To work around this,
+ // convert it to an U32String before drawing it, because our Font class
+ // can handle that.
+ Common::U32String u32str;
+ uint i = 0;
+ while (i < str.size()) {
+ uint32 chr = 0;
+ if ((str[i] & 0xF8) == 0xF0) {
+ chr |= (str[i++] & 0x07) << 18;
+ chr |= (str[i++] & 0x3F) << 12;
+ chr |= (str[i++] & 0x3F) << 6;
+ chr |= (str[i++] & 0x3F);
+ } else if ((str[i] & 0xF0) == 0xE0) {
+ chr |= (str[i++] & 0x0F) << 12;
+ chr |= (str[i++] & 0x3F) << 6;
+ chr |= (str[i++] & 0x3F);
+ } else if ((str[i] & 0xE0) == 0xC0) {
+ chr |= (str[i++] & 0x1F) << 6;
+ chr |= (str[i++] & 0x3F);
+ } else {
+ chr = (str[i++] & 0x7F);
+ }
+ u32str += chr;
+ }
+ return u32str;
+}
+
+void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) {
+ if (_font) {
+ _font->drawChar(dst, chr, x, y, color);
+ if (_style & TTF_STYLE_UNDERLINE) {
+ int16 pos = (int16)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 & TTF_STYLE_STRIKETHROUGH) {
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
+ int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
+ dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color);
+ }
+ }
+}
+
+void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) {
+ if (_font) {
+ Common::U32String u32str = convertUtf8ToUtf32(str);
+ _font->drawString(dst, u32str, x, y, w, color, align);
+ if (_style & TTF_STYLE_UNDERLINE) {
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.87);
+ int16 wd = MIN(_font->getStringWidth(u32str), 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 & TTF_STYLE_STRIKETHROUGH) {
+ int16 pos = (int16)floor(_font->getFontHeight() * 0.60);
+ int16 wd = MIN(_font->getStringWidth(u32str), w);
+ int16 stX = x;
+ if (align == Graphics::kTextAlignCenter)
+ stX += (w - wd) / 2;
+ else if (align == Graphics::kTextAlignRight)
+ stX += (w - wd);
+
+ int thk = MAX((int)(_font->getFontHeight() * 0.05), 1);
+
+ dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color);
+ }
+ }
+}
+
+int StyledTTFont::getStringWidth(const Common::String &str) {
+ if (_font)
+ return _font->getStringWidth(str);
+ return 0;
+}
+
+Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint32 color) {
+ Graphics::Surface *tmp = new Graphics::Surface;
+ if (_font) {
+ int16 w = _font->getStringWidth(str);
+ if (w && w < 1024) {
+ tmp->create(w, _font->getFontHeight(), _engine->_resourcePixelFormat);
+ 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/text/truetype_font.h
index 64f53a2c3b..6abe05cda6 100644
--- a/engines/zvision/fonts/truetype_font.h
+++ b/engines/zvision/text/truetype_font.h
@@ -28,52 +28,65 @@
#include "graphics/font.h"
#include "graphics/pixelformat.h"
-
namespace Graphics {
struct Surface;
}
namespace ZVision {
+struct FontStyle {
+ const char *zorkFont;
+ const char *fontBase;
+ const char *freeFontBase;
+ const char *freeFontItalicName;
+ const char *liberationFontBase;
+};
+
+#define FONT_COUNT 9
+
class ZVision;
-class TruetypeFont {
+// Styled TTF
+class StyledTTFont {
public:
- TruetypeFont(ZVision *engine, int32 fontHeight);
- ~TruetypeFont();
+ StyledTTFont(ZVision *engine);
+ ~StyledTTFont();
+
+ enum {
+ TTF_STYLE_BOLD = 0x01,
+ TTF_STYLE_ITALIC = 0x02,
+ TTF_STYLE_UNDERLINE = 0x04,
+ TTF_STYLE_STRIKETHROUGH = 0x08,
+ TTF_STYLE_SHARP = 0x10
+ };
private:
-// ZVision *_engine;
+ ZVision *_engine;
Graphics::Font *_font;
int _lineHeight;
-
-// size_t _maxCharWidth;
-// size_t _maxCharHeight;
+ uint _style;
+ Common::String _fontName;
public:
- int32 _fontHeight;
+ bool loadFont(const Common::String &fontName, int32 point, uint style);
-public:
- /**
- * Loads a .ttf file into memory. This must be called
- * before any calls to drawTextToSurface
- *
- * @param filename The file name of the .ttf file to load
- */
- bool loadFile(const Common::String &filename);
- /**
- * Renders the supplied text to a Surface using 0x0 as the
- * background color.
- *
- * @param text The to render
- * @param textColor The color to render the text with
- * @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 Surface containing the rendered text
- */
- Graphics::Surface *drawTextToSurface(const Common::String &text, uint16 textColor, int maxWidth, int maxHeight, Graphics::TextAlign align, bool wrap);
+ int getFontHeight();
+ int getMaxCharWidth();
+ int getCharWidth(byte chr);
+ int getKerningOffset(byte left, byte right);
+
+ Common::U32String convertUtf8ToUtf32(const Common::String &str);
+
+ 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
diff --git a/engines/zvision/utility/single_value_container.cpp b/engines/zvision/utility/single_value_container.cpp
deleted file mode 100644
index e609474285..0000000000
--- a/engines/zvision/utility/single_value_container.cpp
+++ /dev/null
@@ -1,348 +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/utility/single_value_container.h"
-
-#include "common/textconsole.h"
-#include "common/str.h"
-
-
-namespace ZVision {
-
-SingleValueContainer::SingleValueContainer(ValueType type) : _objectType(type) { }
-
-SingleValueContainer::SingleValueContainer(bool value) : _objectType(BOOL) {
- _value.boolVal = value;
-}
-
-SingleValueContainer::SingleValueContainer(byte value) : _objectType(BYTE) {
- _value.byteVal = value;
-}
-
-SingleValueContainer::SingleValueContainer(int16 value) : _objectType(INT16) {
- _value.int16Val = value;
-}
-
-SingleValueContainer::SingleValueContainer(uint16 value) : _objectType(UINT16) {
- _value.uint16Val = value;
-}
-
-SingleValueContainer::SingleValueContainer(int32 value) : _objectType(INT32) {
- _value.int32Val = value;
-}
-
-SingleValueContainer::SingleValueContainer(uint32 value) : _objectType(UINT32) {
- _value.uint32Val = value;
-}
-
-SingleValueContainer::SingleValueContainer(float value) : _objectType(FLOAT) {
- _value.floatVal = value;
-}
-
-SingleValueContainer::SingleValueContainer(double value) : _objectType(DOUBLE) {
- _value.doubleVal = value;
-}
-
-SingleValueContainer::SingleValueContainer(Common::String value) : _objectType(BYTE) {
- _value.stringVal = new char[value.size() + 1];
- memcpy(_value.stringVal, value.c_str(), value.size() + 1);
-}
-
-SingleValueContainer::SingleValueContainer(const SingleValueContainer &other) {
- _objectType = other._objectType;
-
- switch (_objectType) {
- case BOOL:
- _value.boolVal = other._value.boolVal;
- break;
- case BYTE:
- _value.byteVal = other._value.byteVal;
- break;
- case INT16:
- _value.int16Val = other._value.int16Val;
- break;
- case UINT16:
- _value.uint16Val = other._value.uint16Val;
- break;
- case INT32:
- _value.int32Val = other._value.int32Val;
- break;
- case UINT32:
- _value.uint32Val = other._value.uint32Val;
- break;
- case FLOAT:
- _value.floatVal = other._value.floatVal;
- break;
- case DOUBLE:
- _value.doubleVal = other._value.doubleVal;
- break;
- case STRING:
- uint32 length = strlen(other._value.stringVal);
- _value.stringVal = new char[length + 1];
- memcpy(_value.stringVal, other._value.stringVal, length + 1);
- break;
- }
-}
-
-SingleValueContainer::~SingleValueContainer() {
- deleteCharPointer();
-}
-
-void SingleValueContainer::deleteCharPointer() {
- if (_objectType == STRING)
- delete[] _value.stringVal;
-}
-
-
-SingleValueContainer &SingleValueContainer::operator=(const bool &rhs) {
- if (_objectType == BOOL) {
- _value.boolVal = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = BOOL;
- _value.boolVal = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const byte &rhs) {
- if (_objectType == BYTE) {
- _value.byteVal = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = BYTE;
- _value.byteVal = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const int16 &rhs) {
- if (_objectType == INT16) {
- _value.int16Val = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = INT16;
- _value.int16Val = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const uint16 &rhs) {
- if (_objectType == UINT16) {
- _value.uint16Val = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = UINT16;
- _value.uint16Val = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const int32 &rhs) {
- if (_objectType == INT32) {
- _value.int32Val = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = INT32;
- _value.int32Val = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const uint32 &rhs) {
- if (_objectType == UINT32) {
- _value.uint32Val = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = UINT32;
- _value.uint32Val = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const float &rhs) {
- if (_objectType == FLOAT) {
- _value.floatVal = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = FLOAT;
- _value.floatVal = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const double &rhs) {
- if (_objectType == DOUBLE) {
- _value.doubleVal = rhs;
- return *this;
- }
-
- deleteCharPointer();
- _objectType = DOUBLE;
- _value.doubleVal = rhs;
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const Common::String &rhs) {
- if (_objectType != STRING) {
- _objectType = STRING;
- _value.stringVal = new char[rhs.size() + 1];
- memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1);
-
- return *this;
- }
-
- uint32 length = strlen(_value.stringVal);
- if (length <= rhs.size() + 1) {
- memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1);
- } else {
- delete[] _value.stringVal;
- _value.stringVal = new char[rhs.size() + 1];
- memcpy(_value.stringVal, rhs.c_str(), rhs.size() + 1);
- }
-
- return *this;
-}
-
-SingleValueContainer &SingleValueContainer::operator=(const SingleValueContainer &rhs) {
- switch (_objectType) {
- case BOOL:
- return operator=(rhs._value.boolVal);
- case BYTE:
- return operator=(rhs._value.byteVal);
- case INT16:
- return operator=(rhs._value.int16Val);
- case UINT16:
- return operator=(rhs._value.uint16Val);
- case INT32:
- return operator=(rhs._value.int32Val);
- case UINT32:
- return operator=(rhs._value.uint32Val);
- case FLOAT:
- return operator=(rhs._value.floatVal);
- case DOUBLE:
- return operator=(rhs._value.doubleVal);
- case STRING:
- uint32 length = strlen(rhs._value.stringVal);
-
- _value.stringVal = new char[length + 1];
- memcpy(_value.stringVal, rhs._value.stringVal, length + 1);
-
- return *this;
- }
-
- return *this;
-}
-
-
-bool SingleValueContainer::getBoolValue(bool *returnValue) const {
- if (_objectType != BOOL) {
- warning("'Object' is not storing a bool.");
- return false;
- }
-
- *returnValue = _value.boolVal;
- return true;
-}
-
-bool SingleValueContainer::getByteValue(byte *returnValue) const {
- if (_objectType != BYTE)
- warning("'Object' is not storing a byte.");
-
- *returnValue = _value.byteVal;
- return true;
-}
-
-bool SingleValueContainer::getInt16Value(int16 *returnValue) const {
- if (_objectType != INT16)
- warning("'Object' is not storing an int16.");
-
- *returnValue = _value.int16Val;
- return true;
-}
-
-bool SingleValueContainer::getUInt16Value(uint16 *returnValue) const {
- if (_objectType != UINT16)
- warning("'Object' is not storing a uint16.");
-
- *returnValue = _value.uint16Val;
- return true;
-}
-
-bool SingleValueContainer::getInt32Value(int32 *returnValue) const {
- if (_objectType != INT32)
- warning("'Object' is not storing an int32.");
-
- *returnValue = _value.int32Val;
- return true;
-}
-
-bool SingleValueContainer::getUInt32Value(uint32 *returnValue) const {
- if (_objectType != UINT32)
- warning("'Object' is not storing a uint32.");
-
- *returnValue = _value.uint32Val;
- return true;
-}
-
-bool SingleValueContainer::getFloatValue(float *returnValue) const {
- if (_objectType != FLOAT)
- warning("'Object' is not storing a float.");
-
- *returnValue = _value.floatVal;
- return true;
-}
-
-bool SingleValueContainer::getDoubleValue(double *returnValue) const {
- if (_objectType != DOUBLE)
- warning("'Object' is not storing a double.");
-
- *returnValue = _value.doubleVal;
- return true;
-}
-
-bool SingleValueContainer::getStringValue(Common::String *returnValue) const {
- if (_objectType != STRING)
- warning("'Object' is not storing a Common::String.");
-
- *returnValue = _value.stringVal;
- return true;
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/utility/single_value_container.h b/engines/zvision/utility/single_value_container.h
deleted file mode 100644
index 951383661a..0000000000
--- a/engines/zvision/utility/single_value_container.h
+++ /dev/null
@@ -1,183 +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.
- *
- */
-
-#ifndef ZVISION_SINGLE_VALUE_CONTAINER_H
-#define ZVISION_SINGLE_VALUE_CONTAINER_H
-
-namespace Common {
-class String;
-}
-
-namespace ZVision {
-
-/**
- * A generic single value storage class. It is useful for storing different
- * value types in a single List, Hashmap, etc.
- */
-class SingleValueContainer {
-public:
- enum ValueType {
- BOOL,
- BYTE,
- INT16,
- UINT16,
- INT32,
- UINT32,
- FLOAT,
- DOUBLE,
- STRING
- };
-
- // Constructors
- explicit SingleValueContainer(ValueType type);
- explicit SingleValueContainer(bool value);
- explicit SingleValueContainer(byte value);
- explicit SingleValueContainer(int16 value);
- explicit SingleValueContainer(uint16 value);
- explicit SingleValueContainer(int32 value);
- explicit SingleValueContainer(uint32 value);
- explicit SingleValueContainer(float value);
- explicit SingleValueContainer(double value);
- explicit SingleValueContainer(Common::String value);
-
- // Copy constructor
- explicit SingleValueContainer(const SingleValueContainer& other);
-
- // Destructor
- ~SingleValueContainer();
-
-private:
- ValueType _objectType;
-
- union {
- bool boolVal;
- byte byteVal;
- int16 int16Val;
- uint16 uint16Val;
- int32 int32Val;
- uint32 uint32Val;
- float floatVal;
- double doubleVal;
- char *stringVal;
- } _value;
-
-public:
- SingleValueContainer &operator=(const bool &rhs);
- SingleValueContainer &operator=(const byte &rhs);
- SingleValueContainer &operator=(const int16 &rhs);
- SingleValueContainer &operator=(const uint16 &rhs);
- SingleValueContainer &operator=(const int32 &rhs);
- SingleValueContainer &operator=(const uint32 &rhs);
- SingleValueContainer &operator=(const float &rhs);
- SingleValueContainer &operator=(const double &rhs);
- SingleValueContainer &operator=(const Common::String &rhs);
-
- SingleValueContainer& operator=(const SingleValueContainer &rhs);
-
- /**
- * Retrieve a bool from the container. If the container is not storing a
- * bool, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getBoolValue(bool *returnValue) const;
- /**
- * Retrieve a byte from the container. If the container is not storing a
- * byte, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getByteValue(byte *returnValue) const;
- /**
- * Retrieve an int16 from the container. If the container is not storing an
- * int16, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getInt16Value(int16 *returnValue) const;
- /**
- * Retrieve a uint16 from the container. If the container is not storing a
- * uint16, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getUInt16Value(uint16 *returnValue) const;
- /**
- * Retrieve an int32 from the container. If the container is not storing an
- * int32, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getInt32Value(int32 *returnValue) const;
- /**
- * Retrieve a uint32 from the container. If the container is not storing a
- * uint32, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getUInt32Value(uint32 *returnValue) const;
- /**
- * Retrieve a float from the container. If the container is not storing a
- * float, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getFloatValue(float *returnValue) const;
- /**
- * Retrieve a double from the container. If the container is not storing a
- * double, this will return false and display a warning().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getDoubleValue(double *returnValue) const;
- /**
- * Retrieve a String from the container. If the container is not storing a
- * string, this will return false and display a warning().
- *
- * Caution: Strings are internally stored as char[]. getStringValue uses
- * Common::String::operator=(char *) to do the assigment, which uses both
- * strlen() AND memmove().
- *
- * @param returnValue Pointer to where you want the value stored
- * @return Value indicating whether the value assignment was successful
- */
- bool getStringValue(Common::String *returnValue) const;
-
-private:
- /**
- * Helper method for destruction and assignment. It checks to see
- * if the char pointer is being used, and if so calls delete on it
- */
- void deleteCharPointer();
-};
-
-} // End of namespace ZVision
-
-#endif
diff --git a/engines/zvision/utility/utility.cpp b/engines/zvision/utility/utility.cpp
deleted file mode 100644
index 905bc4513a..0000000000
--- a/engines/zvision/utility/utility.cpp
+++ /dev/null
@@ -1,237 +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/utility/utility.h"
-
-#include "zvision/zvision.h"
-#include "zvision/sound/zork_raw.h"
-
-#include "common/tokenizer.h"
-#include "common/file.h"
-
-
-namespace ZVision {
-
-void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile) {
- Common::File f;
- if (!f.open(sourceFile)) {
- return;
- }
-
- byte* buffer = new byte[f.size()];
- f.read(buffer, f.size());
-
- Common::DumpFile dumpFile;
- dumpFile.open(destFile);
-
- dumpFile.write(buffer, f.size());
- dumpFile.flush();
- dumpFile.close();
-
- delete[] buffer;
-}
-
-void trimCommentsAndWhiteSpace(Common::String *string) {
- for (int i = string->size() - 1; i >= 0; i--) {
- if ((*string)[i] == '#') {
- string->erase(i);
- }
- }
-
- string->trim();
-}
-
-void tryToDumpLine(const Common::String &key,
- 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]) {
- output.writeString(line);
- output.writeByte('\n');
- (*count)[key]++;
- (*fileAlreadyUsed)[key] = true;
- }
-}
-
-void dumpEveryResultAction(const Common::String &destFile) {
- Common::HashMap<Common::String, byte> count;
- Common::HashMap<Common::String, bool> fileAlreadyUsed;
-
- Common::DumpFile output;
- output.open(destFile);
-
- // Find scr files
- Common::ArchiveMemberList list;
- SearchMan.listMatchingMembers(list, "*.scr");
-
- for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
- Common::SeekableReadStream *stream = (*iter)->createReadStream();
-
- Common::String line = stream->readLine();
- trimCommentsAndWhiteSpace(&line);
-
- while (!stream->eos()) {
- if (line.matchString("*:add*", true)) {
- tryToDumpLine("add", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:animplay*", true)) {
- tryToDumpLine("animplay", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:animpreload*", true)) {
- tryToDumpLine("animpreload", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:animunload*", true)) {
- tryToDumpLine("animunload", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:attenuate*", true)) {
- tryToDumpLine("attenuate", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:assign*", true)) {
- tryToDumpLine("assign", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:change_location*", true)) {
- tryToDumpLine("change_location", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:crossfade*", true) && !fileAlreadyUsed["add"]) {
- tryToDumpLine("crossfade", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:debug*", true)) {
- tryToDumpLine("debug", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:delay_render*", true)) {
- tryToDumpLine("delay_render", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:disable_control*", true)) {
- tryToDumpLine("disable_control", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:disable_venus*", true)) {
- tryToDumpLine("disable_venus", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:display_message*", true)) {
- tryToDumpLine("display_message", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:dissolve*", true)) {
- tryToDumpLine("dissolve", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:distort*", true)) {
- tryToDumpLine("distort", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:enable_control*", true)) {
- tryToDumpLine("enable_control", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:flush_mouse_events*", true)) {
- tryToDumpLine("flush_mouse_events", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:inventory*", true)) {
- tryToDumpLine("inventory", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:kill*", true)) {
- tryToDumpLine("kill", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:menu_bar_enable*", true)) {
- tryToDumpLine("menu_bar_enable", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:music*", true)) {
- tryToDumpLine("music", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:pan_track*", true)) {
- tryToDumpLine("pan_track", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:playpreload*", true)) {
- tryToDumpLine("playpreload", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:preferences*", true)) {
- tryToDumpLine("preferences", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:quit*", true)) {
- tryToDumpLine("quit", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:random*", true)) {
- tryToDumpLine("random", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:region*", true)) {
- tryToDumpLine("region", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:restore_game*", true)) {
- tryToDumpLine("restore_game", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:rotate_to*", true)) {
- tryToDumpLine("rotate_to", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:save_game*", true)) {
- tryToDumpLine("save_game", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:set_partial_screen*", true)) {
- tryToDumpLine("set_partial_screen", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:set_screen*", true)) {
- tryToDumpLine("set_screen", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:set_venus*", true)) {
- tryToDumpLine("set_venus", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:stop*", true)) {
- tryToDumpLine("stop", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:streamvideo*", true)) {
- tryToDumpLine("streamvideo", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:syncsound*", true)) {
- tryToDumpLine("syncsound", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:timer*", true)) {
- tryToDumpLine("timer", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:ttytext*", true)) {
- tryToDumpLine("ttytext", line, &count, &fileAlreadyUsed, output);
- } else if (line.matchString("*:universe_music*", true)) {
- tryToDumpLine("universe_music", line, &count, &fileAlreadyUsed, output);
- }
-
- line = stream->readLine();
- trimCommentsAndWhiteSpace(&line);
- }
-
- for (Common::HashMap<Common::String, bool>::iterator fileUsedIter = fileAlreadyUsed.begin(); fileUsedIter != fileAlreadyUsed.end(); ++fileUsedIter) {
- fileUsedIter->_value = false;
- }
- }
-
- output.close();
-}
-
-Common::String getFileName(const Common::String &fullPath) {
- Common::StringTokenizer tokenizer(fullPath, "/\\");
- Common::String token;
- while (!tokenizer.empty()) {
- token = tokenizer.nextToken();
- }
-
- return token;
-}
-
-void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile) {
- Common::File file;
- if (!file.open(inputFile))
- return;
-
- Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine);
-
- Common::DumpFile output;
- output.open(outputFile);
-
- output.writeUint32BE(MKTAG('R', 'I', 'F', 'F'));
- output.writeUint32LE(file.size() * 2 + 36);
- output.writeUint32BE(MKTAG('W', 'A', 'V', 'E'));
- output.writeUint32BE(MKTAG('f', 'm', 't', ' '));
- output.writeUint32LE(16);
- output.writeUint16LE(1);
- uint16 numChannels;
- if (audioStream->isStereo()) {
- numChannels = 2;
- output.writeUint16LE(2);
- } else {
- numChannels = 1;
- output.writeUint16LE(1);
- }
- output.writeUint32LE(audioStream->getRate());
- output.writeUint32LE(audioStream->getRate() * numChannels * 2);
- output.writeUint16LE(numChannels * 2);
- output.writeUint16LE(16);
- output.writeUint32BE(MKTAG('d', 'a', 't', 'a'));
- output.writeUint32LE(file.size() * 2);
- int16 *buffer = new int16[file.size()];
- audioStream->readBuffer(buffer, file.size());
- output.write(buffer, file.size() * 2);
-
- delete[] buffer;
-}
-
-} // End of namespace ZVision
diff --git a/engines/zvision/utility/utility.h b/engines/zvision/utility/utility.h
deleted file mode 100644
index 063d4c0663..0000000000
--- a/engines/zvision/utility/utility.h
+++ /dev/null
@@ -1,114 +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.
- *
- */
-
-#ifndef ZVISION_UTILITY_H
-#define ZVISION_UTILITY_H
-
-#include "common/array.h"
-
-
-namespace Common {
-class String;
-}
-
-namespace ZVision {
-
-class ZVision;
-
-/**
- * Opens the sourceFile utilizing Common::File (aka SearchMan) and writes the
- * contents to destFile. destFile is created in the working directory
- *
- * @param sourceFile The 'file' you want the contents of
- * @param destFile The name of the file where the content will be written to
- */
-void writeFileContentsToFile(const Common::String &sourceFile, const Common::String &destFile);
-
-/**
- * Removes any line comments using '#' as a sequence start.
- * Then removes any trailing and leading 'whitespace' using String::trim()
- * Note: String::trim uses isspace() to determine what is whitespace and what is not.
- *
- * @param string The string to modify. It is modified in place
- */
-void trimCommentsAndWhiteSpace(Common::String *string);
-
-/**
- * Searches through all the .scr files and dumps 'numberOfExamplesPerType' examples of each type of ResultAction
- * ZVision::initialize() must have been called before this function can be used.
- *
- * @param destFile Where to write the examples
- */
-void dumpEveryResultAction(const Common::String &destFile);
-
-/**
- * Removes all duplicate entries from container. Relative order will be preserved.
- *
- * @param container The Array to remove duplicate entries from
- */
-template<class T>
-void removeDuplicateEntries(Common::Array<T> &container) {
- // Length of modified array
- uint newLength = 1;
- uint j;
-
- for(uint i = 1; i < container.size(); i++) {
- for(j = 0; j < newLength; j++) {
- if (container[i] == container[j]) {
- break;
- }
- }
-
- // If none of the values in index[0..j] of container are the same as array[i],
- // then copy the current value to corresponding new position in array
- if (j == newLength) {
- container[newLength++] = container[i];
- }
- }
-
- // Actually remove the unneeded space
- while (container.size() < newLength) {
- container.pop_back();
- }
-}
-
-/**
- * Gets the name of the file (including extension). Forward or back slashes
- * are interpreted as directory changes
- *
- * @param fullPath A full or partial path to the file. Ex: folderOne/folderTwo/file.txt
- * @return The name of the file without any preceding directories. Ex: file.txt
- */
-Common::String getFileName(const Common::String &fullPath);
-
-/**
- * Converts a ZVision .RAW file to a .WAV
- * The .WAV will be created in the working directory and will overwrite any existing file
- *
- * @param inputFile The path to the input .RAW file
- * @param outputFile The name of the output .WAV file
- */
-void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile);
-
-} // End of namespace ZVision
-
-#endif
diff --git a/engines/zvision/video/rlf_decoder.cpp b/engines/zvision/video/rlf_decoder.cpp
new file mode 100644
index 0000000000..3bbf22edff
--- /dev/null
+++ b/engines/zvision/video/rlf_decoder.cpp
@@ -0,0 +1,313 @@
+/* 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/video/rlf_decoder.h"
+
+#include "common/str.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+#include "common/debug.h"
+#include "common/endian.h"
+
+namespace ZVision {
+
+RLFDecoder::~RLFDecoder() {
+ close();
+}
+
+bool RLFDecoder::loadStream(Common::SeekableReadStream *stream) {
+ close();
+
+ // Check if the stream is valid
+ if (stream && !stream->err() && stream->readUint32BE() == MKTAG('F', 'E', 'L', 'R')) {
+ addTrack(new RLFVideoTrack(stream));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream)
+ : _readStream(stream),
+ _lastFrameRead(0),
+ _frameCount(0),
+ _width(0),
+ _height(0),
+ _frameTime(0),
+ _frames(0),
+ _displayedFrame(-1),
+ _frameBufferByteSize(0) {
+
+ if (!readHeader()) {
+ warning("Not a RLF animation file. Wrong magic number");
+ return;
+ }
+
+ _currentFrameBuffer.create(_width, _height, getPixelFormat());
+ _frameBufferByteSize = _width * _height * sizeof(uint16);
+
+ _frames = new Frame[_frameCount];
+
+ // Read in each frame
+ for (uint i = 0; i < _frameCount; ++i) {
+ _frames[i] = readNextFrame();
+ }
+}
+
+RLFDecoder::RLFVideoTrack::~RLFVideoTrack() {
+ for (uint i = 0; i < _frameCount; ++i) {
+ delete[] _frames[i].encodedData;
+ }
+ delete[] _frames;
+ delete _readStream;
+ _currentFrameBuffer.free();
+}
+
+bool RLFDecoder::RLFVideoTrack::readHeader() {
+ // Read the header
+ _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.
+ _readStream->seek(136, SEEK_CUR);
+
+ //// Read CIN header
+ //_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
+ //_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
+ _readStream->readUint32BE(); // Magic number EMIT
+ _readStream->readUint32LE(); // Size4
+ _readStream->readUint32LE(); // Unknown11
+ _frameTime = _readStream->readUint32LE() / 10; // Frame time in microseconds
+
+ return true;
+}
+
+RLFDecoder::RLFVideoTrack::Frame RLFDecoder::RLFVideoTrack::readNextFrame() {
+ RLFDecoder::RLFVideoTrack::Frame frame;
+
+ _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];
+ _readStream->read(frame.encodedData, frame.encodedSize);
+
+ if (type == MKTAG('E', 'L', 'H', 'D')) {
+ frame.type = Masked;
+ } else if (type == MKTAG('E', 'L', 'R', 'H')) {
+ frame.type = Simple;
+ _completeFrames.push_back(_lastFrameRead);
+ } else {
+ warning("Frame %u doesn't have type that can be decoded", _lastFrameRead);
+ }
+
+ _lastFrameRead++;
+ return frame;
+}
+
+bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) {
+ uint frame = getFrameAtTime(time);
+ assert(frame < _frameCount);
+
+ if ((uint)_displayedFrame == frame)
+ return true;
+
+ int closestFrame = _displayedFrame;
+ int distance = (int)frame - closestFrame;
+
+ if (distance < 0) {
+ for (uint i = 0; i < _completeFrames.size(); ++i) {
+ if (_completeFrames[i] > frame)
+ break;
+ closestFrame = _completeFrames[i];
+ }
+ } else {
+ for (uint i = 0; i < _completeFrames.size(); ++i) {
+ int newDistance = (int)frame - (int)(_completeFrames[i]);
+ if (newDistance < 0)
+ break;
+ if (newDistance < distance) {
+ closestFrame = _completeFrames[i];
+ distance = newDistance;
+ }
+ }
+ }
+
+ for (uint i = closestFrame; i < frame; ++i) {
+ applyFrameToCurrent(i);
+ }
+
+ _displayedFrame = frame - 1;
+
+ return true;
+}
+
+const Graphics::Surface *RLFDecoder::RLFVideoTrack::decodeNextFrame() {
+ if (_displayedFrame >= (int)_frameCount)
+ return NULL;
+
+ _displayedFrame++;
+ applyFrameToCurrent(_displayedFrame);
+
+ return &_currentFrameBuffer;
+}
+
+void RLFDecoder::RLFVideoTrack::applyFrameToCurrent(uint frameNumber) {
+ if (_frames[frameNumber].type == Masked) {
+ decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
+ } else if (_frames[frameNumber].type == Simple) {
+ decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize);
+ }
+}
+
+void RLFDecoder::RLFVideoTrack::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];
+ sourceOffset++;
+
+ // If numberOfSamples is negative, the next abs(numberOfSamples) samples should
+ // be copied directly from source to dest
+ if (numberOfSamples < 0) {
+ numberOfCopy = -numberOfSamples;
+
+ while (numberOfCopy > 0) {
+ if (sourceOffset + 1 >= sourceSize) {
+ return;
+ } else if (destOffset + 1 >= destSize) {
+ debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
+ return;
+ }
+
+ WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset));
+
+ sourceOffset += 2;
+ destOffset += 2;
+ numberOfCopy--;
+ }
+
+ // 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;
+ } else if (destOffset + 1 >= destSize) {
+ debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
+ return;
+ }
+
+ destOffset += (numberOfSamples * 2) + 2;
+ }
+ }
+}
+
+void RLFDecoder::RLFVideoTrack::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];
+ sourceOffset++;
+
+ // If numberOfSamples is negative, the next abs(numberOfSamples) samples should
+ // be copied directly from source to dest
+ if (numberOfSamples < 0) {
+ numberOfCopy = -numberOfSamples;
+
+ while (numberOfCopy > 0) {
+ if (sourceOffset + 1 >= sourceSize) {
+ return;
+ } else if (destOffset + 1 >= destSize) {
+ debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize);
+ return;
+ }
+
+ WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset));
+
+ sourceOffset += 2;
+ destOffset += 2;
+ numberOfCopy--;
+ }
+
+ // If numberOfSamples is >= 0, copy one sample from source to the
+ // next (numberOfSamples + 2) dest spots
+ } else {
+ if (sourceOffset + 1 >= sourceSize) {
+ return;
+ }
+
+ uint16 sampleColor = READ_LE_UINT16(source + sourceOffset);
+ sourceOffset += 2;
+
+ 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;
+ }
+
+ WRITE_UINT16(dest + destOffset, sampleColor);
+ destOffset += 2;
+ numberOfCopy--;
+ }
+ }
+ }
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/video/rlf_decoder.h b/engines/zvision/video/rlf_decoder.h
new file mode 100644
index 0000000000..8b8cbaecd5
--- /dev/null
+++ b/engines/zvision/video/rlf_decoder.h
@@ -0,0 +1,134 @@
+/* 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_RLF_DECODER_H
+#define ZVISION_RLF_DECODER_H
+
+#include "common/file.h"
+#include "video/video_decoder.h"
+
+#include "graphics/surface.h"
+
+namespace ZVision {
+
+class RLFDecoder : public Video::VideoDecoder {
+public:
+ RLFDecoder() {}
+ ~RLFDecoder();
+
+ bool loadStream(Common::SeekableReadStream *stream);
+
+private:
+ class RLFVideoTrack : public FixedRateVideoTrack {
+ public:
+ RLFVideoTrack(Common::SeekableReadStream *stream);
+ ~RLFVideoTrack();
+
+ uint16 getWidth() const { return _width; }
+ uint16 getHeight() const { return _height; }
+ Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); /* RGB 555 */ }
+ int getCurFrame() const { return _displayedFrame; }
+ int getFrameCount() const { return _frameCount; }
+ const Graphics::Surface *decodeNextFrame();
+ bool isSeekable() const { return true; }
+ bool seek(const Audio::Timestamp &time);
+
+ protected:
+ Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); }
+
+ private:
+ enum EncodingType {
+ Masked,
+ Simple
+ };
+
+ struct Frame {
+ EncodingType type;
+ int8 *encodedData;
+ uint32 encodedSize;
+ };
+
+ /**
+ * Reads in the header of the RLF file
+ *
+ * @return Will return false if the header magic number is wrong
+ */
+ bool readHeader();
+
+ /**
+ * Reads the next frame from the RLF file, stores the data in
+ * a Frame object, then returns the object
+ *
+ * @return A Frame object representing the frame data
+ */
+ Frame readNextFrame();
+
+ /**
+ * Applies the frame corresponding to frameNumber on top of _currentFrameBuffer.
+ * This function requires _stream = false so it can look up the Frame object
+ * referenced by frameNumber.
+ *
+ * @param frameNumber The frame number to apply to _currentFrameBuffer
+ */
+ void applyFrameToCurrent(uint frameNumber);
+
+ /**
+ * Decode frame data that uses masked run length encoding. This is the encoding
+ * used by P-frames.
+ *
+ * @param source The source pixel data
+ * @param dest The destination buffer
+ * @param sourceSize The size of the source pixel data
+ * @param destSize The size of the destination buffer
+ */
+ void decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const;
+ /**
+ * Decode frame data that uses simple run length encoding. This is the encoding
+ * used by I-frames.
+ *
+ * @param source The source pixel data
+ * @param dest The destination buffer
+ * @param sourceSize The size of the source pixel data
+ * @param destSize The size of the destination buffer
+ */
+ void decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const;
+
+ uint _lastFrameRead;
+
+ uint _frameCount;
+ uint _width;
+ uint _height;
+ uint32 _frameTime; // In milliseconds
+ Frame *_frames;
+ Common::Array<uint> _completeFrames;
+
+ int _displayedFrame;
+ Graphics::Surface _currentFrameBuffer;
+ uint32 _frameBufferByteSize;
+
+ Common::SeekableReadStream *_readStream;
+ }; // RLFVideoTrack
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp
index d1fff30408..1cfd0f4197 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.
@@ -21,107 +21,78 @@
*/
#include "common/scummsys.h"
-
-#include "zvision/zvision.h"
-
-#include "zvision/utility/clock.h"
-#include "zvision/graphics/render_manager.h"
-
#include "common/system.h"
-
#include "video/video_decoder.h"
-
+#ifdef USE_MPEG2
+#include "video/mpegps_decoder.h"
+#endif
#include "engines/util.h"
-
#include "graphics/surface.h"
+#include "zvision/zvision.h"
+#include "zvision/core/clock.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/text/subtitles.h"
+#include "zvision/video/rlf_decoder.h"
+#include "zvision/video/zork_avi_decoder.h"
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;
- }
- }
+Video::VideoDecoder *ZVision::loadAnimation(const Common::String &fileName) {
+ Common::String tmpFileName = fileName;
+ tmpFileName.toLowercase();
+ Video::VideoDecoder *animation = NULL;
+
+ if (tmpFileName.hasSuffix(".rlf"))
+ animation = new RLFDecoder();
+ else if (tmpFileName.hasSuffix(".avi"))
+ animation = new ZorkAVIDecoder();
+#ifdef USE_MPEG2
+ else if (tmpFileName.hasSuffix(".vob"))
+ animation = new Video::MPEGPSDecoder();
+#endif
+ else
+ error("Unknown suffix for animation %s", fileName.c_str());
+
+ Common::File *_file = getSearchManager()->openFile(tmpFileName);
+ if (!_file)
+ error("Error opening %s", tmpFileName.c_str());
+
+ bool loaded = animation->loadStream(_file);
+ if (!loaded)
+ error("Error loading animation %s", tmpFileName.c_str());
+
+ return animation;
}
-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.
- }
-
- uint16 pitch = origWidth * bytesPerPixel;
+ if (dst.isEmpty())
+ dst = Common::Rect(vid.getWidth(), vid.getHeight());
- uint16 finalWidth = origWidth * scale;
- uint16 finalHeight = origHeight * scale;
+ Graphics::Surface *scaled = NULL;
- 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();
+ bool showSubs = (_scriptManager->getStateValue(StateKey_Subtitles) == 1);
_clock.stop();
- videoDecoder.start();
+ vid.start();
+ _videoIsPlaying = true;
// 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 +102,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 +113,34 @@ 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 && showSubs)
+ 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;
}
+ Common::Rect rect = Common::Rect(x, y, x + finalWidth, y + finalHeight);
+ _renderManager->copyToScreen(*frame, rect, 0, 0);
+ _renderManager->processSubs(0);
}
}
// Always update the screen so the mouse continues to render
_system->updateScreen();
- _system->delayMillis(videoDecoder.getTimeToNextFrame());
+ _system->delayMillis(vid.getTimeToNextFrame() / 2);
}
+ _videoIsPlaying = false;
_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..cf8505ec82 100644
--- a/engines/zvision/video/zork_avi_decoder.cpp
+++ b/engines/zvision/video/zork_avi_decoder.cpp
@@ -29,7 +29,7 @@
#include "common/stream.h"
#include "audio/audiostream.h"
-
+#include "audio/decoders/raw.h"
namespace ZVision {
@@ -39,14 +39,39 @@ Video::AVIDecoder::AVIAudioTrack *ZorkAVIDecoder::createAudioTrack(Video::AVIDec
}
void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *stream) {
+ bool updateCurChunk = true;
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) {
+ byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO;
+#ifdef SCUMM_LITTLE_ENDIAN
+ // RawChunkStream produces native endianness int16
+ flags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+ _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, flags);
+ }
+ } else {
+ updateCurChunk = false;
+ AVIAudioTrack::queueSound(stream);
}
} else {
delete stream;
}
+
+ // The superclass always updates _curChunk, whether or not audio has
+ // been queued, so we should do that too. Unless the superclass already
+ // has done it for us.
+ if (updateCurChunk) {
+ _curChunk++;
+ }
+}
+
+void ZorkAVIDecoder::ZorkAVIAudioTrack::resetStream() {
+ decoder->init();
}
} // End of namespace ZVision
diff --git a/engines/zvision/video/zork_avi_decoder.h b/engines/zvision/video/zork_avi_decoder.h
index c47f007f9b..89c0d1e4b9 100644
--- a/engines/zvision/video/zork_avi_decoder.h
+++ b/engines/zvision/video/zork_avi_decoder.h
@@ -8,41 +8,53 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
+ *
*/
#ifndef ZORK_AVI_DECODER_H
#define ZORK_AVI_DECODER_H
#include "video/avi_decoder.h"
-
+#include "zvision/sound/zork_raw.h"
namespace ZVision {
class ZorkAVIDecoder : public Video::AVIDecoder {
public:
ZorkAVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) :
- Video::AVIDecoder(soundType) {}
+ Video::AVIDecoder(soundType) {}
- virtual ~ZorkAVIDecoder() {}
+ virtual ~ZorkAVIDecoder() {}
private:
class ZorkAVIAudioTrack : public Video::AVIDecoder::AVIAudioTrack {
public:
ZorkAVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) :
- Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType) {}
- virtual ~ZorkAVIAudioTrack() {}
+ Video::AVIDecoder::AVIAudioTrack(streamHeader, waveFormat, soundType),
+ decoder(NULL) {
+ if (_audStream) {
+ decoder = new RawChunkStream(_audStream->isStereo());
+ }
+ }
+ virtual ~ZorkAVIAudioTrack() {
+ if (decoder)
+ delete decoder;
+ }
void queueSound(Common::SeekableReadStream *stream);
+ void resetStream();
+ private:
+ RawChunkStream *decoder;
};
Video::AVIDecoder::AVIAudioTrack *createAudioTrack(Video::AVIDecoder::AVIStreamHeader sHeader, Video::AVIDecoder::PCMWaveFormat wvInfo);
@@ -50,7 +62,7 @@ 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..da80ff9d02 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.
@@ -23,17 +23,22 @@
#include "common/scummsys.h"
#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/graphics/cursors/cursor_manager.h"
+#include "zvision/file/save_manager.h"
+#include "zvision/text/string_manager.h"
#include "zvision/detection.h"
+#include "zvision/scripting/menu.h"
+#include "zvision/file/search_manager.h"
+#include "zvision/text/text.h"
+#include "zvision/text/truetype_font.h"
+#include "zvision/sound/midi.h"
+#include "zvision/file/zfs_archive.h"
#include "common/config-manager.h"
+#include "common/str.h"
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/textconsole.h"
@@ -41,28 +46,67 @@
#include "common/system.h"
#include "common/file.h"
+#include "gui/message.h"
#include "engines/util.h"
-
#include "audio/mixer.h"
-
namespace ZVision {
+#define ZVISION_SETTINGS_KEYS_COUNT 12
+
+struct zvisionIniSettings {
+ const char *name;
+ int16 slot;
+ int16 defaultValue; // -1: use the bool value
+ bool defaultBoolValue;
+ bool allowEditing;
+} settingsKeys[ZVISION_SETTINGS_KEYS_COUNT] = {
+ // Hardcoded settings
+ {"countrycode", StateKey_CountryCode, 0, false, false}, // always 0 = US, subtitles are shown for codes 0 - 4, unused
+ {"lineskipvideo", StateKey_VideoLineSkip, 0, false, false}, // video line skip, 0 = default, 1 = always, 2 = pixel double when possible, unused
+ {"installlevel", StateKey_InstallLevel, 0, false, false}, // 0 = full, checked by universe.scr
+ {"highquality", StateKey_HighQuality, -1, true, false}, // high panorama quality, unused
+ {"qsoundenabled", StateKey_Qsound, -1, true, false}, // 1 = enable QSound - TODO: not supported yet
+ {"debugcheats", StateKey_DebugCheats, -1, true, false}, // always start with the GOxxxx cheat enabled
+ // Editable settings
+ {"keyboardturnspeed", StateKey_KbdRotateSpeed, 5, false, true},
+ {"panarotatespeed", StateKey_RotateSpeed, 540, false, true}, // checked by universe.scr
+ {"noanimwhileturning", StateKey_NoTurnAnim, -1, false, true}, // toggle playing animations during pana rotation
+ {"venusenabled", StateKey_VenusEnable, -1, true, true},
+ {"subtitles", StateKey_Subtitles, -1, true, true},
+ {"mpegmovies", StateKey_MPEGMovies, -1, true, true} // Zork: Grand Inquisitor DVD hi-res MPEG movies (0 = normal, 1 = hires, 2 = disable option)
+};
+
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),
+ _resourcePixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), /* RGB 555 */
+ _screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /* RGB 565 */
+ _desiredFrameTime(33), /* ~30 fps */
+ _clock(_system),
+ _scriptManager(nullptr),
+ _renderManager(nullptr),
+ _saveManager(nullptr),
+ _stringManager(nullptr),
+ _cursorManager(nullptr),
+ _midiManager(nullptr),
+ _rnd(nullptr),
+ _console(nullptr),
+ _menu(nullptr),
+ _searchManager(nullptr),
+ _textRenderer(nullptr),
+ _doubleFPS(false),
+ _audioId(0),
+ _frameRenderDelay(2),
+ _keyboardVelocity(0),
+ _mouseVelocity(0),
+ _videoIsPlaying(false),
+ _renderedFrameCount(0),
+ _fps(0) {
debug(1, "ZVision::ZVision");
+
+ memset(_cheatBuffer, 0, sizeof(_cheatBuffer));
}
ZVision::~ZVision() {
@@ -73,90 +117,237 @@ ZVision::~ZVision() {
delete _cursorManager;
delete _stringManager;
delete _saveManager;
- delete _renderManager;
delete _scriptManager;
+ delete _renderManager; // should be deleted after the script manager
delete _rnd;
+ delete _midiManager;
+
+ getTimerManager()->removeTimerProc(&fpsTimerCallback);
// Remove all of our debug levels
DebugMan.clearAllDebugChannels();
}
+void ZVision::registerDefaultSettings() {
+ for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) {
+ if (settingsKeys[i].allowEditing) {
+ if (settingsKeys[i].defaultValue >= 0)
+ ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultValue);
+ else
+ ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultBoolValue);
+ }
+ }
+
+ ConfMan.registerDefault("originalsaveload", false);
+ ConfMan.registerDefault("doublefps", false);
+}
+
+void ZVision::loadSettings() {
+ int16 value = 0;
+ bool boolValue = false;
+
+ for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) {
+ if (settingsKeys[i].defaultValue >= 0) {
+ value = (settingsKeys[i].allowEditing) ? ConfMan.getInt(settingsKeys[i].name) : settingsKeys[i].defaultValue;
+ } else {
+ boolValue = (settingsKeys[i].allowEditing) ? ConfMan.getBool(settingsKeys[i].name) : settingsKeys[i].defaultBoolValue;
+ value = (boolValue) ? 1 : 0;
+ }
+
+ _scriptManager->setStateValue(settingsKeys[i].slot, value);
+ }
+
+ if (getGameId() == GID_NEMESIS)
+ _scriptManager->setStateValue(StateKey_ExecScopeStyle, 1);
+ else
+ _scriptManager->setStateValue(StateKey_ExecScopeStyle, 0);
+}
+
+void ZVision::saveSettings() {
+ for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) {
+ if (settingsKeys[i].allowEditing) {
+ if (settingsKeys[i].defaultValue >= 0)
+ ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot));
+ else
+ ConfMan.setBool(settingsKeys[i].name, (_scriptManager->getStateValue(settingsKeys[i].slot) == 1));
+ }
+ }
+
+ ConfMan.flushToDisk();
+}
+
void ZVision::initialize() {
const Common::FSNode gameDataDir(ConfMan.get("path"));
- // TODO: There are 10 file clashes when we flatten the directories.
- // From a quick look, the files are exactly the same, so it shouldn't matter.
- // But I'm noting it here just in-case it does become a problem.
- SearchMan.addSubDirectoryMatching(gameDataDir, "data1", 0, 4, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "data2", 0, 4, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "data3", 0, 4, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "zassets1", 0, 2, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "zassets2", 0, 2, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "znemmx", 0, 1, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "zgi", 0, 4, true);
- SearchMan.addSubDirectoryMatching(gameDataDir, "fonts", 0, 1, true);
-
- // Find zfs archive files
- Common::ArchiveMemberList list;
- SearchMan.listMatchingMembers(list, "*.zfs");
-
- // Register the file entries within the zfs archives with the SearchMan
- for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
- Common::String name = (*iter)->getName();
- Common::SeekableReadStream *stream = (*iter)->createReadStream();
- ZfsArchive *archive = new ZfsArchive(name, stream);
-
- delete stream;
-
- SearchMan.add(name, archive);
+
+ _searchManager = new SearchManager(ConfMan.get("path"), 6);
+
+ _searchManager->addDir("FONTS");
+ _searchManager->addDir("addon");
+
+ if (_gameDescription->gameId == GID_GRANDINQUISITOR) {
+ if (!_searchManager->loadZix("INQUIS.ZIX"))
+ error("Unable to load file INQUIS.ZIX");
+ } else if (_gameDescription->gameId == GID_NEMESIS) {
+ if (!_searchManager->loadZix("NEMESIS.ZIX")) {
+ // The game might not be installed, try MEDIUM.ZIX instead
+ if (!_searchManager->loadZix("ZNEMSCR/MEDIUM.ZIX"))
+ error("Unable to load the file ZNEMSCR/MEDIUM.ZIX");
+ }
}
- initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_pixelFormat);
+ initScreen();
// Register random source
_rnd = new Common::RandomSource("zvision");
// 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, _resourcePixelFormat, _doubleFPS);
_saveManager = new SaveManager(this);
_stringManager = new StringManager(this);
- _cursorManager = new CursorManager(this, &_pixelFormat);
+ _cursorManager = new CursorManager(this, _resourcePixelFormat);
+ _textRenderer = new TextRenderer(this);
+ _midiManager = new MidiManager();
+
+ if (_gameDescription->gameId == GID_GRANDINQUISITOR)
+ _menu = new MenuZGI(this);
+ else
+ _menu = new MenuNemesis(this);
// Initialize the managers
_cursorManager->initialize();
_scriptManager->initialize();
_stringManager->initialize(_gameDescription->gameId);
+ registerDefaultSettings();
+
+ loadSettings();
+
+#ifndef USE_MPEG2
+ // libmpeg2 not loaded, disable the MPEG2 movies option
+ _scriptManager->setStateValue(StateKey_MPEGMovies, 2);
+#endif
+
// Create debugger console. It requires GFX to be initialized
_console = new Console(this);
+ _doubleFPS = ConfMan.getBool("doublefps");
+
+ // Initialize FPS timer callback
+ getTimerManager()->installTimerProc(&fpsTimerCallback, 1000000, this, "zvisionFPS");
}
+extern const FontStyle getSystemFont(int fontIndex);
+
Common::Error ZVision::run() {
initialize();
+ // Check if a saved game is to be loaded from the launcher
+ if (ConfMan.hasKey("save_slot"))
+ _saveManager->loadGame(ConfMan.getInt("save_slot"));
+
+ bool foundAllFonts = true;
+
+ // Before starting, make absolutely sure that the user has copied the needed fonts
+ for (int i = 0; i < FONT_COUNT; i++) {
+ FontStyle curFont = getSystemFont(i);
+ Common::String freeFontBoldItalic = Common::String("Bold") + curFont.freeFontItalicName;
+
+ const char *fontSuffixes[4] = { "", "bd", "i", "bi" };
+ const char *freeFontSuffixes[4] = { "", "Bold", curFont.freeFontItalicName, freeFontBoldItalic.c_str() };
+ const char *liberationFontSuffixes[4] = { "-Regular", "-Bold", "-Italic", "-BoldItalic" };
+
+ for (int j = 0; j < 4; j++) {
+ Common::String fontName = curFont.fontBase;
+ if (fontName == "censcbk" && j > 0)
+ fontName = "schlbk";
+ fontName += fontSuffixes[j];
+ fontName += ".ttf";
+
+ if (fontName == "schlbkbd.ttf")
+ fontName = "schlbkb.ttf";
+ if (fontName == "garabi.ttf")
+ continue;
+ if (fontName == "garai.ttf")
+ fontName = "garait.ttf";
+
+ Common::String freeFontName = curFont.freeFontBase;
+ freeFontName += freeFontSuffixes[j];
+ freeFontName += ".ttf";
+
+ Common::String liberationFontName = curFont.liberationFontBase;
+ liberationFontName += liberationFontSuffixes[j];
+ liberationFontName += ".ttf";
+
+ if (!Common::File::exists(fontName) && !_searchManager->hasFile(fontName) &&
+ !Common::File::exists(liberationFontName) && !_searchManager->hasFile(liberationFontName) &&
+ !Common::File::exists(freeFontName) && !_searchManager->hasFile(freeFontName)) {
+ foundAllFonts = false;
+ break;
+ }
+ }
+
+ if (!foundAllFonts)
+ break;
+ }
+
+ if (!foundAllFonts) {
+ GUI::MessageDialog dialog(
+ "Before playing this game, you'll need to copy the required "
+ "fonts into ScummVM's extras directory, or into the game directory. "
+ "On Windows, you'll need the following font files from the Windows "
+ "font directory: Times New Roman, Century Schoolbook, Garamond, "
+ "Courier New and Arial. Alternatively, you can download the "
+ "Liberation Fonts or the GNU FreeFont package. You'll need all the "
+ "fonts from the font package you choose, i.e., LiberationMono, "
+ "LiberationSans and LiberationSerif, or FreeMono, FreeSans and "
+ "FreeSerif respectively."
+ );
+ dialog.runModal();
+ quitGame();
+ return Common::kUnknownError;
+ }
+
// Main loop
while (!shouldQuit()) {
_clock.update();
uint32 currentTime = _clock.getLastMeasuredTime();
uint32 deltaTime = _clock.getDeltaTime();
+ _cursorManager->setItemID(_scriptManager->getStateValue(StateKey_InventoryItem));
+
processEvents();
+ _renderManager->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->renderBackbufferToScreen();
+ _renderManager->prepareBackground();
+ _renderManager->renderMenuToScreen();
+ _renderManager->processSubs(deltaTime);
+ _renderManager->renderSceneToScreen();
// Update the screen
- _system->updateScreen();
+ if (canRender()) {
+ _system->updateScreen();
+ _renderedFrameCount++;
+ } else {
+ _frameRenderDelay--;
+ }
// Calculate the frame delay based off a desired frame time
int delay = _desiredFrameTime - int32(_system->getMillis() - currentTime);
// Ensure non-negative
delay = delay < 0 ? 0 : delay;
+
+ if (_doubleFPS) {
+ delay >>= 1;
+ }
+
+ if (canSaveGameStateCurrently() && shouldPerformAutoSave(_saveManager->getLastSaveTime())) {
+ _saveManager->autoSave();
+ }
+
_system->delayMillis(delay);
}
@@ -174,11 +365,53 @@ void ZVision::pauseEngineIntern(bool pause) {
}
Common::String ZVision::generateSaveFileName(uint slot) {
- return Common::String::format("%s.%02u", _targetName.c_str(), slot);
+ return Common::String::format("%s.%03u", _targetName.c_str(), slot);
+}
+
+void ZVision::setRenderDelay(uint delay) {
+ _frameRenderDelay = delay;
}
-Common::String ZVision::generateAutoSaveFileName() {
- return Common::String::format("%s.auto", _targetName.c_str());
+bool ZVision::canRender() {
+ return _frameRenderDelay <= 0;
+}
+
+GUI::Debugger *ZVision::getDebugger() {
+ return _console;
+}
+
+void ZVision::syncSoundSettings() {
+ Engine::syncSoundSettings();
+
+ _scriptManager->setStateValue(StateKey_Subtitles, ConfMan.getBool("subtitles") ? 1 : 0);
+}
+
+void ZVision::fpsTimerCallback(void *refCon) {
+ ((ZVision *)refCon)->fpsTimer();
+}
+
+void ZVision::fpsTimer() {
+ _fps = _renderedFrameCount;
+ _renderedFrameCount = 0;
+}
+
+void ZVision::initScreen() {
+ uint16 workingWindowWidth = (_gameDescription->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_WIDTH : ZGI_WORKING_WINDOW_WIDTH;
+ uint16 workingWindowHeight = (_gameDescription->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_HEIGHT : ZGI_WORKING_WINDOW_HEIGHT;
+ _workingWindow = Common::Rect(
+ (WINDOW_WIDTH - workingWindowWidth) / 2,
+ (WINDOW_HEIGHT - workingWindowHeight) / 2,
+ ((WINDOW_WIDTH - workingWindowWidth) / 2) + workingWindowWidth,
+ ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight
+ );
+
+ initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat);
+}
+
+void ZVision::initHiresScreen() {
+ _renderManager->upscaleRect(_workingWindow);
+
+ initGraphics(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, true, &_screenPixelFormat);
}
} // End of namespace ZVision
diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h
index 0b5a86f370..854cd77bb8 100644
--- a/engines/zvision/zvision.h
+++ b/engines/zvision/zvision.h
@@ -8,24 +8,25 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
+ *
*/
#ifndef ZVISION_ZVISION_H
#define ZVISION_ZVISION_H
-#include "zvision/core/console.h"
#include "zvision/detection.h"
-#include "zvision/utility/clock.h"
+#include "zvision/core/clock.h"
+#include "zvision/file/search_manager.h"
#include "common/random.h"
#include "common/events.h"
@@ -36,20 +37,56 @@
#include "gui/debugger.h"
-
namespace Video {
class VideoDecoder;
}
+/**
+ * This is the namespace of the ZVision engine.
+ *
+ * Status of this engine: complete
+ *
+ * Games using this engine:
+ * - Zork Nemesis: The Forbidden Lands
+ * - Zork: Grand Inquisitor
+ *
+ */
+
namespace ZVision {
struct ZVisionGameDescription;
+class Console;
class ScriptManager;
class RenderManager;
class CursorManager;
class StringManager;
class SaveManager;
-class RlfAnimation;
+class RLFDecoder;
+class MenuHandler;
+class TextRenderer;
+class Subtitle;
+class MidiManager;
+
+enum {
+ WINDOW_WIDTH = 640,
+ WINDOW_HEIGHT = 480,
+
+ HIRES_WINDOW_WIDTH = 800,
+ HIRES_WINDOW_HEIGHT = 600,
+
+ // Zork nemesis working window sizes
+ ZNM_WORKING_WINDOW_WIDTH = 512,
+ ZNM_WORKING_WINDOW_HEIGHT = 320,
+
+ // ZGI working window sizes
+ ZGI_WORKING_WINDOW_WIDTH = 640,
+ ZGI_WORKING_WINDOW_HEIGHT = 344,
+
+ ROTATION_SCREEN_EDGE_OFFSET = 60,
+ MAX_ROTATION_SPEED = 400, // Pixels per second
+
+ KEYBUF_SIZE = 20
+};
class ZVision : public Engine {
public:
@@ -62,26 +99,11 @@ public:
* are given in this coordinate space. Also, all images are clipped to the
* edges of this Rectangle
*/
- const Common::Rect _workingWindow;
- const Graphics::PixelFormat _pixelFormat;
+ Common::Rect _workingWindow;
+ const Graphics::PixelFormat _resourcePixelFormat;
+ const Graphics::PixelFormat _screenPixelFormat;
private:
- enum {
- WINDOW_WIDTH = 640,
- WINDOW_HEIGHT = 480,
-
- //Zork nemesis working window sizes
- ZNEM_WORKING_WINDOW_WIDTH = 512,
- ZNEM_WORKING_WINDOW_HEIGHT = 320,
-
- //ZGI(and default) working window sizes
- ZGI_WORKING_WINDOW_WIDTH = 640,
- ZGI_WORKING_WINDOW_HEIGHT = 344,
-
- ROTATION_SCREEN_EDGE_OFFSET = 60,
- MAX_ROTATION_SPEED = 400 // Pixels per second
- };
-
Console *_console;
const ZVisionGameDescription *_gameDescription;
@@ -96,27 +118,87 @@ private:
CursorManager *_cursorManager;
SaveManager *_saveManager;
StringManager *_stringManager;
+ MenuHandler *_menu;
+ SearchManager *_searchManager;
+ TextRenderer *_textRenderer;
+ MidiManager *_midiManager;
// Clock
Clock _clock;
+ // Audio ID
+ int _audioId;
+
// To prevent allocation every time we process events
Common::Event _event;
+ int _frameRenderDelay;
+ int _renderedFrameCount;
+ int _fps;
+ int16 _mouseVelocity;
+ int16 _keyboardVelocity;
+ bool _doubleFPS;
+ bool _videoIsPlaying;
+
+ uint8 _cheatBuffer[KEYBUF_SIZE];
public:
uint32 getFeatures() const;
Common::Language getLanguage() const;
Common::Error run();
void pauseEngineIntern(bool pause);
- ScriptManager *getScriptManager() const { return _scriptManager; }
- RenderManager *getRenderManager() const { return _renderManager; }
- CursorManager *getCursorManager() const { return _cursorManager; }
- SaveManager *getSaveManager() const { return _saveManager; }
- StringManager *getStringManager() const { return _stringManager; }
- Common::RandomSource *getRandomSource() const { return _rnd; }
- ZVisionGameId getGameId() const { return _gameDescription->gameId; }
- GUI::Debugger *getDebugger() { return _console; }
+ ScriptManager *getScriptManager() const {
+ return _scriptManager;
+ }
+ RenderManager *getRenderManager() const {
+ return _renderManager;
+ }
+ CursorManager *getCursorManager() const {
+ return _cursorManager;
+ }
+ SaveManager *getSaveManager() const {
+ return _saveManager;
+ }
+ StringManager *getStringManager() const {
+ return _stringManager;
+ }
+ SearchManager *getSearchManager() const {
+ return _searchManager;
+ }
+ TextRenderer *getTextRenderer() const {
+ return _textRenderer;
+ }
+ MidiManager *getMidiManager() const {
+ return _midiManager;
+ }
+ MenuHandler *getMenuHandler() const {
+ return _menu;
+ }
+ Common::RandomSource *getRandomSource() const {
+ return _rnd;
+ }
+ ZVisionGameId getGameId() const {
+ return _gameDescription->gameId;
+ }
+ int16 getKeyboardVelocity() const {
+ return _keyboardVelocity;
+ }
+ int16 getMouseVelocity() const {
+ return _mouseVelocity;
+ }
+
+ uint8 getZvisionKey(Common::KeyCode scummKeyCode);
+
+ void startClock() {
+ _clock.start();
+ }
+
+ void stopClock() {
+ _clock.stop();
+ }
+
+ void initScreen();
+ void initHiresScreen();
/**
* Play a video until it is finished. This is a blocking call. It will call
@@ -127,11 +209,33 @@ 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);
+ Video::VideoDecoder *loadAnimation(const Common::String &fileName);
Common::String generateSaveFileName(uint slot);
- Common::String generateAutoSaveFileName();
+ void setRenderDelay(uint);
+ bool canRender();
+ static void fpsTimerCallback(void *refCon);
+ void fpsTimer();
+ int getFPS() const {
+ return _fps;
+ }
+
+ GUI::Debugger *getDebugger();
+ void syncSoundSettings();
+
+ void loadSettings();
+ void saveSettings();
+
+ bool ifQuit();
+
+ // Engine features
+ bool hasFeature(EngineFeature f) const;
+ bool canLoadGameStateCurrently();
+ bool canSaveGameStateCurrently();
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &desc);
private:
void initialize();
void initFonts();
@@ -141,9 +245,15 @@ private:
/** Called every frame from ZVision::run() to process any events from EventMan */
void processEvents();
- void onMouseDown(const Common::Point &pos);
- void onMouseUp(const Common::Point &pos);
void onMouseMove(const Common::Point &pos);
+
+ void registerDefaultSettings();
+ void shortKeys(Common::Event);
+
+ void cheatCodes(uint8 key);
+ void pushKeyToCheatBuf(uint8 key);
+ bool checkCode(const char *code);
+ uint8 getBufferedKey(uint8 pos);
};
} // End of namespace ZVision