aboutsummaryrefslogtreecommitdiff
path: root/engines/bladerunner/ui/esper.cpp
diff options
context:
space:
mode:
authorPeter Kohaut2018-01-31 00:37:19 +0100
committerPeter Kohaut2018-02-04 17:34:43 +0100
commit6e9a340640686fe7dd95efbea34cbf3b7f4209af (patch)
treefe0adec0b85f3e2b387097c4378ce8337b731460 /engines/bladerunner/ui/esper.cpp
parent0300979bdd8d2ca451110d77cb8e05c84365bbec (diff)
downloadscummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.tar.gz
scummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.tar.bz2
scummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.zip
BLADERUNNER: ESPER interface
Diffstat (limited to 'engines/bladerunner/ui/esper.cpp')
-rw-r--r--engines/bladerunner/ui/esper.cpp1791
1 files changed, 1791 insertions, 0 deletions
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
new file mode 100644
index 0000000000..ea1df6bd38
--- /dev/null
+++ b/engines/bladerunner/ui/esper.cpp
@@ -0,0 +1,1791 @@
+/* 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 "bladerunner/ui/esper.h"
+
+#include "bladerunner/actor.h"
+#include "bladerunner/ambient_sounds.h"
+#include "bladerunner/audio_player.h"
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/decompress_lcw.h"
+#include "bladerunner/font.h"
+#include "bladerunner/game_info.h"
+#include "bladerunner/game_constants.h"
+#include "bladerunner/mouse.h"
+#include "bladerunner/shape.h"
+#include "bladerunner/script/esper.h"
+#include "bladerunner/text_resource.h"
+#include "bladerunner/ui/ui_image_picker.h"
+#include "bladerunner/vqa_player.h"
+
+#include "common/rect.h"
+#include "common/str.h"
+
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+const Common::Rect ESPER::kScreen = Common::Rect(135, 123, 435, 387);
+
+ESPER::ESPER(BladeRunnerEngine *vm) {
+ _vm = vm;
+
+ _isWaiting = false;
+ _shapeButton = nullptr;
+ _shapeThumbnail = nullptr;
+ _regionSelectedAck = false;
+ _isDrawingSelection = false;
+
+ _isOpen = false;
+ _photoData = nullptr;
+ _viewportData = nullptr;
+ _shapeButton = nullptr;
+ _shapeThumbnail = nullptr;
+ _vqaMainPlayer = nullptr;
+ _vqaPhotoPlayer = nullptr;
+ _script = nullptr;
+
+ reset();
+ _buttons = new UIImagePicker(vm, 16);
+}
+
+ESPER::~ESPER() {
+ delete _buttons;
+ reset();
+}
+
+void ESPER::open(Graphics::Surface *surface) {
+ // CD-changing logic has been removed
+
+ while (!_vm->playerHasControl()) {
+ _vm->playerGainsControl();
+ }
+
+ while (_vm->_mouse->isDisabled()) {
+ _vm->_mouse->enable();
+ }
+
+ //TODO: time->lock()
+ _ambientVolume = _vm->_ambientSounds->getVolume();
+ _vm->_ambientSounds->setVolume(_ambientVolume / 2);
+
+ reset();
+
+ if (!_vm->openArchive("MODE.MIX")) {
+ return;
+ }
+
+ _photoData = new Graphics::Surface();
+ _photoData->create(kPhotoWidth, kPhotoHeight, createRGB555());
+
+ _viewportData = new Graphics::Surface();
+ _viewportData->create(kScreen.width(), kScreen.height(), createRGB555());
+
+ _viewportNext = _viewport;
+
+ _vm->_mainFont->setColor(0x001F);
+
+ _shapeButton = new Shape(_vm);
+ if (!_shapeButton->open("ESPBUTTN.SHP", 0)) {
+ return;
+ }
+
+ _shapesPhotos.resize(10);
+
+ _vqaMainPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
+ if (!_vqaMainPlayer->open("ESPER.VQA")) {
+ return;
+ }
+ _vqaMainPlayer->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr);
+
+ _isOpen = true;
+ _flash = false;
+
+ _script = new ESPERScript(_vm);
+ activate(true);
+}
+
+void ESPER::close() {
+ // CD-changing logic has been removed
+ delete _script;
+ _script = nullptr;
+
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(425), 25, 0, 0, 50, 0);
+
+ unloadPhotos();
+ _shapesPhotos.clear();
+
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+
+ _buttons->deactivate();
+ _buttons->resetImages();
+
+ delete _shapeButton;
+ _shapeButton = nullptr;
+
+ delete _photoData;
+ _photoData = nullptr;
+
+ delete _viewportData;
+ _viewportData = nullptr;
+
+ if (_vqaMainPlayer) {
+ _vqaMainPlayer->close();
+ delete _vqaMainPlayer;
+ _vqaMainPlayer= nullptr;
+ }
+
+ _vm->closeArchive("MODE.MIX");
+
+ //TODO: time->unlock()
+ _vm->_ambientSounds->setVolume(_ambientVolume);
+ //TODO: _vm->_scene->resume(false);
+ reset();
+}
+
+bool ESPER::isOpen() {
+ return _isOpen;
+}
+
+void ESPER::handleMouseUp(int x, int y, bool mainButton) {
+ bool actionHandled = _buttons->handleMouseAction(x, y, false, true, false);
+ if (mainButton) {
+ _isMouseDown = false;
+ if (!actionHandled) {
+ if (_isScrolling) {
+ scrollingStop();
+ } else if (_isDrawingSelection && _mouseOverScroll == 4) {
+ _isDrawingSelection = false;
+ resetSelectionRect();
+ }
+ }
+ } else if (_statePhoto == kEsperPhotoStatePhotoZoomOut) {
+ zoomOutStop();
+ }
+}
+
+void ESPER::handleMouseDown(int x, int y, bool mainButton) {
+ bool actionHandled = _buttons->handleMouseAction(x, y, true, false, false);
+
+ if (actionHandled || _vm->_mouse->isDisabled()) {
+ return;
+ }
+
+ if (mainButton) {
+ if (_statePhoto != kEsperPhotoStateVideoZoomOut) {
+ if (kScreen.contains(x, y)) {
+ _isMouseDown = true;
+ playSound(460, 100);
+ }
+ if ( _mouseOverScroll >= 0 && _mouseOverScroll <= 3 && !_isScrolling) {
+ scrollingStart(_mouseOverScroll);
+ }
+ tick();
+ }
+ } else {
+ if (_statePhoto == kEsperPhotoStateShow || _statePhoto == kEsperPhotoStateVideoShow) {
+ zoomOutStart();
+ }
+ }
+}
+
+void ESPER::tick() {
+ if (!_vm->_windowIsActive) {
+ return;
+ }
+
+ tickSound();
+
+ blit(_vm->_surfaceBack, _vm->_surfaceFront);
+
+ int mouseX, mouseY;
+ _vm->_mouse->getXY(&mouseX, &mouseY);
+ if (!_vm->_mouse->isDisabled()) {
+ _buttons->handleMouseAction(mouseX, mouseY, false, false, false);
+ }
+
+ if (!_isOpen) {
+ return;
+ }
+
+ draw(_vm->_surfaceFront);
+ _buttons->draw(_vm->_surfaceFront);
+ tickMouse(_vm->_surfaceFront);
+ tickSound();
+
+ _vm->blitToScreen(_vm->_surfaceFront);
+
+ // TODO: implement 60hz lock for smoother experience
+ _vm->_system->delayMillis(10);
+
+ if (_statePhoto == kEsperPhotoStateVideoShow) {
+ if (_regionSelectedAck) {
+ _regionSelectedAck = false;
+ _script->specialRegionSelected(_photoIdSelected, _regions[_regionSelected].regionId);
+ }
+ }
+}
+
+void ESPER::resume() {
+ //TODO
+}
+
+void ESPER::addPhoto(const char *name, int photoId, int shapeId) {
+ int i = findEmptyPhoto();
+ if (i >= 0) {
+ _photos[i].shapeId = shapeId;
+ _photos[i].isPresent = true;
+ _photos[i].photoId = photoId;
+ strcpy(_photos[i].name, name);
+
+ assert((uint)shapeId < _shapesPhotos.size());
+ _shapesPhotos[shapeId] = new Shape(_vm);
+ _shapesPhotos[shapeId]->open("ESPTHUMB.SHP", shapeId);
+
+ _buttons->defineImage(i,
+ Common::Rect(
+ 100 * (i % 3) + kScreen.left + 3,
+ 66 * (i / 3) + kScreen.top + 3,
+ 100 * (i % 3) + kScreen.left + 100 - 3,
+ 66 * (i / 3) + kScreen.top + 66 - 3
+ ),
+ _shapesPhotos[shapeId],
+ _shapesPhotos[shapeId],
+ _shapesPhotos[shapeId],
+ nullptr);
+ }
+ playSound(420, 25);
+ wait(300);
+ tick();
+}
+
+void ESPER::defineRegion(int regionId, Common::Rect inner, Common::Rect outer, Common::Rect selection, const char *name) {
+ int i = findEmptyRegion();
+ if (i >= 0) {
+ _regions[i].isPresent = true;
+ _regions[i].regionId = regionId;
+ _regions[i].rectInner = inner;
+ _regions[i].rectOuter = outer;
+ _regions[i].rectSelection = selection;
+ strncpy(_regions[i].name, name, 13);
+ _regions[i].name[13] = 0;
+ }
+}
+
+void ESPER::mouseDownCallback(int buttonId, void *callbackData) {
+ ESPER *self = ((ESPER *)callbackData);
+ if (self->_statePhoto != kEsperPhotoStateVideoZoomOut && buttonId == kPhotoCount + 2) {
+ self->zoomOutStart();
+ }
+}
+
+void ESPER::mouseUpCallback(int buttonId, void *callbackData) {
+ ESPER *self = (ESPER *)callbackData;
+ if (buttonId < kPhotoCount) {
+ self->selectPhoto(buttonId);
+ } else if (self->_statePhoto != kEsperPhotoStateVideoZoomOut) {
+ if (buttonId == kPhotoCount + 1) {
+ // TODO: is it even used?
+ } else if (buttonId == kPhotoCount + 2) {
+ self->zoomOutStop();
+ } else if (buttonId == kPhotoCount + 3) {
+ self->goBack();
+ }
+ }
+}
+
+void ESPER::reset() {
+ delete _photoData;
+ _photoData = nullptr;
+
+ delete _viewportData;
+ _viewportData = nullptr;
+
+ delete _shapeButton;
+ _shapeButton = nullptr;
+
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+
+ delete _vqaMainPlayer;
+ _vqaMainPlayer = nullptr;
+
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ delete _script;
+ _script = nullptr;
+
+ _isOpen = false;
+
+ _shapesPhotos.clear();
+ resetData();
+}
+
+void ESPER::resetData() {
+ if (_vqaPhotoPlayer) {
+ _vqaPhotoPlayer->close();
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+ }
+ if (_shapeThumbnail) {
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+ }
+
+ _viewport.left = 0;
+ _viewport.top = 0;
+
+ _regionSelectedAck = false;
+ _mouseOverScroll = -1;
+ _isMouseDown = 0;
+ _viewportNext.left = 0;
+ _viewportNext.top = 0;
+
+ _selectionRect.left = -1;
+ _selectionRect.top = -1;
+ _selectionRect.right = -1;
+ _selectionRect.bottom = -1;
+
+ _selectionCrosshairX = -1;
+ _selectionCrosshairY = -1;
+
+ _stateMain = kEsperMainStatePhoto;
+ _statePhoto = kEsperPhotoStateShow;
+
+ _isDrawingSelection = false;
+ _flash = false;
+ _isScrolling = false;
+
+ _scrollingDirection = -1;
+ _timeScrollNext = 0;
+
+ resetPhotos();
+ resetRegions();
+ resetViewport();
+ resetSelectionBlinking();
+ prepareZoom();
+ resetPhotoZooming();
+ resetPhotoOpening();
+
+ _soundId1 = -1;
+ _soundId2 = -1;
+ _soundId3 = -1;
+}
+
+void ESPER::resetPhotos() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ _photos[i].isPresent = false;
+ _photos[i].photoId = -1;
+ }
+}
+
+void ESPER::resetRegions() {
+ for (int i = 0; i < kRegionCount; ++i) {
+ _regions[i].isPresent = false;
+ _regions[i].regionId = -1;
+ }
+}
+
+void ESPER::resetViewport() {
+ _zoomHorizontal = (float)(kScreen.width()) / (float)kPhotoWidth;
+ _zoomVertical = (float)(kScreen.height()) / (float)kPhotoHeight;
+ _zoom = _zoomVertical;
+ _zoomMin = _zoom;
+
+ _timeZoomOutNext = 0;
+ _viewportPositionX = kPhotoWidth / 2;
+ _viewportPositionY = kPhotoHeight / 2;
+
+ updateViewport();
+
+ _screenHalfWidth = kScreen.width() / 2;
+ _screenHalfHeight = kScreen.height() / 2;
+}
+
+void ESPER::resetSelectionRect() {
+ _selectionRect = kScreen;
+ _selectionCrosshairX = -1;
+ _selectionCrosshairY = -1;
+}
+
+void ESPER::resetSelectionBlinking() {
+ _selectionBlinkingCounter = 0;
+ _selectionBlinkingStyle = 0;
+ _timeSelectionBlinkingNext = 0;
+}
+
+void ESPER::resetPhotoZooming() {
+ _zoomStep = 0;
+ _timeZoomNext = 0;
+}
+
+void ESPER::resetPhotoOpening() {
+ _photoOpeningWidth = kScreen.left + 1;
+ _photoOpeningHeight = kScreen.top + 1;
+ _timePhotoOpeningNext = 0;
+}
+
+void ESPER::updateViewport() {
+ float halfWidth = (1.0f / 2.0f) * ((float)kPhotoWidth * (_zoomHorizontal / _zoom));
+ _viewport.left = _viewportPositionX - halfWidth;
+ _viewport.right = _viewportPositionX + halfWidth;
+ if (_viewport.left < 0) {
+ _viewport.right -= _viewport.left;
+ _viewport.left = 0;
+ }
+ if (_viewport.right >= kPhotoWidth) {
+ _viewport.left -= _viewport.right - (kPhotoWidth - 1);
+ if (_viewport.left < 0) {
+ _viewport.left = 0;
+ }
+ _viewport.right = kPhotoWidth - 1;
+ }
+
+ float halfHeight = 1.0f / 2.0f * ((float)kPhotoHeight * (_zoomVertical / _zoom));
+ _viewport.top = _viewportPositionY - halfHeight;
+ _viewport.bottom = _viewportPositionY + halfHeight;
+ if (_viewport.top < 0) {
+ _viewport.bottom -= _viewport.top;
+ _viewport.top = 0;
+ }
+ if (_viewport.bottom >= kPhotoHeight) {
+ _viewport.top -= _viewport.bottom - (kPhotoHeight - 1);
+ if (_viewport.top < 0) {
+ _viewport.top = 0;
+ }
+ _viewport.bottom = kPhotoHeight - 1;
+ }
+
+ _viewportWidth = _viewport.right + 1 - _viewport.left;
+ _viewportHeight = _viewport.bottom + 1 - _viewport.top;
+
+ int centerX = (_viewport.left + _viewport.right) / 2;
+ int centerY = (_viewport.top + _viewport.bottom) / 2;
+
+ float v50 = _zoom / _zoomHorizontal * 1.0f;
+ if ((_viewportPositionX > centerX + v50) || (_viewportPositionX < centerX - v50)) {
+ _viewportPositionX = centerX;
+ }
+
+ float v51 = _zoom / _zoomVertical * 1.0f;
+ if ((_viewportPositionY > centerY + v51) || (_viewportPositionY < centerY - v51)) {
+ _viewportPositionY = centerY;
+ }
+}
+
+void ESPER::activate(bool withOpening) {
+ _vm->_mouse->disable();
+
+ _buttons->resetImages();
+
+ if (withOpening) {
+ setStateMain(kEsperMainStateOpening);
+ playSound(413, 25);
+ wait(1000);
+ playSound(414, 25);
+ wait(2000);
+ } else {
+ _buttons->deactivate();
+ setStateMain(kEsperMainStateClear);
+ }
+
+ _buttons->activate(nullptr, nullptr, mouseDownCallback, mouseUpCallback, this);
+ _buttons->defineImage(kPhotoCount + 3, Common::Rect(42, 403, 76, 437), nullptr, nullptr, _shapeButton, nullptr);
+
+ playSound(415, 25);
+ wait(1000);
+
+ setStateMain(kEsperMainStateList);
+ resetPhotos();
+ _script->initialize();
+
+ _vm->_mouse->enable();
+}
+
+void ESPER::setStateMain(EsperMainStates state) {
+ if (_isOpen) {
+ _stateMain = state;
+ debug("ESPER main state: %d", _stateMain);
+
+ }
+}
+
+void ESPER::setStatePhoto(EsperPhotoStates state) {
+ _statePhoto = state;
+ debug("ESPER photo state: %d", _statePhoto);
+}
+
+void ESPER::wait(int timeout) {
+ if (!_isWaiting) {
+ _isWaiting = true;
+ uint timeEnd = timeout + _vm->getTotalPlayTime();
+ while (_vm->getTotalPlayTime() < timeEnd) {
+ _vm->gameTick();
+ }
+ _isWaiting = false;
+ }
+}
+
+void ESPER::playSound(int soundId, int volume) {
+ if (_soundId1 == -1) {
+ _soundId1 = soundId;
+ _volume1 = volume;
+ } else if (_soundId2 == -1) {
+ _soundId2 = soundId;
+ _volume2 = volume;
+ } else if (_soundId3 == -1) {
+ _soundId3 = soundId;
+ _volume3 = volume;
+ }
+}
+
+void ESPER::draw(Graphics::Surface &surface) {
+ if (!_isOpen) {
+ return;
+ }
+ _vqaMainPlayer->update(false);
+ switch (_stateMain) {
+ case kEsperMainStateOpening:
+ case kEsperMainStateList:
+ return;
+ case kEsperMainStatePhotoOpening:
+ drawPhotoOpening(surface);
+ break;
+ case kEsperMainStateClear:
+ surface.fillRect(kScreen, 0x0000);
+ break;
+ case kEsperMainStatePhoto:
+ if (_isScrolling) {
+ tickScroll();
+ }
+ switch (_statePhoto) {
+ case kEsperPhotoStateShow:
+ drawPhotoWithGrid(surface);
+ if (_isDrawingSelection) {
+ drawSelection(surface, true, 1);
+ }
+#if BLADERUNNER_DEBUG_RENDERING
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (_regions[i].isPresent) {
+ surface.frameRect(
+ Common::Rect(
+ viewportXToScreenX(_regions[i].rectInner.left),
+ viewportYToScreenY(_regions[i].rectInner.top),
+ viewportXToScreenX(_regions[i].rectInner.right),
+ viewportYToScreenY(_regions[i].rectInner.bottom)
+ ),
+ 0x7FE0
+ );
+ surface.frameRect(
+ Common::Rect(
+ viewportXToScreenX(_regions[i].rectOuter.left),
+ viewportYToScreenY(_regions[i].rectOuter.top),
+ viewportXToScreenX(_regions[i].rectOuter.right),
+ viewportYToScreenY(_regions[i].rectOuter.bottom)
+ ),
+ 0x7FE0
+ );
+ }
+ }
+#endif
+ break;
+ case kEsperPhotoStateScrolling:
+ scrollUpdate();
+ drawPhotoWithGrid(surface);
+ break;
+ case kEsperPhotoStateSelectionZooming:
+ drawPhotoWithGrid(surface);
+ if (!drawSelectionZooming(surface)) {
+ setStatePhoto(kEsperPhotoStateSelectionBlinking);
+ playSound(418, 25);
+ }
+ break;
+ case kEsperPhotoStateSelectionBlinking:
+ drawPhotoWithGrid(surface);
+ if (!drawSelectionBlinking(surface)) {
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+ break;
+ case kEsperPhotoStatePhotoZooming:
+ drawPhotoZooming(surface);
+ break;
+ case kEsperPhotoStatePhotoSharpening:
+ drawPhotoSharpening(surface);
+ break;
+ case kEsperPhotoStatePhotoZoomOut:
+ drawPhotoZoomOut(surface);
+ break;
+ case kEsperPhotoStateVideoZooming:
+ drawVideoZooming(surface);
+ break;
+ case kEsperPhotoStateVideoShow:
+ drawVideoFrame(surface);
+ drawGrid(surface);
+ break;
+ case kEsperPhotoStateVideoZoomOut:
+ drawVideoZoomOut(surface);
+ break;
+ default:
+ break;
+ }
+ drawTextCoords(surface);
+
+ break;
+ }
+}
+
+void ESPER::drawPhotoOpening(Graphics::Surface &surface) {
+ bool needMoreZooming = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow >= _timePhotoOpeningNext) {
+ _photoOpeningWidth = MIN(_photoOpeningWidth + 8, kScreen.right - 1);
+ _photoOpeningHeight = MIN(_photoOpeningHeight + 7, kScreen.bottom - 1);
+
+ if (_photoOpeningWidth == kScreen.right - 1 && _photoOpeningHeight == kScreen.bottom - 1) {
+ needMoreZooming = false;
+ }
+
+ _timePhotoOpeningNext = timeNow + 20;
+ }
+ copyImageScale(_photoData, _viewport, &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+
+ surface.hLine(kScreen.left, _photoOpeningHeight, kScreen.right - 1, 0x03E0);
+ surface.vLine(_photoOpeningWidth, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, _photoOpeningHeight - 1, kScreen.right - 1, 0x0240);
+ surface.vLine(_photoOpeningWidth - 1, kScreen.top, kScreen.bottom - 1, 0x0240);
+
+ drawGrid(surface);
+
+ if (!needMoreZooming) {
+ setStateMain(kEsperMainStatePhoto);
+ setStatePhoto(kEsperPhotoStateShow);
+ _vm->_mouse->enable();
+ }
+}
+
+bool ESPER::drawSelectionZooming(Graphics::Surface &surface) {
+ bool zooming = false;
+ bool needMoreZooming = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeSelectionZoomNext) {
+ zooming = true;
+ _selectionRect.left += _selectionRectDelta.left;
+ _selectionRect.top += _selectionRectDelta.top;
+ _selectionRect.right += _selectionRectDelta.right;
+ _selectionRect.bottom += _selectionRectDelta.bottom;
+ ++_selectionZoomStep;
+ _timeSelectionZoomNext = timeNow + 150;
+ if (_selectionZoomStep > kSelectionZoomSteps) {
+ needMoreZooming = false;
+ _selectionRect.left = _selectionRectTarget.left;
+ _selectionRect.top = _selectionRectTarget.top;
+ _selectionRect.right = _selectionRectTarget.right;
+ _selectionRect.bottom = _selectionRectTarget.bottom;
+ }
+ }
+ drawSelection(surface, false, 1);
+ if (!needMoreZooming) {
+ _statePhoto = kEsperPhotoStatePhotoZooming;
+ resetPhotoZooming();
+ zooming = false;
+ }
+ if (zooming) {
+ playSound(416, 20);
+ }
+ return needMoreZooming;
+}
+
+bool ESPER::drawSelectionBlinking(Graphics::Surface &surface) {
+ bool needMoreBlinking = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeSelectionBlinkingNext) {
+ _timeSelectionBlinkingNext = timeNow + 100;
+ _selectionBlinkingStyle ^= 1;
+ ++_selectionBlinkingCounter;
+ if (_selectionBlinkingCounter > 10) {
+ needMoreBlinking = false;
+ _selectionBlinkingStyle = 0;
+ }
+ }
+ drawSelection(surface, false, _selectionBlinkingStyle);
+ if (!needMoreBlinking) {
+ resetSelectionBlinking();
+ }
+ return needMoreBlinking;
+}
+
+void ESPER::drawPhotoZooming(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ if ((timeNow > _timeZoomNext) && (_zoomStep < _zoomSteps)) {
+ _flash = true;
+
+ _viewportPositionXCurrent += _viewportPositionXDelta;
+ _viewportPositionYCurrent += _viewportPositionYDelta;
+ _viewportPositionX = _viewportPositionXCurrent;
+ _viewportPositionY = _viewportPositionYCurrent;
+
+ _zoom += _zoomDelta;
+ if (_zoomDelta > 0.0f) {
+ if (_zoom > _zoomTarget) {
+ _zoom = _zoomTarget;
+ _zoomStep = _zoomSteps;
+ } else {
+ _blur += _zoomDelta * 2.0f;
+ }
+ } else if (_zoomDelta < 0.0f) {
+ if (_zoom < _zoomTarget) {
+ _zoom = _zoomTarget;
+ _zoomStep = _zoomSteps;
+ }
+ }
+ ++_zoomStep;
+ if (_zoomStep >= _zoomSteps) {
+ _zoom = _zoomTarget;
+ _viewportPositionX = _viewportPositionXTarget;
+ _viewportPositionY = _viewportPositionYTarget;
+ }
+ updateViewport();
+ _timeZoomNext = timeNow + 300;
+ }
+
+ if (_zoomDelta >= 0.0f) {
+ drawPhoto(surface);
+ } else {
+ drawPhotoWithGrid(surface);
+ }
+ drawGrid(surface);
+
+ if ((timeNow > _timeZoomNext) && (_zoomStep >= _zoomSteps)) {
+ if (_regionSelectedAck) {
+ if (_regions[_regionSelected].name[0]) {
+ if (_zoomDelta < 0.0f) {
+ _blur = 1.0f;
+ _zoomDelta = (_zoom * 1.5f - _zoom) / (float)_zoomSteps; // 0.5f * _zoom ???
+ }
+ setStatePhoto(kEsperPhotoStateVideoZooming);
+ _timeZoomNext += 300;
+ } else {
+ _regionSelectedAck = false;
+ _selectionRect.left = viewportXToScreenX(_regions[_regionSelected].rectInner.left);
+ _selectionRect.right = viewportXToScreenX(_regions[_regionSelected].rectInner.right);
+ _selectionRect.top = viewportYToScreenY(_regions[_regionSelected].rectInner.top);
+ _selectionRect.bottom = viewportYToScreenY(_regions[_regionSelected].rectInner.bottom);
+ prepareZoom();
+ resetPhotoZooming();
+ updateSelection();
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+ } else {
+ setStatePhoto(kEsperPhotoStatePhotoSharpening);
+ }
+ resetPhotoOpening();
+ }
+}
+
+void ESPER::drawPhotoSharpening(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ bool needMoreSharpening = true;
+ if (timeNow >= _timePhotoOpeningNext) {
+ _photoOpeningWidth = MIN(_photoOpeningWidth + 8, kScreen.right - 1);
+ _photoOpeningHeight = MIN(_photoOpeningHeight + 7, kScreen.bottom - 1);
+
+ if (_photoOpeningWidth == kScreen.right - 1 && _photoOpeningHeight == kScreen.bottom - 1) {
+ needMoreSharpening = false;
+ }
+
+ _timePhotoOpeningNext = timeNow + 50;
+ }
+
+ if (_regionSelectedAck && _regions[_regionSelected].name[0]) {
+ _vqaPhotoPlayer->update(true, false);
+ copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, kScreen, _blur);
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+ } else {
+ drawPhoto(surface);
+ copyImageScale(_photoData, _viewport, _viewportData, Common::Rect(0, 0, kScreen.width(), kScreen.height()));
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+
+ }
+ drawGrid(surface);
+ surface.hLine(kScreen.left, _photoOpeningHeight, kScreen.right - 1, 0x03E0);
+ surface.vLine(_photoOpeningWidth, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, _photoOpeningHeight - 1, kScreen.right - 1, 0x0240);
+ surface.vLine(_photoOpeningWidth - 1, kScreen.top, kScreen.bottom - 1, 0x0240);
+ if (!needMoreSharpening) {
+ if (_regionSelectedAck && _regions[_regionSelected].name[0]){
+ setStatePhoto(kEsperPhotoStateVideoShow);
+ } else {
+ setStatePhoto(kEsperPhotoStateShow);
+ }
+ resetPhotoZooming();
+ resetPhotoOpening();
+ _vm->_mouse->enable();
+ }
+}
+
+void ESPER::drawPhotoZoomOut(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow >= _timeZoomOutNext) {
+ _timeZoomOutNext = timeNow + 300;
+
+ if (_zoom > _zoomMin) {
+ _zoom /= 1.3f;
+ _flash = true;
+ if (_zoomHorizontal <= _zoomVertical) {
+ if (_zoom < _zoomVertical) {
+ _zoom = _zoomVertical;
+ }
+ } else {
+ if (_zoom < _zoomHorizontal) {
+ _zoom = _zoomHorizontal;
+ }
+ }
+ updateViewport();
+ } else {
+ _statePhoto = kEsperPhotoStateShow;
+ }
+ }
+ drawPhotoWithGrid(surface);
+}
+
+void ESPER::drawVideoZooming(Graphics::Surface &surface) {
+ if (_vqaPhotoPlayer == nullptr) {
+ _vqaPhotoPlayer = new VQAPlayer(_vm, _viewportData);
+ if (!_vqaPhotoPlayer->open(Common::String(_regions[_regionSelected].name) + ".VQA")) {
+ setStatePhoto(kEsperPhotoStateShow);
+ _vm->_mouse->enable();
+
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ return;
+ }
+
+ _timeZoomNext = 0;
+ }
+
+ bool flash = false;
+ bool advanceFrame = false;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeZoomNext) {
+ _timeZoomNext = timeNow + 300;
+ playSound(419, 25);
+ flash = true;
+ advanceFrame = true;
+ _blur += _zoomDelta * 5.0f;
+ }
+
+ int frame = _vqaPhotoPlayer->update(true, advanceFrame);
+ if (frame == _vqaPhotoPlayer->getFrameCount() - 1) {
+ _vqaLastFrame = frame;
+ setStatePhoto(kEsperPhotoStatePhotoSharpening);
+ }
+
+ if (flash) {
+ flashViewport();
+ }
+ copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, kScreen, _blur);
+ drawGrid(surface);
+}
+
+void ESPER::drawVideoZoomOut(Graphics::Surface &surface) {
+ bool flash = false;
+ bool advanceFrame = false;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeZoomNext && _vqaLastFrame > 0) {
+ _timeZoomNext = timeNow + 300;
+ playSound(419, 25);
+ //TODO: implement frame loading after seek, then advanceFrame can be removed
+ _vqaPhotoPlayer->seekToFrame(_vqaLastFrame);
+ int nextFrame = _vqaPhotoPlayer->getFrameCount() / 4;
+ if (nextFrame <= 0) {
+ nextFrame = 1;
+ } else if (nextFrame > 4) {
+ nextFrame = 4;
+ }
+ flash = true;
+ advanceFrame = true;
+ _vqaLastFrame -= nextFrame;
+ }
+
+ _vqaPhotoPlayer->update(true, advanceFrame);
+ if (flash) {
+ flashViewport();
+ }
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, kScreen);
+ drawGrid(surface);
+ if (timeNow > _timeZoomNext && _vqaLastFrame <= 0) {
+ _vqaPhotoPlayer->close();
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ //TODO: there is code to stop zooming, but it is not working properly in original game
+ // if (_isMouseDown) {
+ // zoomOutStart();
+ // } else {
+ // zoomOutStop();
+ // }
+ zoomOutStart();
+ }
+}
+
+void ESPER::drawPhoto(Graphics::Surface &surface) {
+ copyImageBlur(_photoData, _viewport, &surface, kScreen, _blur);
+}
+
+void ESPER::drawGrid(Graphics::Surface &surface) {
+ for (int i = 0; i < 7; ++i) {
+ surface.drawLine(kScreen.left + i * 50, kScreen.top, kScreen.left + i * 50, kScreen.bottom - 1, 0x109C);
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ surface.drawLine(kScreen.left, kScreen.top + i * 44, kScreen.right - 1, kScreen.top + i * 44, 0x109C);
+ }
+}
+
+void ESPER::drawPhotoWithGrid(Graphics::Surface &surface) {
+ copyImageScale(_photoData, _viewport, &surface, kScreen);
+ drawGrid(surface);
+}
+
+void ESPER::drawSelection(Graphics::Surface &surface, bool crosshair, int style) {
+ int left = CLIP(_selectionRect.left, kScreen.left, (int16)(kScreen.right - 1));
+ int top = CLIP(_selectionRect.top, kScreen.top, (int16)(kScreen.bottom - 1));
+ int right = CLIP(_selectionRect.right, kScreen.left, (int16)(kScreen.right - 1));
+ int bottom = CLIP(_selectionRect.bottom, kScreen.top, (int16)(kScreen.bottom - 1));
+
+ int color = 0x0240;
+ if (style) {
+ color = 0x03E0;
+ }
+
+ // selection rectangle
+ Common::Rect selectedRect(MIN(left, right), MIN(top, bottom), MAX(left, right) + 1, MAX(top, bottom) + 1);
+ Common::Rect selectedRect2 = selectedRect;
+ selectedRect2.grow(-1);
+ surface.frameRect(selectedRect, color);
+ surface.frameRect(selectedRect2, color);
+
+ if (crosshair) {
+ if (_selectionCrosshairX == -1) {
+ if (_selectionRect.left < (kScreen.left + kScreen.right) / 2) {
+ _selectionCrosshairX = kScreen.left;
+ } else {
+ _selectionCrosshairX = kScreen.right - 1;
+ }
+ }
+ if (_selectionCrosshairY == -1) {
+ if (_selectionRect.top < (kScreen.top + kScreen.bottom) / 2) {
+ _selectionCrosshairY = kScreen.top;
+ } else {
+ _selectionCrosshairY = kScreen.bottom - 1;
+ }
+ }
+
+ // ghosting
+ if (_selectionCrosshairX != right) {
+ surface.vLine(_selectionCrosshairX, kScreen.top, kScreen.bottom - 1, 0x0240);
+ if (abs(_selectionCrosshairX - right) <= 1) {
+ _selectionCrosshairX = right;
+ } else {
+ _selectionCrosshairX = (_selectionCrosshairX + right) / 2;
+ }
+ }
+ if (_selectionCrosshairY != bottom) {
+ surface.hLine(kScreen.left, _selectionCrosshairY, kScreen.right - 1, 0x0240);
+ if (abs(_selectionCrosshairY - bottom) <= 1) {
+ _selectionCrosshairY = bottom;
+ } else {
+ _selectionCrosshairY = (_selectionCrosshairY + bottom) / 2;
+ }
+ }
+
+ surface.vLine(right, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, bottom, kScreen.right - 1, 0x03E0);
+ }
+}
+
+void ESPER::drawVideoFrame(Graphics::Surface &surface) {
+ _vqaPhotoPlayer->update(true, false);
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, kScreen);
+}
+
+void ESPER::drawTextCoords(Graphics::Surface &surface) {
+ _vm->_mainFont->drawColor(Common::String::format("ZM %04.0f", _zoom / _zoomMin * 2.0f ), surface, 155, 364, 0x001F);
+ _vm->_mainFont->drawColor(Common::String::format("NS %04d", 12 * _viewport.top + 98 ), surface, 260, 364, 0x001F);
+ _vm->_mainFont->drawColor(Common::String::format("EW %04d", 12 * _viewport.left + 167), surface, 364, 364, 0x001F);
+}
+
+void ESPER::flashViewport() {
+ uint16 *ptr = (uint16 *)_viewportData->getPixels();
+ for (int i = 0; i < _viewportData->w * _viewportData->h; ++i) {
+ int8 r = (*ptr >> 10) & 0x1F;
+ int8 g = (*ptr >> 5) & 0x1F;
+ int8 b = (*ptr ) & 0x1F;
+ b = MIN(b * 2, 31);
+ *ptr = r << 10 | g << 5 | b;
+
+ ++ptr;
+ }
+}
+
+void ESPER::copyImageScale(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect) {
+ if (_flash) {
+ playSound(419, 25);
+ }
+
+ int srcDstWidthRatio = srcRect.width() / dstRect.width();
+ int srcDstWidthRest = srcRect.width() % dstRect.width();
+ int srcDstHeightRatio = srcRect.height() / dstRect.height();
+ int srcDstHeightRest = srcRect.height() % dstRect.height();
+
+ if (srcRect.width() > dstRect.width() && srcRect.height() > dstRect.height()) {
+ // reduce
+ int srcY = srcRect.top;
+ int srcYCounter = 0;
+ for (int dstY = dstRect.top; dstY < dstRect.bottom; ++dstY) {
+ int srcX = srcRect.left;
+ int srcXCounter = 0;
+ for (int dstX = dstRect.left; dstX < dstRect.right; ++dstX) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ srcX += srcDstWidthRatio;
+ srcXCounter += srcDstWidthRest;
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ ++srcX;
+ }
+ }
+
+ srcY += srcDstHeightRatio;
+ srcYCounter += srcDstHeightRest;
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ ++srcY;
+ }
+ }
+ } else {
+ // enlarge
+ int srcY = srcRect.top;
+ int srcYCounter = 0;
+ for (int dstY = dstRect.top; dstY < dstRect.bottom; ++dstY) {
+ int srcX = srcRect.left;
+ int srcXCounter = 0;
+ for (int dstX = dstRect.left; dstX < dstRect.right; ++dstX) {
+ srcXCounter += srcRect.width();
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ ++srcX;
+ }
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+ }
+
+ srcYCounter += srcRect.height();
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ ++srcY;
+ }
+ }
+ }
+ _flash = false;
+}
+
+void ESPER::copyImageBlur(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect, float blur) {
+ if (_flash) {
+ playSound(419, 25);
+ }
+
+ int srcDstWidthRatio = srcRect.width() / dstRect.width();
+ int srcDstWidthRest = srcRect.width() % dstRect.width();
+ int srcDstHeightRatio = srcRect.height() / dstRect.height();
+ int srcDstHeightRest = srcRect.height() % dstRect.height();
+
+ int skipStep = (blur - (int)blur) * 1000.0f;
+ if (srcRect.width() > dstRect.width() && srcRect.height() > dstRect.height()) {
+ // reduce
+ int srcY = srcRect.top;
+ int dstY = dstRect.top;
+ int srcYCounter = 0;
+ int skipYMaxCounter = 0;
+ while (dstY < dstRect.bottom) {
+ skipYMaxCounter += skipStep;
+ int skipYMax = blur;
+ if (skipYMaxCounter >= 1000) {
+ skipYMaxCounter -= 1000;
+ ++skipYMax;
+ }
+ int skipY = 0;
+ while (dstY < dstRect.bottom && skipY < skipYMax) {
+ int srcX = srcRect.left;
+ int dstX = dstRect.left;
+ int srcXCounter = 0;
+ int skipXMaxCounter = 0;
+ while (dstX < dstRect.right) {
+ skipXMaxCounter += skipStep;
+ int skipXMax = blur;
+ if (skipXMaxCounter >= 1000) {
+ skipXMaxCounter -= 1000;
+ ++skipXMax;
+ }
+ int skipX = 0;
+ while (dstX < dstRect.right && skipX < skipXMax) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ ++dstX;
+ ++skipX;
+ }
+ srcXCounter += srcDstWidthRest;
+ srcX += srcDstWidthRatio * skipX;
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ srcX += skipX;
+ }
+ }
+
+ ++dstY;
+ ++skipY;
+ }
+
+ srcYCounter += srcDstHeightRest;
+ srcY += srcDstHeightRatio * skipY;
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ srcY += skipY;
+ }
+ }
+ } else {
+ // enlarge
+ int srcY = srcRect.top;
+ int dstY = dstRect.top;
+ int srcYCounter = srcRect.height(); // TODO: look at this again because in original source this is 0, but then first line is doubled
+ int skipYMaxCounter = 0;
+ while (dstY < dstRect.bottom) {
+ skipYMaxCounter += skipStep;
+ int skipYMax = blur;
+ if (skipYMaxCounter >= 1000) {
+ skipYMaxCounter -= 1000;
+ ++skipYMax;
+ }
+ int skipY = 0;
+ while (dstY < dstRect.bottom && skipY < skipYMax) {
+ int srcX = srcRect.left;
+ int dstX = dstRect.left;
+ int srcXCounter = 0;
+ int skipXMaxCounter = 0;
+ while (dstX < dstRect.right) {
+ skipXMaxCounter += skipStep;
+ int skipXMax = blur;
+ if (skipXMaxCounter >= 1000) {
+ skipXMaxCounter -= 1000;
+ ++skipXMax;
+ }
+ int skipX = 0;
+ while (dstX < dstRect.right && skipX < skipXMax) {
+ srcXCounter += srcRect.width();
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ srcX += 1; // bug in original game? Is using 1 instead of skipX as for Y
+ }
+
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ ++dstX;
+ ++skipX;
+ }
+ }
+
+ ++dstY;
+ ++skipY;
+ }
+
+ srcYCounter += srcRect.height();
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ srcY += skipY;
+ }
+ }
+ }
+ _flash = false;
+}
+
+void ESPER::copyImageBlit(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect) {
+ for (int y = 0; y < dstRect.height(); ++y) {
+ for (int x = 0; x < dstRect.width(); ++x) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcRect.left + x, srcRect.top + y);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstRect.left + x, dstRect.top + y);
+ *dstPtr = *srcPtr;
+ }
+ }
+}
+
+void ESPER::tickSound() {
+ if (_soundId1 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId1), _volume1, 0, 0, 50, 0);
+ _soundId1 = -1;
+ }
+ if (_soundId2 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId2), _volume2, 0, 0, 50, 0);
+ _soundId2 = -1;
+ }
+ if (_soundId3 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId3), _volume3, 0, 0, 50, 0);
+ _soundId3 = -1;
+ }
+}
+
+void ESPER::tickMouse(Graphics::Surface &surface) {
+ if (_vm->_mouse->isDisabled()) {
+ return;
+ }
+
+ int cursor = -1;
+
+ Common::Point p = _vm->getMousePos();
+
+ _mouseOverScroll = 4;
+ if (_stateMain == kEsperMainStatePhoto) {
+ if (kScreen.contains(p)) {
+ if (_statePhoto == kEsperPhotoStateShow) {
+ if ( _zoom != 2.0f) {
+ if (_isMouseDown) {
+ if (_isDrawingSelection) {
+ _selectionRect.right = p.x;
+ _selectionRect.bottom = p.y;
+ } else {
+ _selectionRect.left = p.x;
+ _selectionRect.top = p.y;
+ _selectionRect.right = p.x + 1;
+ _selectionRect.bottom = p.y + 1;
+ _isDrawingSelection = true;
+ }
+ } else {
+ if (_isDrawingSelection) {
+ _selectionRect.right = p.x;
+ _selectionRect.bottom = p.y;
+ if (_selectionRect.right < _selectionRect.left) {
+ SWAP(_selectionRect.left, _selectionRect.right);
+ }
+ if (_selectionRect.bottom < _selectionRect.top) {
+ SWAP(_selectionRect.bottom, _selectionRect.top);
+ }
+
+ if (_selectionRect.right >= _selectionRect.left + 3) {
+ updateSelection();
+ _vm->_mouse->disable();
+ zoomingStart();
+ } else {
+ resetSelectionRect();
+ }
+ }
+ _isDrawingSelection = false;
+ }
+ }
+ }
+ surface.vLine(p.x, p.y - 8, p.y - 1, 0x03E0);
+ surface.vLine(p.x, p.y + 8, p.y + 1, 0x03E0);
+ surface.hLine(p.x - 8, p.y, p.x - 1, 0x03E0);
+ surface.hLine(p.x + 8, p.y, p.x + 1, 0x03E0);
+ _mouseOverScroll = -1;
+ } else if (p.x >= 85 && p.y >= 73 && p.x <= 484 && p.y <= 436) {
+ if (!_isDrawingSelection && _statePhoto != kEsperPhotoStateVideoShow && _zoom != 2.0f) {
+ _mouseOverScroll = (angle_1024((kScreen.left + kScreen.right) / 2, (kScreen.top + kScreen.bottom) / 2, p.x, p.y) + 128) / 256;
+ if (_mouseOverScroll >= 4) {
+ _mouseOverScroll = 0;
+ }
+ if (_mouseOverScroll == 0 && this->_viewport.top == 0) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 1 && this->_viewport.right == kPhotoWidth - 1) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 2 && this->_viewport.bottom == kPhotoHeight - 1) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 3 && this->_viewport.left == 0) {
+ _mouseOverScroll = 4;
+ }
+ if (_mouseOverScroll != 4) {
+ cursor = _mouseOverScroll + 2;
+ }
+ }
+ }
+ }
+
+ if (_mouseOverScroll == 4) {
+ cursor = _buttons->hasHoveredImage() ? 1 : 0;
+ }
+ if (cursor != -1) {
+ _vm->_mouse->setCursor(cursor);
+ _vm->_mouse->draw(surface, p.x, p.y);
+ }
+}
+
+void ESPER::tickScroll() {
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow <= _timeScrollNext) {
+ return;
+ }
+ _timeScrollNext = timeNow + 300;
+
+ if (_scrollingDirection == 0) {
+ scrollUp();
+ } else if (_scrollingDirection == 1) {
+ scrollRight();
+ } else if (_scrollingDirection == 2) {
+ scrollDown();
+ } else if (_scrollingDirection == 3) {
+ scrollLeft();
+ }
+}
+
+int ESPER::findEmptyPhoto() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ if (!_photos[i].isPresent) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void ESPER::selectPhoto(int photoId) {
+ _vm->_mouse->disable();
+ _photoIdSelected = _photos[photoId].photoId;
+ unloadPhotos();
+ _script->photoSelected(_photoIdSelected);
+
+ Common::ScopedPtr<Common::SeekableReadStream> s(_vm->getResourceStream(_photos[photoId].name));
+
+ if (!s) {
+ reset();
+ }
+
+ int photoSize = _photoData->w * _photoData->h * _photoData->format.bytesPerPixel;
+
+ s->skip(3); // not used, but there is compression type
+ uint width = s->readUint32LE();
+ uint height = s->readUint32LE();
+ int photoCompressedSize = s->size() - s->pos();
+ uint8 *photoCompressed = (uint8 *)_photoData->getPixels() + photoSize - photoCompressedSize;
+ s->read(photoCompressed, photoCompressedSize);
+
+ decompress_lcw(photoCompressed, photoCompressedSize, (uint8 *)_photoData->getPixels(), photoSize);
+
+ // apply palette
+ for (uint j = 0; j < width * height; ++j) {
+ // _photoData[j] = Palette[_photoData[j]];
+ }
+
+ _shapeThumbnail = new Shape(_vm);
+ _shapeThumbnail->open("ESPTHUMB.SHP", _photos[photoId].shapeId);
+ _buttons->resetImages();
+ _buttons->defineImage(kPhotoCount + 2, Common::Rect(480, 350, 578, 413), _shapeThumbnail, _shapeThumbnail, _shapeThumbnail, nullptr);
+ _buttons->defineImage(kPhotoCount + 3, Common::Rect(42, 403, 76, 437), nullptr, nullptr, _shapeButton, nullptr);
+
+ resetPhotoOpening();
+ resetViewport();
+ setStateMain(kEsperMainStatePhotoOpening);
+ setStatePhoto(kEsperPhotoStateOpening);
+ playSound(422, 25);
+ playSound(423, 25);
+}
+
+void ESPER::unloadPhotos() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ if (_photos[i].isPresent) {
+ _buttons->resetImage(i);
+ delete _shapesPhotos[i];
+ _shapesPhotos[i] = nullptr;
+ _photos[i].isPresent = false;
+ }
+ }
+}
+
+int ESPER::findEmptyRegion() {
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (!_regions[i].isPresent) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int ESPER::findRegion(Common::Rect where) {
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (_regions[i].isPresent && _regions[i].rectOuter.contains(where) && where.contains(_regions[i].rectInner)){
+ return i;
+ }
+ }
+ return -1;
+}
+
+void ESPER::zoomingStart() {
+ prepareZoom();
+ setStatePhoto(kEsperPhotoStateSelectionZooming);
+}
+
+void ESPER::zoomOutStart() {
+ if (_statePhoto == kEsperPhotoStateVideoShow) {
+ resetPhotoZooming();
+ setStatePhoto(kEsperPhotoStateVideoZoomOut);
+ } else {
+ zoomOutStop();
+ if (_zoomMin < _zoom) {
+ _isZoomingOut = true;
+ setStatePhoto(kEsperPhotoStatePhotoZoomOut);
+ }
+ }
+}
+
+void ESPER::zoomOutStop() {
+ _isZoomingOut = false;
+ _statePhoto = kEsperPhotoStateShow;
+}
+
+void ESPER::scrollingStart(int direction) {
+ scrollingStop();
+ if ((direction != 3 || _viewport.left > 0)
+ && (direction != 0 || _viewport.top > 0)
+ && (direction != 1 || _viewport.right != kPhotoWidth - 1)
+ && (direction != 2 || _viewport.bottom != kPhotoHeight - 1)) {
+ _isScrolling = true;
+ _scrollingDirection = direction;
+ }
+}
+
+void ESPER::scrollingStop() {
+ _isScrolling = false;
+ _scrollingDirection = -1;
+}
+
+void ESPER::scrollUpdate() {
+ if ((_viewport.left == _viewportNext.left) && (_viewportNext.top == _viewport.top)) {
+ setStatePhoto(kEsperPhotoStateShow);
+ return;
+ }
+
+ if (_viewport.left != _viewportNext.left) {
+ _viewport.left = _viewportNext.left;
+ _viewport.right = _viewportNext.right;
+ _viewportPositionX = (_viewportNext.left + _viewportNext.right) / 2;
+ }
+
+ if (_viewport.top != _viewportNext.top) {
+ _viewport.top = _viewportNext.top;
+ _viewport.bottom = _viewportNext.bottom;
+ _viewportPositionY = (_viewportNext.top + _viewportNext.bottom) / 2;
+ }
+}
+
+void ESPER::scrollLeft() {this->_flash = 1;
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.left = _viewport.left - 40;
+ _viewportNext.right = _viewport.right - 40;
+ if (_viewportNext.left < 0) {
+ _viewportNext.right -= _viewportNext.left;
+ _viewportNext.left = 0;
+ scrollingStop();
+ }
+ _viewportNext.top = _viewport.top;
+ _viewportNext.bottom = _viewport.bottom;
+}
+
+void ESPER::scrollUp() {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.top = _viewport.top - 40;
+ _viewportNext.bottom = _viewport.bottom - 40;
+ if (_viewportNext.top < 0) {
+ _viewportNext.bottom -= _viewportNext.top;
+ _viewportNext.top = 0;
+ scrollingStop();
+ }
+ _viewportNext.left = _viewport.left;
+ _viewportNext.right = _viewport.right;
+}
+
+void ESPER::scrollRight() {
+ if (_viewport.right < kPhotoWidth - 1) {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.left = _viewport.left + 40;
+ _viewportNext.right = _viewport.right + 40;
+ _viewportNext.top = _viewport.top;
+ _viewportNext.bottom = _viewport.bottom;
+
+ if (_viewportNext.right > kPhotoWidth - 1) {
+ _viewportNext.left -= _viewportNext.right - (kPhotoWidth - 1);
+ _viewportNext.right = kPhotoWidth - 1;
+ scrollingStop();
+ }
+ }
+}
+
+void ESPER::scrollDown() {
+ if (_viewport.bottom < kPhotoHeight - 1) {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.top = _viewport.top + 40;
+ _viewportNext.bottom = _viewport.bottom + 40;
+ _viewportNext.left = _viewport.left;
+ _viewportNext.right = _viewport.right;
+
+ if (_viewportNext.bottom > kPhotoHeight - 1) {
+ _viewportNext.top -= _viewportNext.bottom - (kPhotoHeight - 1);
+ _viewportNext.bottom = kPhotoHeight - 1;
+ scrollingStop();
+ }
+ }
+}
+
+void ESPER::goBack() {
+ // CD-changing logic has been removed
+
+ if (_stateMain == kEsperMainStateList) {
+ close();
+ } else {
+ resetData();
+ activate(false);
+ }
+}
+
+void ESPER::prepareZoom() {
+ _selectionZoomStep = 0;
+ _timeSelectionZoomNext = 0;
+
+ _selectionRectTarget = _selectionRect;
+ resetSelectionRect();
+ _selectionRectDelta.left = (_selectionRectTarget.left - _selectionRect.left) / kSelectionZoomSteps;
+ _selectionRectDelta.top = (_selectionRectTarget.top - _selectionRect.top) / kSelectionZoomSteps;
+ _selectionRectDelta.right = (_selectionRectTarget.right - _selectionRect.right) / kSelectionZoomSteps;
+ _selectionRectDelta.bottom = (_selectionRectTarget.bottom - _selectionRect.bottom) / kSelectionZoomSteps;
+
+ Common::Rect rect = _selectionRectTarget;
+ if (_regionSelectedAck) {
+ rect.left = viewportXToScreenX(_regions[_regionSelected].rectSelection.left);
+ rect.top = viewportYToScreenY(_regions[_regionSelected].rectSelection.top);
+ rect.right = viewportXToScreenX(_regions[_regionSelected].rectSelection.right);
+ rect.bottom = viewportYToScreenY(_regions[_regionSelected].rectSelection.bottom);
+ }
+
+ _zoomSteps = 10;
+ float ratio = (rect.width() + 1.0f) / (float)kScreen.width();
+ if (ratio == 0.0f) {
+ _zoomTarget = ratio;
+ _zoomDelta = 0.0f;
+ } else {
+ _zoomTarget = CLIP(_zoom / ratio, _zoomMin, 2.0f);
+ _zoomSteps = CLIP((int)(_zoomTarget / _zoom) - 1, 0, 5) + 5;
+ _zoomDelta = (_zoomTarget - _zoom) / (float)_zoomSteps;
+ }
+
+ _blur = 1.0f;
+
+ _viewportPositionXTarget = _viewport.left + ((rect.left + rect.right) / 2 - kScreen.left) * _viewport.width() / kScreen.width();
+ _viewportPositionYTarget = _viewport.top + ((rect.top + rect.bottom) / 2 - kScreen.top ) * _viewport.height() / kScreen.height();
+ _viewportPositionXDelta = (_viewportPositionXTarget - _viewportPositionX) / (float)_zoomSteps;
+ _viewportPositionYDelta = (_viewportPositionYTarget - _viewportPositionY) / (float)_zoomSteps;
+ _viewportPositionXCurrent = _viewportPositionX;
+ _viewportPositionYCurrent = _viewportPositionY;
+}
+
+void ESPER::updateSelection() {
+ int selectionWidth = abs(_selectionRect.right + 1 - _selectionRect.left);
+ int selectionHeight = abs(_selectionRect.bottom + 1 - _selectionRect.top);
+
+ int photoSelectedWidth = _viewport.width() * selectionWidth / kScreen.width();
+ if (photoSelectedWidth < _screenHalfWidth) {
+ // minimal width of selection
+ selectionWidth = kScreen.width() * _screenHalfWidth / _viewport.width();
+ }
+
+ photoSelectedWidth = _viewport.height() * selectionHeight / kScreen.height();
+ if (photoSelectedWidth < _screenHalfHeight) {
+ // minimal height of selection
+ selectionHeight = kScreen.height() * _screenHalfHeight / _viewport.height();
+ }
+
+ // correct aspect ratio
+ if (selectionWidth / (float)kScreen.width() <= selectionHeight / (float)kScreen.height()) {
+ while (selectionWidth / (float)kScreen.width() <= selectionHeight / (float)kScreen.height()) {
+ ++selectionWidth;
+ }
+ } else {
+ while (selectionHeight / (float)kScreen.height() <= selectionWidth / (float)kScreen.width()) {
+ ++selectionHeight;
+ }
+ }
+
+ if (selectionWidth > kScreen.width()) {
+ selectionWidth = kScreen.width();
+ }
+ if (selectionHeight > kScreen.height()) {
+ selectionHeight = kScreen.height();
+ }
+
+ int left = _viewport.right - (kScreen.right - 1 - _selectionRect.left) * _viewport.width() / kScreen.width();
+ int right = _viewport.left + (_selectionRect.right - kScreen.left ) * _viewport.width() / kScreen.width();
+ int top = _viewport.bottom - (kScreen.bottom - 1 - _selectionRect.top ) * _viewport.height() / kScreen.height();
+ int bottom = _viewport.top + (_selectionRect.bottom - kScreen.top ) * _viewport.height() / kScreen.height();
+
+ bool stop = false;
+ bool alternate = false;
+
+ while (selectionWidth > abs(_selectionRect.right + 1 - _selectionRect.left)) {
+ if (alternate) {
+ --_selectionRect.left;
+ if (_selectionRect.left < 0) {
+ left = _viewport.right - (kScreen.right - 1 + 100 - _selectionRect.left) * _viewport.width() / kScreen.width();
+ if (left < 0) {
+ left = 0;
+ ++_selectionRect.left;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = false;
+ }
+ }
+ } else {
+ ++_selectionRect.right;
+ if (_selectionRect.right > kScreen.right - 1) {
+ right = _viewport.left + (_selectionRect.right - kScreen.left) * _viewport.width() / kScreen.width();
+ if (right > kPhotoWidth - 1) {
+ right = kPhotoWidth - 1;
+ --_selectionRect.right;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = true;
+ }
+ }
+ }
+ if (!stop) {
+ alternate = !alternate;
+ }
+ }
+
+ alternate = false;
+ stop = false;
+ while (selectionHeight > abs(_selectionRect.bottom + 1 - _selectionRect.top)) {
+ if (alternate) {
+ --_selectionRect.top;
+ if (_selectionRect.top < 0) {
+ top = _viewport.bottom - (kScreen.bottom - 1 - _selectionRect.top) * _viewport.height() / kScreen.height();
+ if (top < 0) {
+ top = 0;
+ ++_selectionRect.top;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = false;
+ }
+ }
+ } else {
+ ++_selectionRect.bottom;
+ if (_selectionRect.bottom > kScreen.bottom - 1) {
+ bottom = _viewport.top + (_selectionRect.bottom - kScreen.top) * _viewport.height() / kScreen.height();
+ if (bottom > kPhotoHeight - 1) {
+ bottom = kPhotoHeight - 1;
+ --_selectionRect.bottom;
+ if (stop) {
+ break;
+ }
+ alternate = true;
+ stop = true;
+ }
+ }
+ }
+ if (!stop) {
+ alternate = !alternate;
+ }
+ }
+
+ _regionSelected = findRegion(Common::Rect(left, top, right, bottom));
+ if (_regionSelected >= 0) {
+ _regionSelectedAck = true;
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+}
+
+int ESPER::viewportXToScreenX(int x) {
+ return kScreen.width() * (x - _viewport.left) / _viewport.width() + kScreen.left;
+}
+
+int ESPER::viewportYToScreenY(int y) {
+ return kScreen.height() * (y - _viewport.top) / _viewport.height() + kScreen.top;
+}
+
+} // End of namespace BladeRunner