diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/zvision/control.cpp | 401 | ||||
-rw-r--r-- | engines/zvision/control.h | 136 | ||||
-rw-r--r-- | engines/zvision/lever_control.cpp | 349 | ||||
-rw-r--r-- | engines/zvision/lever_control.h | 130 | ||||
-rw-r--r-- | engines/zvision/module.mk | 2 | ||||
-rw-r--r-- | engines/zvision/push_toggle_control.cpp | 104 | ||||
-rw-r--r-- | engines/zvision/push_toggle_control.h | 77 | ||||
-rw-r--r-- | engines/zvision/scr_file_handling.cpp | 1 |
8 files changed, 665 insertions, 535 deletions
diff --git a/engines/zvision/control.cpp b/engines/zvision/control.cpp index 9a1d52bd4f..1cef2a0ab8 100644 --- a/engines/zvision/control.cpp +++ b/engines/zvision/control.cpp @@ -23,24 +23,14 @@ #include "common/scummsys.h" #include "common/stream.h" -#include "common/system.h" -#include "common/tokenizer.h" +#include "zvision/control.h" #include "zvision/zvision.h" #include "zvision/render_manager.h" -#include "zvision/render_table.h" -#include "zvision/script_manager.h" -#include "zvision/cursor_manager.h" -#include "zvision/control.h" #include "zvision/utility.h" -#include "zvision/rlf_animation.h" -#include "zvision/zork_avi_decoder.h" -namespace ZVision { -////////////////////////////////////////////////////////////////////////////// -// Static member functions -////////////////////////////////////////////////////////////////////////////// +namespace ZVision { void Control::parseFlatControl(ZVision *engine) { engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT); @@ -112,391 +102,4 @@ void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stre renderTable->generateRenderTable(); } - -////////////////////////////////////////////////////////////////////////////// -// PushToggleControl -////////////////////////////////////////////////////////////////////////////// - -PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : MouseEvent(key), - _engine(engine) { - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.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); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - if (_hotspot.isEmpty() || _hoverCursor.empty()) { - warning("Push_toggle cursor %u was parsed incorrectly", key); - } -} - -bool PushToggleControl::enable() { - if (!_enabled) { - _engine->registerMouseEvent(this); - _enabled = true; - return true; - } - - debug("Control %u is already enabled", _key); - return false; -} - -bool PushToggleControl::disable() { - if (_enabled) { - _engine->removeMouseEvent(_key); - _enabled = false; - return true; - } - - debug("Control %u is already disabled", _key); - return false; -} - -void PushToggleControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { - _engine->getScriptManager()->setStateValue(_key, 1); -} - -bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { - if (_hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_hoverCursor); - return true; - } - - return false; -} - -////////////////////////////////////////////////////////////////////////////// -// LeverControl -////////////////////////////////////////////////////////////////////////////// - -LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) - : MouseEvent(key), - _engine(engine), - _frameInfo(0), - _frameCount(0), - _startFrame(0), - _currentFrame(0), - _mouseIsCaptured(false), - _isReturning(false) { - - // Loop until we find the closing brace - Common::String line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - - while (!stream.eos() && !line.contains('}')) { - if (line.matchString("*descfile*", true)) { - char levFileName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName); - - parseLevFile(levFileName); - } else if (line.matchString("*cursor*", true)) { - char cursorName[25]; - sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName); - - _cursorName = Common::String(cursorName); - } - - line = stream.readLine(); - trimCommentsAndWhiteSpace(&line); - } - - renderFrame(_currentFrame); - - _engine->getScriptManager()->addActionNode(this); -} - -LeverControl::~LeverControl() { - if (_fileType == AVI) { - delete _animation.avi; - } else if (_fileType == RLF) { - delete _animation.rlf; - } - if (_frameInfo != 0) { - delete[] _frameInfo; - } -} - -void LeverControl::parseLevFile(const Common::String &fileName) { - Common::File file; - if (!file.open(fileName)) { - warning("LEV file %s could could be opened", fileName.c_str()); - return; - } - - Common::String line = file.readLine(); - - while (!file.eos()) { - if (line.matchString("*animation_id*", true)) { - // Not used - } else if (line.matchString("*filename*", true)) { - char fileNameBuffer[25]; - sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); - - Common::String fileName(fileNameBuffer); - - if (fileName.hasSuffix(".avi")) { - _animation.avi = new ZorkAVIDecoder(); - _animation.avi->loadFile(fileName); - } else if (fileName.hasSuffix(".rlf")) { - _animation.rlf = new RlfAnimation(fileNameBuffer, false); - } - } else if (line.matchString("*skipcolor*", true)) { - // Not used - } else if (line.matchString("*anim_coords*", true)) { - sscanf(line.c_str(), "%*[^:]:%u %u %u %u~", &_animationCoords.left, &_animationCoords.top, &_animationCoords.right, &_animationCoords.bottom); - } else if (line.matchString("*mirrored*", true)) { - uint mirrored; - sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); - - _mirrored = mirrored == 0 ? false : true; - } else if (line.matchString("*frames*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); - - _frameInfo = new FrameInfo[_frameCount]; - } else if (line.matchString("*elsewhere*", true)) { - // Not used - } else if (line.matchString("*out_of_control*", true)) { - // Not used - } else if (line.matchString("*start_pos*", true)) { - sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); - _currentFrame = _startFrame; - } else if (line.matchString("*hotspot_deltas*", true)) { - uint x; - uint y; - sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); - - _hotspotDelta.x = x; - _hotspotDelta.y = y; - } else { - uint frameNumber; - uint x, y; - - if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { - _frameInfo[frameNumber].hotspot.left = x; - _frameInfo[frameNumber].hotspot.top = y; - _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; - _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; - } - - Common::StringTokenizer tokenizer(line, " ^="); - tokenizer.nextToken(); - tokenizer.nextToken(); - - Common::String token = tokenizer.nextToken(); - while (!tokenizer.empty()) { - if (token == "D") { - token = tokenizer.nextToken(); - - uint angle; - uint toFrame; - sscanf(token.c_str(), "%u,%u", &toFrame, &angle); - - _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); - } else if (token.hasPrefix("P")) { - uint to; - sscanf(token.c_str(), "P(%*u to %u)", &to); - - _frameInfo[frameNumber].returnRoute.push_back(to); - } - - token = tokenizer.nextToken(); - } - } - - line = file.readLine(); - } -} - -void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { - if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _mouseIsCaptured = true; - _lastMousePos = backgroundImageSpacePos; - } -} - -void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { - _mouseIsCaptured = false; - _engine->getScriptManager()->setStateValue(_key, _currentFrame); - - if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - - } - - // TODO: Animation reversal back to origin -} - -bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { - bool cursorWasChanged = false; - - if (_mouseIsCaptured) { - // Make sure the square distance between the last point and the current point is greater than 64 - // 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) { - int angle = calculateVectorAngle(_lastMousePos, backgroundImageSpacePos); - _lastMousePos = backgroundImageSpacePos; - - for (Common::List<Direction>::iterator iter = _frameInfo[_currentFrame].directions.begin(); iter != _frameInfo[_currentFrame].directions.end(); iter++) { - if (angle >= (int)(*iter).angle - ANGLE_DELTA && angle <= (int)(*iter).angle) { - _currentFrame = (*iter).toFrame; - renderFrame(_currentFrame); - break; - } - } - } - } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { - _engine->getCursorManager()->changeCursor(_cursorName); - } - - return cursorWasChanged; -} - -bool LeverControl::process(uint32 deltaTimeInMillis) { - // TODO: Implement reversal over time - - return false; -} - -int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo) { - // Check for the easy angles first - if (pointOne.x == pointTwo.x && pointOne.y == pointTwo.y) - return -1; // This should never happen - else if (pointOne.x == pointTwo.x) { - if (pointTwo.y > pointOne.y) - return 90; - else - return 270; - } else if (pointOne.y == pointTwo.y) { - if (pointTwo.x > pointOne.x) - return 0; - else - return 180; - } else { - // Calculate the angle with trig - int16 xDist = pointTwo.x - pointOne.x; - int16 yDist = pointTwo.y - pointOne.y; - - int angle = int(atan((float)yDist / (float)xDist)); - - // Convert to degrees. (180 / 3.14159 = 57.2958) - angle *= 57; - - // Calculate what quadrant pointTwo is in - uint quadrant = ((yDist > 0 ? 1 : 0) << 1) | (xDist < 0 ? 1 : 0); - - // Explanation of quadrants: - // - // yDist > 0 | xDist < 0 | Quadrant number - // 0 | 0 | 0 - // 0 | 1 | 1 - // 1 | 0 | 2 - // 1 | 1 | 3 - // - // Note: I know this doesn't line up with traditional mathematical quadrants - // but doing it this way allows you can use a switch and is a bit cleaner IMO. - // - // The graph below shows the 4 quadrants pointTwo can end up in as well - // as what the angle as calculated above refers to. - // Note: The calculated angle in quadrants 0 and 3 is negative - // due to arctan(-x) = -theta - // - // Origin => (pointOne.x, pointOne.y) - // * => (pointTwo.x, pointTwo.y) - // - // 90 - // ^ - // * | * - // \ | / - // \ | / - // \ | / - // Quadrant 3 \ | / Quadrant 2 - // \ | / - // \ | / - // -angle ( \|/ ) angle - // 180 <----------------------------------------> 0 - // angle ( /|\ ) -angle - // / | \ - // / | \ - // Quadrant 1 / | \ Quadrant 0 - // / | \ - // / | \ - // / | \ - // * | * - // ^ - // 270 - - // Convert the local angles to unit circle angles - switch (quadrant) { - case 0: - angle = 360 + angle; - break; - case 1: - angle = 180 + angle; - break; - case 2: - angle = 180 + angle; - break; - case 3: - // Do nothing - break; - } - - return angle; - } -} - -void LeverControl::renderFrame(uint frameNumber) { - const uint16 *frameData; - int pitch; - int x; - int y; - int width; - int height; - - if (_fileType == RLF) { - // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can - frameData = _animation.rlf->getFrameData(_currentFrame); - pitch = _animation.rlf->width() * sizeof(uint16); - x = _animationCoords.left; - y = _animationCoords.right; - 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) { - // Cry because AVI seeking isn't implemented (yet) - } - - _engine->_system->copyRectToScreen(frameData, pitch, x + _engine->_workingWindow.left, y + _engine->_workingWindow.top, width, height); -} - -bool LeverControl::enable() { - // TODO: Implement - return true; -} - -bool LeverControl::disable() { - // TODO: Implement - return true; -} - } // End of namespace ZVision diff --git a/engines/zvision/control.h b/engines/zvision/control.h index d764f914bb..616589f055 100644 --- a/engines/zvision/control.h +++ b/engines/zvision/control.h @@ -25,9 +25,6 @@ #include "common/types.h" -#include "zvision/mouse_event.h" -#include "zvision/action_node.h" - namespace Common { class SeekableReadStream; } @@ -35,8 +32,6 @@ class SeekableReadStream; namespace ZVision { class ZVision; -class RlfAnimation; -class ZorkAVIDecoder; class Control { public: @@ -55,137 +50,6 @@ public: static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream); }; - -class PushToggleControl : public Control, public MouseEvent { -public: - PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); - bool enable(); - bool disable(); - - /** - * Called when LeftMouse is pushed. 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 onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos); - /** - * Called when LeftMouse is lifted. Does nothing - * - * @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) {} - /** - * 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); - -private: - ZVision * _engine; - /** - * The area that will trigger the event - * This is in image space coordinates, NOT screen space - */ - Common::Rect _hotspot; - /** The cursor to use when hovering over _hotspot */ - Common::String _hoverCursor; -}; - - -class LeverControl : public Control, public MouseEvent, public ActionNode { -public: - LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); - ~LeverControl(); - -private: - enum FileType { - RLF = 1, - AVI = 2 - }; - - struct Direction { - Direction(uint angle, uint toFrame) : angle(angle), toFrame(toFrame) {} - - uint angle; - uint toFrame; - }; - - struct FrameInfo { - Common::Rect hotspot; - Common::List<Direction> directions; - Common::List<uint> returnRoute; - }; - - enum { - ANGLE_DELTA = 30 // How far off a mouse angle can be and still be considered valid. This is in both directions, so the total buffer zone is (2 * ANGLE_DELTA) - }; - -private: - ZVision *_engine; - - union { - RlfAnimation *rlf; - ZorkAVIDecoder *avi; - } _animation; - FileType _fileType; - - Common::String _cursorName; - Common::Rect _animationCoords; - bool _mirrored; - uint _frameCount; - uint _startFrame; - Common::Point _hotspotDelta; - FrameInfo *_frameInfo; - - uint _currentFrame; - bool _mouseIsCaptured; - bool _isReturning; - Common::Point _lastMousePos; - Common::List<uint>::iterator _returnRoutesCurrentProgress; - -public: - bool enable(); - bool disable(); - void onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos); - void 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 parseLevFile(const Common::String &fileName); - /** - * Calculates the angle a vector makes with the negative y-axis - * - * 90 - * pointTwo * ^ - * \ | - * \ | - * \ | - * \ | - * angle ( \|pointOne - * 180 <-----------*-----------> 0 - * | - * | - * | - * | - * | - * ^ - * 270 - * - * @param pointOne The origin of the vector - * @param pointTwo The end of the vector - * @return The angle the vector makes with the negative y-axis - */ - static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo); - void renderFrame(uint frameNumber); -}; - // TODO: Implement InputControl // TODO: Implement SaveControl // TODO: Implement SlotControl diff --git a/engines/zvision/lever_control.cpp b/engines/zvision/lever_control.cpp new file mode 100644 index 0000000000..34877cd23f --- /dev/null +++ b/engines/zvision/lever_control.cpp @@ -0,0 +1,349 @@ +/* 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/stream.h" +#include "common/file.h" +#include "common/tokenizer.h" +#include "common/system.h" + +#include "zvision/lever_control.h" +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/rlf_animation.h" +#include "zvision/zork_avi_decoder.h" +#include "zvision/utility.h" + +namespace ZVision { + +LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : MouseEvent(key), + _engine(engine), + _frameInfo(0), + _frameCount(0), + _startFrame(0), + _currentFrame(0), + _mouseIsCaptured(false), + _isReturning(false) { + + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.matchString("*descfile*", true)) { + char levFileName[25]; + sscanf(line.c_str(), "%*[^(](%25[^)])", levFileName); + + parseLevFile(levFileName); + } else if (line.matchString("*cursor*", true)) { + char cursorName[25]; + sscanf(line.c_str(), "%*[^(](%25[^)])", cursorName); + + _cursorName = Common::String(cursorName); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + renderFrame(_currentFrame); + + _engine->getScriptManager()->addActionNode(this); +} + +LeverControl::~LeverControl() { + if (_fileType == AVI) { + delete _animation.avi; + } else if (_fileType == RLF) { + delete _animation.rlf; + } + if (_frameInfo != 0) { + delete[] _frameInfo; + } +} + +void LeverControl::parseLevFile(const Common::String &fileName) { + Common::File file; + if (!file.open(fileName)) { + warning("LEV file %s could could be opened", fileName.c_str()); + return; + } + + Common::String line = file.readLine(); + + while (!file.eos()) { + if (line.matchString("*animation_id*", true)) { + // Not used + } else if (line.matchString("*filename*", true)) { + char fileNameBuffer[25]; + sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); + + Common::String fileName(fileNameBuffer); + + if (fileName.hasSuffix(".avi")) { + _animation.avi = new ZorkAVIDecoder(); + _animation.avi->loadFile(fileName); + } else if (fileName.hasSuffix(".rlf")) { + _animation.rlf = new RlfAnimation(fileNameBuffer, false); + } + } else if (line.matchString("*skipcolor*", true)) { + // Not used + } else if (line.matchString("*anim_coords*", true)) { + sscanf(line.c_str(), "%*[^:]:%u %u %u %u~", &_animationCoords.left, &_animationCoords.top, &_animationCoords.right, &_animationCoords.bottom); + } else if (line.matchString("*mirrored*", true)) { + uint mirrored; + sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); + + _mirrored = mirrored == 0 ? false : true; + } else if (line.matchString("*frames*", true)) { + sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); + + _frameInfo = new FrameInfo[_frameCount]; + } else if (line.matchString("*elsewhere*", true)) { + // Not used + } else if (line.matchString("*out_of_control*", true)) { + // Not used + } else if (line.matchString("*start_pos*", true)) { + sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); + _currentFrame = _startFrame; + } else if (line.matchString("*hotspot_deltas*", true)) { + uint x; + uint y; + sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); + + _hotspotDelta.x = x; + _hotspotDelta.y = y; + } else { + uint frameNumber; + uint x, y; + + if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { + _frameInfo[frameNumber].hotspot.left = x; + _frameInfo[frameNumber].hotspot.top = y; + _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; + _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; + } + + Common::StringTokenizer tokenizer(line, " ^="); + tokenizer.nextToken(); + tokenizer.nextToken(); + + Common::String token = tokenizer.nextToken(); + while (!tokenizer.empty()) { + if (token == "D") { + token = tokenizer.nextToken(); + + uint angle; + uint toFrame; + sscanf(token.c_str(), "%u,%u", &toFrame, &angle); + + _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); + } else if (token.hasPrefix("P")) { + uint to; + sscanf(token.c_str(), "P(%*u to %u)", &to); + + _frameInfo[frameNumber].returnRoute.push_back(to); + } + + token = tokenizer.nextToken(); + } + } + + line = file.readLine(); + } +} + +void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { + if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + _mouseIsCaptured = true; + _lastMousePos = backgroundImageSpacePos; + } +} + +void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { + _mouseIsCaptured = false; + _engine->getScriptManager()->setStateValue(_key, _currentFrame); + + if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + + } + + // TODO: Animation reversal back to origin +} + +bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { + bool cursorWasChanged = false; + + if (_mouseIsCaptured) { + // Make sure the square distance between the last point and the current point is greater than 64 + // 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) { + int angle = calculateVectorAngle(_lastMousePos, backgroundImageSpacePos); + _lastMousePos = backgroundImageSpacePos; + + for (Common::List<Direction>::iterator iter = _frameInfo[_currentFrame].directions.begin(); iter != _frameInfo[_currentFrame].directions.end(); iter++) { + if (angle >= (int)(*iter).angle - ANGLE_DELTA && angle <= (int)(*iter).angle) { + _currentFrame = (*iter).toFrame; + renderFrame(_currentFrame); + break; + } + } + } + } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_cursorName); + } + + return cursorWasChanged; +} + +bool LeverControl::process(uint32 deltaTimeInMillis) { + // TODO: Implement reversal over time + + return false; +} + +int LeverControl::calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo) { + // Check for the easy angles first + if (pointOne.x == pointTwo.x && pointOne.y == pointTwo.y) + return -1; // This should never happen + else if (pointOne.x == pointTwo.x) { + if (pointTwo.y > pointOne.y) + return 90; + else + return 270; + } else if (pointOne.y == pointTwo.y) { + if (pointTwo.x > pointOne.x) + return 0; + else + return 180; + } else { + // Calculate the angle with trig + int16 xDist = pointTwo.x - pointOne.x; + int16 yDist = pointTwo.y - pointOne.y; + + int angle = int(atan((float)yDist / (float)xDist)); + + // Convert to degrees. (180 / 3.14159 = 57.2958) + angle *= 57; + + // Calculate what quadrant pointTwo is in + uint quadrant = ((yDist > 0 ? 1 : 0) << 1) | (xDist < 0 ? 1 : 0); + + // Explanation of quadrants: + // + // yDist > 0 | xDist < 0 | Quadrant number + // 0 | 0 | 0 + // 0 | 1 | 1 + // 1 | 0 | 2 + // 1 | 1 | 3 + // + // Note: I know this doesn't line up with traditional mathematical quadrants + // but doing it this way allows you can use a switch and is a bit cleaner IMO. + // + // The graph below shows the 4 quadrants pointTwo can end up in as well + // as what the angle as calculated above refers to. + // Note: The calculated angle in quadrants 0 and 3 is negative + // due to arctan(-x) = -theta + // + // Origin => (pointOne.x, pointOne.y) + // * => (pointTwo.x, pointTwo.y) + // + // 90 + // ^ + // * | * + // \ | / + // \ | / + // \ | / + // Quadrant 3 \ | / Quadrant 2 + // \ | / + // \ | / + // -angle ( \|/ ) angle + // 180 <----------------------------------------> 0 + // angle ( /|\ ) -angle + // / | \ + // / | \ + // Quadrant 1 / | \ Quadrant 0 + // / | \ + // / | \ + // / | \ + // * | * + // ^ + // 270 + + // Convert the local angles to unit circle angles + switch (quadrant) { + case 0: + angle = 360 + angle; + break; + case 1: + angle = 180 + angle; + break; + case 2: + angle = 180 + angle; + break; + case 3: + // Do nothing + break; + } + + return angle; + } +} + +void LeverControl::renderFrame(uint frameNumber) { + const uint16 *frameData; + int pitch; + int x; + int y; + int width; + int height; + + if (_fileType == RLF) { + // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can + frameData = _animation.rlf->getFrameData(_currentFrame); + pitch = _animation.rlf->width() * sizeof(uint16); + x = _animationCoords.left; + y = _animationCoords.right; + 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) { + // Cry because AVI seeking isn't implemented (yet) + } + + _engine->_system->copyRectToScreen(frameData, pitch, x + _engine->_workingWindow.left, y + _engine->_workingWindow.top, width, height); +} + +bool LeverControl::enable() { + // TODO: Implement + return true; +} + +bool LeverControl::disable() { + // TODO: Implement + return true; +} + +} // End of namespace ZVision diff --git a/engines/zvision/lever_control.h b/engines/zvision/lever_control.h new file mode 100644 index 0000000000..dbfd172c45 --- /dev/null +++ b/engines/zvision/lever_control.h @@ -0,0 +1,130 @@ +/* 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_PUSH_TOGGLE_CONTROL_H +#define ZVISION_PUSH_TOGGLE_CONTROL_H + +#include "common/types.h" + +#include "common/list.h" + +#include "zvision/control.h" +#include "zvision/mouse_event.h" +#include "zvision/action_node.h" + + +namespace ZVision { + +class ZorkAVIDecoder; +class RlfAnimation; + +class LeverControl : public Control, public MouseEvent, public ActionNode { +public: + LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~LeverControl(); + +private: + enum FileType { + RLF = 1, + AVI = 2 + }; + + struct Direction { + Direction(uint angle, uint toFrame) : angle(angle), toFrame(toFrame) {} + + uint angle; + uint toFrame; + }; + + struct FrameInfo { + Common::Rect hotspot; + Common::List<Direction> directions; + Common::List<uint> returnRoute; + }; + + enum { + ANGLE_DELTA = 30 // How far off a mouse angle can be and still be considered valid. This is in both directions, so the total buffer zone is (2 * ANGLE_DELTA) + }; + +private: + ZVision *_engine; + + union { + RlfAnimation *rlf; + ZorkAVIDecoder *avi; + } _animation; + FileType _fileType; + + Common::String _cursorName; + Common::Rect _animationCoords; + bool _mirrored; + uint _frameCount; + uint _startFrame; + Common::Point _hotspotDelta; + FrameInfo *_frameInfo; + + uint _currentFrame; + bool _mouseIsCaptured; + bool _isReturning; + Common::Point _lastMousePos; + Common::List<uint>::iterator _returnRoutesCurrentProgress; + +public: + bool enable(); + bool disable(); + void onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos); + void 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 parseLevFile(const Common::String &fileName); + /** + * Calculates the angle a vector makes with the negative y-axis + * + * 90 + * pointTwo * ^ + * \ | + * \ | + * \ | + * \ | + * angle ( \|pointOne + * 180 <-----------*-----------> 0 + * | + * | + * | + * | + * | + * ^ + * 270 + * + * @param pointOne The origin of the vector + * @param pointTwo The end of the vector + * @return The angle the vector makes with the negative y-axis + */ + static int calculateVectorAngle(const Common::Point &pointOne, const Common::Point &pointTwo); + void renderFrame(uint frameNumber); +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/module.mk b/engines/zvision/module.mk index a4b7c68cde..7412ad8916 100644 --- a/engines/zvision/module.mk +++ b/engines/zvision/module.mk @@ -11,7 +11,9 @@ MODULE_OBJS := \ cursor_manager.o \ detection.o \ events.o \ + lever_control.o \ lzss_read_stream.o \ + push_toggle_control.o \ render_manager.o \ render_table.o \ rlf_animation.o \ diff --git a/engines/zvision/push_toggle_control.cpp b/engines/zvision/push_toggle_control.cpp new file mode 100644 index 0000000000..9e68bdb8be --- /dev/null +++ b/engines/zvision/push_toggle_control.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 "common/stream.h" + +#include "zvision/push_toggle_control.h" +#include "zvision/zvision.h" +#include "zvision/script_manager.h" +#include "zvision/cursor_manager.h" +#include "zvision/utility.h" + +namespace ZVision { + +PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) + : MouseEvent(key), + _engine(engine) { + // Loop until we find the closing brace + Common::String line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + + while (!stream.eos() && !line.contains('}')) { + if (line.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); + } + + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + + if (_hotspot.isEmpty() || _hoverCursor.empty()) { + warning("Push_toggle cursor %u was parsed incorrectly", key); + } +} + + bool PushToggleControl::enable() { + if (!_enabled) { + _engine->registerMouseEvent(this); + _enabled = true; + return true; + } + + debug("Control %u is already enabled", _key); + return false; + } + + bool PushToggleControl::disable() { + if (_enabled) { + _engine->removeMouseEvent(_key); + _enabled = false; + return true; + } + + debug("Control %u is already disabled", _key); + return false; + } + + void PushToggleControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { + _engine->getScriptManager()->setStateValue(_key, 1); + } + + bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos) { + if (_hotspot.contains(backgroundImageSpacePos)) { + _engine->getCursorManager()->changeCursor(_hoverCursor); + return true; + } + + return false; + } + +} // End of namespace ZVision diff --git a/engines/zvision/push_toggle_control.h b/engines/zvision/push_toggle_control.h new file mode 100644 index 0000000000..9384a43d93 --- /dev/null +++ b/engines/zvision/push_toggle_control.h @@ -0,0 +1,77 @@ +/* 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_PUSH_TOGGLE_CONTROL_H +#define ZVISION_PUSH_TOGGLE_CONTROL_H + +#include "common/types.h" + +#include "zvision/control.h" +#include "zvision/mouse_event.h" + + +namespace ZVision { + +class PushToggleControl : public Control, public MouseEvent { +public: + PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + bool enable(); + bool disable(); + + /** + * Called when LeftMouse is pushed. 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 onMouseDown(const Common::Point &screenSpacePos, const Common::Point backgroundImageSpacePos); + /** + * Called when LeftMouse is lifted. Does nothing + * + * @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) {} + /** + * 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); + +private: + ZVision * _engine; + /** + * The area that will trigger the event + * This is in image space coordinates, NOT screen space + */ + Common::Rect _hotspot; + /** The cursor to use when hovering over _hotspot */ + Common::String _hoverCursor; +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/scr_file_handling.cpp b/engines/zvision/scr_file_handling.cpp index 921c1d5b44..c4aed70ac5 100644 --- a/engines/zvision/scr_file_handling.cpp +++ b/engines/zvision/scr_file_handling.cpp @@ -26,6 +26,7 @@ #include "zvision/utility.h" #include "zvision/puzzle.h" #include "zvision/actions.h" +#include "zvision/push_toggle_control.h" #include "common/textconsole.h" #include "common/file.h" |