aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/zvision/control.cpp401
-rw-r--r--engines/zvision/control.h136
-rw-r--r--engines/zvision/lever_control.cpp349
-rw-r--r--engines/zvision/lever_control.h130
-rw-r--r--engines/zvision/module.mk2
-rw-r--r--engines/zvision/push_toggle_control.cpp104
-rw-r--r--engines/zvision/push_toggle_control.h77
-rw-r--r--engines/zvision/scr_file_handling.cpp1
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"