aboutsummaryrefslogtreecommitdiff
path: root/engines/zvision/scripting/controls
diff options
context:
space:
mode:
Diffstat (limited to 'engines/zvision/scripting/controls')
-rw-r--r--engines/zvision/scripting/controls/animation_control.cpp263
-rw-r--r--engines/zvision/scripting/controls/animation_control.h87
-rw-r--r--engines/zvision/scripting/controls/input_control.cpp142
-rw-r--r--engines/zvision/scripting/controls/input_control.h60
-rw-r--r--engines/zvision/scripting/controls/lever_control.cpp402
-rw-r--r--engines/zvision/scripting/controls/lever_control.h127
-rw-r--r--engines/zvision/scripting/controls/push_toggle_control.cpp98
-rw-r--r--engines/zvision/scripting/controls/push_toggle_control.h67
-rw-r--r--engines/zvision/scripting/controls/timer_node.cpp73
-rw-r--r--engines/zvision/scripting/controls/timer_node.h55
10 files changed, 1374 insertions, 0 deletions
diff --git a/engines/zvision/scripting/controls/animation_control.cpp b/engines/zvision/scripting/controls/animation_control.cpp
new file mode 100644
index 0000000000..ec8f7a9647
--- /dev/null
+++ b/engines/zvision/scripting/controls/animation_control.cpp
@@ -0,0 +1,263 @@
+/* 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/animation_control.h b/engines/zvision/scripting/controls/animation_control.h
new file mode 100644
index 0000000000..77663aaf1e
--- /dev/null
+++ b/engines/zvision/scripting/controls/animation_control.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ZVISION_ANIMATION_CONTROL_H
+#define ZVISION_ANIMATION_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+
+namespace Common {
+class String;
+}
+
+namespace Video {
+class VideoDecoder;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace ZVision {
+
+class ZVision;
+class RlfAnimation;
+
+class AnimationControl : public Control {
+public:
+ AnimationControl(ZVision *engine, uint32 controlKey, const Common::String &fileName);
+ ~AnimationControl();
+
+private:
+ enum FileType {
+ RLF = 1,
+ AVI = 2
+ };
+
+private:
+ uint32 _animationKey;
+
+ union {
+ RlfAnimation *rlf;
+ Video::VideoDecoder *avi;
+ } _animation;
+
+ FileType _fileType;
+ uint _loopCount;
+ int32 _x;
+ int32 _y;
+
+ uint _accumulatedTime;
+ uint _currentLoop;
+
+ Graphics::Surface *_cachedFrame;
+ bool _cachedFrameNeedsDeletion;
+
+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; }
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp
new file mode 100644
index 0000000000..2685b01312
--- /dev/null
+++ b/engines/zvision/scripting/controls/input_control.cpp
@@ -0,0 +1,142 @@
+/* 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/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/strings/string_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/utility/utility.h"
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/rect.h"
+
+
+namespace ZVision {
+
+InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key),
+ _nextTabstop(0),
+ _focused(false),
+ _textChanged(false),
+ _cursorOffset(0) {
+ // Loop until we find the closing brace
+ Common::String line = stream.readLine();
+ trimCommentsAndWhiteSpace(&line);
+
+ while (!stream.eos() && !line.contains('}')) {
+ if (line.matchString("*rectangle*", true)) {
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+
+ sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2);
+
+ _textRectangle = Common::Rect(x1, y1, x2, y2);
+ } else if (line.matchString("*aux_hotspot*", true)) {
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+
+ sscanf(line.c_str(), "%*[^(](%d %d %d %d)", &x1, &y1, &x2, &y2);
+
+ _headerRectangle = Common::Rect(x1, y1, x2, y2);
+ } else if (line.matchString("*string_init*", true)) {
+ uint fontFormatNumber;
+
+ sscanf(line.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[25];
+
+ sscanf(line.c_str(), "%*[^(](%25s %*u)", fileName);
+
+ _cursorAnimationFileName = Common::String(fileName);
+ } else if (line.matchString("*cursor_dimensions*", true)) {
+ // Ignore, use the dimensions in the animation file
+ } else if (line.matchString("*cursor_animation_frames*", true)) {
+ // Ignore, use the frame count in the animation file
+ } else if (line.matchString("*focus*", true)) {
+ _focused = true;
+ }
+
+ line = stream.readLine();
+ trimCommentsAndWhiteSpace(&line);
+ }
+}
+
+void InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ _engine->getScriptManager()->focusControl(_key);
+}
+
+void InputControl::onKeyDown(Common::KeyState keyState) {
+ if (!_focused) {
+ return;
+ }
+
+ if (keyState.keycode == Common::KEYCODE_BACKSPACE) {
+ _currentInputText.deleteLastChar();
+ } else if (keyState.keycode == Common::KEYCODE_TAB) {
+ _focused = false;
+ // Focus the next input control
+ _engine->getScriptManager()->focusControl(_nextTabstop);
+ } 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;
+ }
+ }
+}
+
+bool InputControl::process(uint32 deltaTimeInMillis) {
+ if (!_focused) {
+ return false;
+ }
+
+ // First see if we need to render the text
+ if (_textChanged) {
+ // Blit the text using the RenderManager
+ Common::Rect destRect = _engine->getRenderManager()->renderTextToWorkingWindow(_key, _currentInputText, _textStyle.font, _textRectangle.left, _textRectangle.top, _textStyle.color, _textRectangle.width());
+
+ _cursorOffset = destRect.left - _textRectangle.left;
+ }
+
+ // Render the next frame of the animation
+ // TODO: Implement animation handling
+
+ return false;
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h
new file mode 100644
index 0000000000..f0fd8b502d
--- /dev/null
+++ b/engines/zvision/scripting/controls/input_control.h
@@ -0,0 +1,60 @@
+/* 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_INPUT_CONTROL_H
+#define ZVISION_INPUT_CONTROL_H
+
+#include "zvision/scripting/control.h"
+#include "zvision/strings/string_manager.h"
+
+#include "common/rect.h"
+
+
+namespace ZVision {
+
+class InputControl : public Control {
+public:
+ InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+
+private:
+ Common::Rect _textRectangle;
+ Common::Rect _headerRectangle;
+ StringManager::TextStyle _textStyle;
+ uint32 _nextTabstop;
+ Common::String _cursorAnimationFileName;
+ bool _focused;
+
+ Common::String _currentInputText;
+ bool _textChanged;
+ uint _cursorOffset;
+
+public:
+ void focus() { _focused = true; }
+ void unfocus() { _focused = false; }
+ void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
+ void onKeyDown(Common::KeyState keyState);
+ bool process(uint32 deltaTimeInMillis);
+};
+
+} // End of namespace ZVision
+
+#endif
diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp
new file mode 100644
index 0000000000..e08fdd10f3
--- /dev/null
+++ b/engines/zvision/scripting/controls/lever_control.cpp
@@ -0,0 +1,402 @@
+/* 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/lever_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/graphics/render_manager.h"
+#include "zvision/cursors/cursor_manager.h"
+#include "zvision/animation/rlf_animation.h"
+#include "zvision/video/zork_avi_decoder.h"
+#include "zvision/utility/utility.h"
+
+#include "common/stream.h"
+#include "common/file.h"
+#include "common/tokenizer.h"
+#include "common/system.h"
+
+#include "graphics/surface.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) {
+
+ // 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);
+}
+
+LeverControl::~LeverControl() {
+ if (_fileType == AVI) {
+ delete _animation.avi;
+ } else if (_fileType == RLF) {
+ delete _animation.rlf;
+ }
+
+ 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 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)) {
+ // Not used
+ } else if (line.matchString("*anim_coords*", true)) {
+ int left, top, right, bottom;
+ sscanf(line.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)) {
+ 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")) {
+ // Format: P(<from> to <to>)
+ tokenizer.nextToken();
+ tokenizer.nextToken();
+ token = tokenizer.nextToken();
+ uint to = atoi(token.c_str());
+
+ _frameInfo[frameNumber].returnRoute.push_back(to);
+ }
+
+ token = tokenizer.nextToken();
+ }
+ }
+
+ line = file.readLine();
+ }
+}
+
+void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (!_enabled) {
+ return;
+ }
+
+ if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
+ _mouseIsCaptured = true;
+ _lastMousePos = backgroundImageSpacePos;
+ }
+}
+
+void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (!_enabled) {
+ return;
+ }
+
+ if (_mouseIsCaptured) {
+ _mouseIsCaptured = false;
+ _engine->getScriptManager()->setStateValue(_key, _currentFrame);
+
+ _isReturning = true;
+ _returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin();
+ _returnRoutesCurrentFrame = _currentFrame;
+ }
+}
+
+bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (!_enabled) {
+ return false;
+ }
+
+ 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 + ANGLE_DELTA) {
+ _currentFrame = iter->toFrame;
+ renderFrame(_currentFrame);
+ break;
+ }
+ }
+ }
+ } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
+ _engine->getCursorManager()->changeCursor(_cursorName);
+ cursorWasChanged = true;
+ }
+
+ return cursorWasChanged;
+}
+
+bool LeverControl::process(uint32 deltaTimeInMillis) {
+ if (!_enabled) {
+ return false;
+ }
+
+ if (_isReturning) {
+ _accumulatedTime += deltaTimeInMillis;
+ while (_accumulatedTime >= ANIMATION_FRAME_TIME) {
+ _accumulatedTime -= ANIMATION_FRAME_TIME;
+ if (_returnRoutesCurrentFrame == *_returnRoutesCurrentProgress) {
+ _returnRoutesCurrentProgress++;
+ }
+ if (_returnRoutesCurrentProgress == _frameInfo[_currentFrame].returnRoute.end()) {
+ _isReturning = false;
+ _currentFrame = _returnRoutesCurrentFrame;
+ return false;
+ }
+
+ uint toFrame = *_returnRoutesCurrentProgress;
+ if (_returnRoutesCurrentFrame < toFrame) {
+ _returnRoutesCurrentFrame++;
+ } else if (_returnRoutesCurrentFrame > toFrame) {
+ _returnRoutesCurrentFrame--;
+ }
+
+ _engine->getScriptManager()->setStateValue(_key, _returnRoutesCurrentFrame);
+ renderFrame(_returnRoutesCurrentFrame);
+ }
+ }
+
+ 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;
+
+ // Calculate the angle using arctan
+ // Then convert to degrees. (180 / 3.14159 = 57.2958)
+ int angle = int(atan((float)yDist / (float)xDist) * 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 1 \ | / Quadrant 0 |
+ // \ | / |
+ // \ | / |
+ // angle ( \|/ ) -angle |
+ // 180 <----------------------------------------> 0 |
+ // -angle ( /|\ ) angle |
+ // / | \ |
+ // / | \ |
+ // Quadrant 3 / | \ Quadrant 2 |
+ // / | \ |
+ // / | \ |
+ // / | \ |
+ // * | * |
+ // ^ |
+ // 270 |
+
+ // Convert the local angles to unit circle angles
+ switch (quadrant) {
+ case 0:
+ angle = 180 + angle;
+ break;
+ case 1:
+ // Do nothing
+ break;
+ case 2:
+ angle = 180 + angle;
+ break;
+ case 3:
+ angle = 360 + angle;
+ break;
+ }
+
+ return angle;
+ }
+}
+
+void LeverControl::renderFrame(uint frameNumber) {
+ if (frameNumber == 0) {
+ _lastRenderedFrame = frameNumber;
+ } else if (frameNumber < _lastRenderedFrame && _mirrored) {
+ _lastRenderedFrame = frameNumber;
+ frameNumber = (_frameCount * 2) - frameNumber - 1;
+ } else {
+ _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;
+ }
+
+ _engine->getRenderManager()->copyRectToWorkingWindow(frameData, x, y, width, width, height);
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h
new file mode 100644
index 0000000000..69473cf119
--- /dev/null
+++ b/engines/zvision/scripting/controls/lever_control.h
@@ -0,0 +1,127 @@
+/* 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_LEVER_CONTROL_H
+#define ZVISION_LEVER_CONTROL_H
+
+#include "zvision/scripting/control.h"
+
+#include "common/list.h"
+#include "common/rect.h"
+
+
+namespace ZVision {
+
+class ZorkAVIDecoder;
+class RlfAnimation;
+
+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) {}
+
+ 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)
+ ANIMATION_FRAME_TIME = 30 // In millis
+ };
+
+private:
+ 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;
+ uint _lastRenderedFrame;
+ bool _mouseIsCaptured;
+ bool _isReturning;
+ Common::Point _lastMousePos;
+ Common::List<uint>::iterator _returnRoutesCurrentProgress;
+ uint _returnRoutesCurrentFrame;
+ uint32 _accumulatedTime;
+
+public:
+ 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/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp
new file mode 100644
index 0000000000..11ec4bb73f
--- /dev/null
+++ b/engines/zvision/scripting/controls/push_toggle_control.cpp
@@ -0,0 +1,98 @@
+/* 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/push_toggle_control.h"
+
+#include "zvision/zvision.h"
+#include "zvision/scripting/script_manager.h"
+#include "zvision/cursors/cursor_manager.h"
+#include "zvision/utility/utility.h"
+
+#include "common/stream.h"
+
+
+namespace ZVision {
+
+PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
+ : Control(engine, key) {
+ // 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);
+ }
+}
+
+PushToggleControl::~PushToggleControl() {
+ // Clear the state value back to 0
+ _engine->getScriptManager()->setStateValue(_key, 0);
+}
+
+void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (!_enabled) {
+ return;
+ }
+
+ if (_hotspot.contains(backgroundImageSpacePos)) {
+ _engine->getScriptManager()->setStateValue(_key, 1);
+ }
+}
+
+bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
+ if (!_enabled) {
+ return false;
+ }
+
+ if (_hotspot.contains(backgroundImageSpacePos)) {
+ _engine->getCursorManager()->changeCursor(_hoverCursor);
+ 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
new file mode 100644
index 0000000000..13dc54a65f
--- /dev/null
+++ b/engines/zvision/scripting/controls/push_toggle_control.h
@@ -0,0 +1,67 @@
+/* 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 "zvision/scripting/control.h"
+
+#include "common/rect.h"
+
+
+namespace ZVision {
+
+class PushToggleControl : public Control {
+public:
+ PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
+ ~PushToggleControl();
+
+ /**
+ * 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);
+ /**
+ * 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:
+ /**
+ * 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/scripting/controls/timer_node.cpp b/engines/zvision/scripting/controls/timer_node.cpp
new file mode 100644
index 0000000000..3b691be275
--- /dev/null
+++ b/engines/zvision/scripting/controls/timer_node.cpp
@@ -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.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "zvision/scripting/controls/timer_node.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) {
+ _timeLeft = timeInSeconds * 1000;
+ } else if (_engine->getGameId() == GID_GRANDINQUISITOR) {
+ _timeLeft = timeInSeconds * 100;
+ }
+
+ _engine->getScriptManager()->setStateValue(_key, 1);
+}
+
+TimerNode::~TimerNode() {
+ if (_timeLeft <= 0)
+ _engine->getScriptManager()->setStateValue(_key, 2);
+ else
+ _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;
+ }
+
+ return false;
+}
+
+void TimerNode::serialize(Common::WriteStream *stream) {
+ stream->writeUint32LE(_key);
+ stream->writeUint32LE(_timeLeft);
+}
+
+void TimerNode::deserialize(Common::SeekableReadStream *stream) {
+ _timeLeft = stream->readUint32LE();
+}
+
+} // End of namespace ZVision
diff --git a/engines/zvision/scripting/controls/timer_node.h b/engines/zvision/scripting/controls/timer_node.h
new file mode 100644
index 0000000000..a8e579cbe4
--- /dev/null
+++ b/engines/zvision/scripting/controls/timer_node.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_TIMER_NODE_H
+#define ZVISION_TIMER_NODE_H
+
+#include "zvision/scripting/control.h"
+
+namespace ZVision {
+
+class ZVision;
+
+class TimerNode : public Control {
+public:
+ TimerNode(ZVision *engine, uint32 key, uint timeInSeconds);
+ ~TimerNode();
+
+ /**
+ * 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 serialize(Common::WriteStream *stream);
+ void deserialize(Common::SeekableReadStream *stream);
+ inline bool needsSerialization() { return true; }
+
+private:
+ int32 _timeLeft;
+};
+
+} // End of namespace ZVision
+
+#endif