From 6e9a340640686fe7dd95efbea34cbf3b7f4209af Mon Sep 17 00:00:00 2001 From: Peter Kohaut Date: Wed, 31 Jan 2018 00:37:19 +0100 Subject: BLADERUNNER: ESPER interface --- engines/bladerunner/ui/elevator.cpp | 2 +- engines/bladerunner/ui/esper.cpp | 1791 +++++++++++++++++++++++ engines/bladerunner/ui/esper.h | 285 ++++ engines/bladerunner/ui/kia.cpp | 188 +-- engines/bladerunner/ui/kia.h | 35 +- engines/bladerunner/ui/kia_section_crimes.cpp | 2 +- engines/bladerunner/ui/kia_section_suspects.cpp | 2 +- engines/bladerunner/ui/kia_shapes.cpp | 2 +- engines/bladerunner/ui/spinner.cpp | 3 +- engines/bladerunner/ui/ui_image_picker.cpp | 17 +- engines/bladerunner/ui/ui_image_picker.h | 2 +- 11 files changed, 2216 insertions(+), 113 deletions(-) create mode 100644 engines/bladerunner/ui/esper.cpp create mode 100644 engines/bladerunner/ui/esper.h (limited to 'engines/bladerunner/ui') diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp index 1ee4115fab..4c72be2345 100644 --- a/engines/bladerunner/ui/elevator.cpp +++ b/engines/bladerunner/ui/elevator.cpp @@ -76,7 +76,7 @@ int Elevator::activate(int elevatorId) { for (int i = 0; i != 16; ++i) { _shapes.push_back(new Shape(_vm)); - _shapes[i]->readFromContainer("ELEVATOR.SHP", i); + _shapes[i]->open("ELEVATOR.SHP", i); } _imagePicker->resetImages(); 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 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 diff --git a/engines/bladerunner/ui/esper.h b/engines/bladerunner/ui/esper.h new file mode 100644 index 0000000000..9d0dd6d012 --- /dev/null +++ b/engines/bladerunner/ui/esper.h @@ -0,0 +1,285 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BLADERUNNER_ESPER_H +#define BLADERUNNER_ESPER_H + +#include "common/array.h" +#include "common/rect.h" + +namespace Graphics { +struct Surface; +} + +namespace BladeRunner { + +class BladeRunnerEngine; +class Font; +class Shape; +class VQAPlayer; +class UIImagePicker; +class ESPERScript; + +// CD-changing logic has been removed + +enum EsperMainStates { + kEsperMainStateOpening = 0, + kEsperMainStateList = 1, + kEsperMainStatePhotoOpening = 2, + kEsperMainStateClear = 3, + kEsperMainStatePhoto = 5 +}; + +enum EsperPhotoStates { + kEsperPhotoStateShow = 0, + kEsperPhotoStateOpening = 1, + kEsperPhotoStateScrolling = 2, + kEsperPhotoStateSelectionZooming = 3, + kEsperPhotoStateSelectionBlinking = 4, + kEsperPhotoStatePhotoZooming = 5, + kEsperPhotoStatePhotoSharpening = 6, + kEsperPhotoStatePhotoZoomOut = 7, + kEsperPhotoStateVideoZooming = 8, + kEsperPhotoStateVideoShow = 9, + kEsperPhotoStateVideoZoomOut = 10 +}; + +class ESPER { + static const int kPhotoCount = 12; + static const int kRegionCount = 6; + static const int kPhotoWidth = 1280; + static const int kPhotoHeight = 960; + static const int kSelectionZoomSteps = 6; + + static const Common::Rect kScreen; + + struct Photo { + bool isPresent; + char name[13]; + int photoId; + int shapeId; + }; + + struct Region { + bool isPresent; + int regionId; + Common::Rect rectInner; + Common::Rect rectOuter; + Common::Rect rectSelection; + char name[13]; + }; + + BladeRunnerEngine *_vm; + ESPERScript *_script; + + bool _isWaiting; + bool _isOpen; + + UIImagePicker *_buttons; + + Graphics::Surface *_photoData; + Graphics::Surface *_viewportData; + + VQAPlayer *_vqaMainPlayer; + VQAPlayer *_vqaPhotoPlayer; + int _vqaLastFrame; + + Shape *_shapeButton; + Common::Array _shapesPhotos; + Shape *_shapeThumbnail; + + Photo _photos[kPhotoCount]; + int _photoIdSelected; + + Region _regions[kRegionCount]; + int _regionSelected; + bool _regionSelectedAck; + + EsperMainStates _stateMain; + EsperPhotoStates _statePhoto; + + bool _isDrawingSelection; + bool _isMouseDown; + int _mouseOverScroll; + + float _zoomHorizontal; + float _zoomVertical; + float _zoom; + float _zoomMin; + float _zoomTarget; + float _zoomDelta; + float _blur; + int _zoomSteps; + int _zoomStep; + int _timeZoomNext; + + bool _isZoomingOut; + int _timeZoomOutNext; + + Common::Rect _viewport; + Common::Rect _viewportNext; + int _viewportPositionX; + int _viewportPositionY; + float _viewportPositionXCurrent; + float _viewportPositionYCurrent; + int _viewportPositionXTarget; + int _viewportPositionYTarget; + float _viewportPositionXDelta; + float _viewportPositionYDelta; + int _viewportWidth; + int _viewportHeight; + + int _screenHalfWidth; + int _screenHalfHeight; + + int _flash; + + Common::Rect _selectionRect; + Common::Rect _selectionRectTarget; + Common::Rect _selectionRectDelta; + int _selectionCrosshairX; + int _selectionCrosshairY; + + int _selectionBlinkingCounter; + int _selectionBlinkingStyle; + int _timeSelectionBlinkingNext; + + int _selectionZoomStep; + int _timeSelectionZoomNext; + + int _photoOpeningWidth; + int _photoOpeningHeight; + int _timePhotoOpeningNext; + + bool _isScrolling; + int _scrollingDirection; + int _timeScrollNext; + + int _soundId1; + int _volume1; + int _soundId2; + int _volume2; + int _soundId3; + int _volume3; + int _ambientVolume; + +public: + ESPER(BladeRunnerEngine *vm); + ~ESPER(); + + void open(Graphics::Surface *surface); + void close(); + bool isOpen(); + + void handleMouseUp(int x, int y, bool buttonLeft); + void handleMouseDown(int x, int y, bool buttonLeft); + + void tick(); + + void resume(); + + void addPhoto(const char *name, int photoId, int shapeId); + void defineRegion(int regionId, Common::Rect inner, Common::Rect outer, Common::Rect selection, const char *name); + +private: + static void mouseDownCallback(int, void *); + static void mouseUpCallback(int, void *); + + void reset(); + void resetData(); + void resetPhotos(); + void resetRegions(); + void resetViewport(); + void resetSelectionRect(); + void resetSelectionBlinking(); + void resetPhotoZooming(); + void resetPhotoOpening(); + + void updateViewport(); + + void activate(bool withOpening); + void setStateMain(EsperMainStates state); + void setStatePhoto(EsperPhotoStates state); + + void wait(int timeout); + void playSound(int soundId, int volume); + + void draw(Graphics::Surface &surface); + + void drawPhotoOpening(Graphics::Surface &surface); + bool drawSelectionZooming(Graphics::Surface &surface); + bool drawSelectionBlinking(Graphics::Surface &surface); + void drawPhotoZooming(Graphics::Surface &surface); + void drawPhotoSharpening(Graphics::Surface &surface); + void drawPhotoZoomOut(Graphics::Surface &surface); + void drawVideoZooming(Graphics::Surface &surface); + void drawVideoZoomOut(Graphics::Surface &surface); + + void drawPhoto(Graphics::Surface &surface); + void drawGrid(Graphics::Surface &surface); + void drawPhotoWithGrid(Graphics::Surface &surface); + void drawSelection(Graphics::Surface &surface, bool crosshair, int style); + void drawVideoFrame(Graphics::Surface &surface); + void drawTextCoords(Graphics::Surface &surface); + + void flashViewport(); + + void copyImageScale(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect); + void copyImageBlur(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect, float u); + void copyImageBlit(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect); + + void tickSound(); + void tickMouse(Graphics::Surface &surface); + void tickScroll(); + + int findEmptyPhoto(); + void selectPhoto(int photoId); + void unloadPhotos(); + + int findEmptyRegion(); + int findRegion(Common::Rect where); + + void zoomingStart(); + void zoomOutStart(); + void zoomOutStop(); + + void scrollingStart(int direction); + void scrollingStop(); + void scrollUpdate(); + void scrollLeft(); + void scrollUp(); + void scrollRight(); + void scrollDown(); + + void goBack(); + + void prepareZoom(); + void updateSelection(); + + int viewportXToScreenX(int x); + int viewportYToScreenY(int y); + +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp index 95a66b6332..7563816f0f 100644 --- a/engines/bladerunner/ui/kia.cpp +++ b/engines/bladerunner/ui/kia.cpp @@ -59,20 +59,6 @@ namespace BladeRunner { const char *KIA::kPogo = "POGO"; -enum KIASections { - kKIASectionNone = 0, - kKIASectionCrimes = 1, - kKIASectionSuspects = 2, - kKIASectionClues = 3, - kKIASectionSettings = 4, - kKIASectionHelp = 5, - kKIASectionSave = 6, - kKIASectionLoad = 7, - kKIASectionQuit = 8, - kKIASectionDiagnostic = 9, - kKIASectionPogo = 10 -}; - KIA::KIA(BladeRunnerEngine *vm) { _vm = vm; @@ -137,12 +123,81 @@ void KIA::openLastOpened() { open(_lastSectionIdKIA); } -void KIA::openOptions() { - open(kKIASectionSettings); +void KIA::open(KIASections sectionId) { + if (_currentSectionId == sectionId) { + return; + } + + if (!sectionId) { + unload(); + return; + } + + if (!isOpen()) { + init(); + } + + switch (_currentSectionId) { + case kKIASectionCrimes: + _crimesSection->saveToLog(); + break; + case kKIASectionSuspects: + _suspectsSection->saveToLog(); + break; + case kKIASectionClues: + _cluesSection->saveToLog(); + break; + default: + break; + } + + if (sectionId != kKIASectionCrimes && sectionId != kKIASectionSuspects && sectionId != kKIASectionClues) { + playerReset(); + } + + _transitionId = getTransitionId(_currentSectionId, sectionId); + const char *name = getSectionVqaName(sectionId); + if (getSectionVqaName(_currentSectionId) != name) { + if (_mainVqaPlayer) { + _mainVqaPlayer->close(); + delete _mainVqaPlayer; + _mainVqaPlayer = nullptr; + } + + _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); + _mainVqaPlayer->open(name); + } + + if (_transitionId) { + playTransitionSound(_transitionId); + _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr); + _mainVqaPlayer->setLoop(getVqaLoopMain(sectionId), -1, kLoopSetModeEnqueue, loopEnded, this); + } else { + int loopId = getVqaLoopMain(sectionId); + _mainVqaPlayer->setLoop(loopId, -1, kLoopSetModeImmediate, nullptr, nullptr); + _mainVqaPlayer->setLoop(loopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr); + } + + _buttons->resetImages(); + createButtons(sectionId); + switchSection(sectionId); + _currentSectionId = sectionId; + + if (sectionId == kKIASectionCrimes || sectionId == kKIASectionSuspects || sectionId == kKIASectionClues) { + _lastSectionIdKIA = _currentSectionId; + } + + if (sectionId == kKIASectionSettings || sectionId == kKIASectionHelp || sectionId == kKIASectionSave || sectionId == kKIASectionLoad) { + _lastSectionIdOptions = _currentSectionId; + } +} + +bool KIA::isOpen() { + return _currentSectionId != kKIASectionNone; } void KIA::tick() { - if (!_currentSectionId) { + if (!isOpen()) { return; } @@ -235,8 +290,9 @@ void KIA::tick() { _shapes->get(39)->draw(_vm->_surfaceFront, 583, 342); } } + //TODO: implement frame loading after seek, then advanceFrame can be removed _playerVqaPlayer->seekToFrame(_playerVqaFrame); - _playerVqaPlayer->update(true); //_vm->_surfaceFront, 3 + _playerVqaPlayer->update(true, true); _playerSliceModelAngle += (float)(timeDiff) * 1.0f/400.0f; while (_playerSliceModelAngle >= 2 * M_PI) { @@ -296,11 +352,24 @@ void KIA::tick() { _vm->_mouse->draw(_vm->_surfaceFront, mouse.x, mouse.y); _vm->blitToScreen(_vm->_surfaceFront); + _vm->_system->delayMillis(10); + _timeLast = timeNow; } +void KIA::resume() { + // vqaPlayer::clear(this->_vqaPlayerMain); + if (_transitionId) { + _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr); + _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId), -1, kLoopSetModeEnqueue, loopEnded, this); + } else { + _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId), -1, kLoopSetModeImmediate, nullptr, nullptr); + _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId) + 1, -1, kLoopSetModeJustStart, nullptr, nullptr); + } +} + void KIA::handleMouseDown(int mouseX, int mouseY, bool mainButton) { - if (!_currentSectionId) { + if (!isOpen()) { return; } if (mainButton) { @@ -312,7 +381,7 @@ void KIA::handleMouseDown(int mouseX, int mouseY, bool mainButton) { } void KIA::handleMouseUp(int mouseX, int mouseY, bool mainButton) { - if (!_currentSectionId) { + if (!isOpen()) { return; } if (mainButton) { @@ -339,7 +408,7 @@ void KIA::handleMouseUp(int mouseX, int mouseY, bool mainButton) { } void KIA::handleKeyUp(const Common::KeyState &kbd) { - if (!_currentSectionId) { + if (!isOpen()) { return; } @@ -370,7 +439,7 @@ void KIA::handleKeyUp(const Common::KeyState &kbd) { } void KIA::handleKeyDown(const Common::KeyState &kbd) { - if (!_currentSectionId) { + if (!isOpen()) { return; } switch (kbd.keycode) { @@ -473,7 +542,7 @@ void KIA::playPhotograph(int photographId) { } _playerPhotographId = photographId; _playerPhotograph = new Shape(_vm); - _playerPhotograph->readFromContainer("photos.shp", photographId); + _playerPhotograph->open("photos.shp", photographId); } void KIA::mouseDownCallback(int buttonId, void *callbackData) { @@ -544,73 +613,6 @@ void KIA::loopEnded(void *callbackData, int frame, int loopId) { self->_transitionId = 0; } -void KIA::open(int sectionId) { - if (_currentSectionId == sectionId) { - return; - } - - if (!sectionId) { - unload(); - return; - } - - if (!_currentSectionId) { - init(); - } - - switch (_currentSectionId) { - case kKIASectionCrimes: - _crimesSection->saveToLog(); - break; - case kKIASectionSuspects: - _suspectsSection->saveToLog(); - break; - case kKIASectionClues: - _cluesSection->saveToLog(); - break; - } - - if (sectionId != kKIASectionCrimes && sectionId != kKIASectionSuspects && sectionId != kKIASectionClues) { - playerReset(); - } - - _transitionId = getTransitionId(_currentSectionId, sectionId); - const char *name = getSectionVqaName(sectionId); - if (getSectionVqaName(_currentSectionId) != name) { - if (_mainVqaPlayer) { - _mainVqaPlayer->close(); - delete _mainVqaPlayer; - _mainVqaPlayer = nullptr; - } - - _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); - _mainVqaPlayer->open(name); - } - - if (_transitionId) { - playTransitionSound(_transitionId); - _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr); - _mainVqaPlayer->setLoop(getVqaLoopMain(sectionId), -1, kLoopSetModeEnqueue, loopEnded, this); - } else { - int loopId = getVqaLoopMain(sectionId); - _mainVqaPlayer->setLoop(loopId, -1, kLoopSetModeImmediate, nullptr, nullptr); - _mainVqaPlayer->setLoop(loopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr); - } - - _buttons->resetImages(); - createButtons(sectionId); - switchSection(sectionId); - _currentSectionId = sectionId; - - if (sectionId == kKIASectionCrimes || sectionId == kKIASectionSuspects || sectionId == kKIASectionClues) { - _lastSectionIdKIA = _currentSectionId; - } - - if (sectionId == kKIASectionSettings || sectionId == kKIASectionHelp || sectionId == kKIASectionSave || sectionId == kKIASectionLoad) { - _lastSectionIdOptions = _currentSectionId; - } -} - void KIA::init() { if (!_vm->openArchive("MODE.MIX")) { return; @@ -641,7 +643,7 @@ void KIA::init() { } void KIA::unload() { - if (!_currentSectionId) { + if (!isOpen()) { return; } @@ -671,7 +673,7 @@ void KIA::unload() { _vm->closeArchive("MODE.MIX"); - _currentSectionId = 0; + _currentSectionId = kKIASectionNone; // TODO: Unfreeze game time @@ -846,7 +848,7 @@ void KIA::createButtons(int sectionId) { void KIA::buttonClicked(int buttonId) { int soundId = 0; - if (!_currentSectionId) { + if (!isOpen()) { return; } switch (buttonId) { diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h index 0452230382..0ac7a22600 100644 --- a/engines/bladerunner/ui/kia.h +++ b/engines/bladerunner/ui/kia.h @@ -54,6 +54,21 @@ class Shape; class UIImagePicker; class VQAPlayer; +enum KIASections { + kKIASectionNone = 0, + kKIASectionCrimes = 1, + kKIASectionSuspects = 2, + kKIASectionClues = 3, + kKIASectionSettings = 4, + kKIASectionHelp = 5, + kKIASectionSave = 6, + kKIASectionLoad = 7, + kKIASectionQuit = 8, + kKIASectionDiagnostic = 9, + kKIASectionPogo = 10 +}; + + class KIA { static const char *kPogo; static const int kPlayerActorDialogueQueueCapacity = 31; @@ -68,9 +83,6 @@ class KIA { int _forceOpen; int _transitionId; - int _lastSectionIdKIA; - int _lastSectionIdOptions; - int _playerVqaTimeLast; VQAPlayer *_playerVqaPlayer; int _playerVqaFrame; @@ -86,7 +98,11 @@ class KIA { int _playerActorDialogueQueueSize; int _playerActorDialogueState; + KIASections _currentSectionId; + KIASections _lastSectionIdKIA; + KIASections _lastSectionIdOptions; KIASectionBase *_currentSection; + KIASectionClues *_cluesSection; KIASectionCrimes *_crimesSection; KIASectionDiagnostic *_diagnosticSection; @@ -104,20 +120,22 @@ class KIA { int _pogoPos; public: - int _currentSectionId; - KIALog *_log; - KIAScript *_script; - KIAShapes *_shapes; + KIALog *_log; + KIAScript *_script; + KIAShapes *_shapes; public: KIA(BladeRunnerEngine *vm); ~KIA(); void openLastOpened(); - void openOptions(); + void open(KIASections sectionId); + bool isOpen(); void tick(); + void resume(); + void handleMouseDown(int mouseX, int mouseY, bool mainButton); void handleMouseUp(int mouseX, int mouseY, bool mainButton); void handleKeyUp(const Common::KeyState &kbd); @@ -133,7 +151,6 @@ private: static void mouseUpCallback(int buttonId, void *callbackData); static void loopEnded(void *callbackData, int frame, int loopId); - void open(int sectionId); void init(); void unload(); void switchSection(int sectionId); diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp index b0b3d7c234..a5ff221250 100644 --- a/engines/bladerunner/ui/kia_section_crimes.cpp +++ b/engines/bladerunner/ui/kia_section_crimes.cpp @@ -409,7 +409,7 @@ void KIASectionCrimes::updateSuspectPhoto() { if (_suspectPhotoShapeId != -1) { _suspectPhotoShape = new Shape(_vm); - _suspectPhotoShape->readFromContainer("photos.shp", _suspectPhotoShapeId); + _suspectPhotoShape->open("photos.shp", _suspectPhotoShapeId); } } diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp index ce13243f76..053705c9da 100644 --- a/engines/bladerunner/ui/kia_section_suspects.cpp +++ b/engines/bladerunner/ui/kia_section_suspects.cpp @@ -498,7 +498,7 @@ void KIASectionSuspects::updateSuspectPhoto() { if (_suspectPhotoShapeId != -1) { _suspectPhotoShape = new Shape(_vm); - _suspectPhotoShape->readFromContainer("photos.shp", _suspectPhotoShapeId); + _suspectPhotoShape->open("photos.shp", _suspectPhotoShapeId); } } diff --git a/engines/bladerunner/ui/kia_shapes.cpp b/engines/bladerunner/ui/kia_shapes.cpp index d992ec7045..ce5c8a7e5e 100644 --- a/engines/bladerunner/ui/kia_shapes.cpp +++ b/engines/bladerunner/ui/kia_shapes.cpp @@ -45,7 +45,7 @@ void KIAShapes::load() { for (uint i = 0; i < kShapeCount; ++i) { Shape *shape = new Shape(_vm); - shape->readFromContainer("kiaopt.shp", i); + shape->open("kiaopt.shp", i); _shapes[i] = shape; } diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp index 72c8b19070..2df9390afe 100644 --- a/engines/bladerunner/ui/spinner.cpp +++ b/engines/bladerunner/ui/spinner.cpp @@ -23,6 +23,7 @@ #include "bladerunner/ui/spinner.h" #include "bladerunner/bladerunner.h" +#include "bladerunner/game_constants.h" #include "bladerunner/mouse.h" #include "bladerunner/scene.h" #include "bladerunner/shape.h" @@ -123,7 +124,7 @@ int Spinner::chooseDestination(int loopId, bool immediately) { for (int j = 0; j != shapeCount; ++j) { _shapes.push_back(new Shape(_vm)); - _shapes[j]->readFromContainer("SPINNER.SHP", firstShapeId + j); + _shapes[j]->open("SPINNER.SHP", firstShapeId + j); } _imagePicker->resetImages(); diff --git a/engines/bladerunner/ui/ui_image_picker.cpp b/engines/bladerunner/ui/ui_image_picker.cpp index 6c46880e02..ca61a4d989 100644 --- a/engines/bladerunner/ui/ui_image_picker.cpp +++ b/engines/bladerunner/ui/ui_image_picker.cpp @@ -24,6 +24,7 @@ #include "bladerunner/bladerunner.h" #include "bladerunner/font.h" +#include "bladerunner/game_constants.h" #include "bladerunner/mouse.h" #include "bladerunner/shape.h" @@ -207,6 +208,7 @@ void UIImagePicker::draw(Graphics::Surface &surface) { } #if BLADERUNNER_DEBUG_RENDERING surface.frameRect(img.rect, 0x7fff); + _vm->_mainFont->drawColor(Common::String::format("%d", i), surface, (img.rect.left + img.rect.right) / 2, (img.rect.top + img.rect.bottom) / 2, 0x7fff); #endif } } @@ -252,11 +254,11 @@ void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) { _vm->_mainFont->drawColor(tooltip, surface, rect.left + 2, rect.top, 0x7FFF); } -void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) { +bool UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) { if (!_isVisible || ignore) { - return; + return false; } - + bool actionHandled = false; int hoveredImageIndex = -1; for (int i = 0; i != _imageCount; ++i) { if (_images[i].rect.contains(x, y)) { @@ -286,9 +288,10 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign if (down && !_isButtonDown) { _isButtonDown = true; _pressedImageIndex = _hoveredImageIndex; - if (_hoveredImageIndex != 1) { + if (_hoveredImageIndex != -1) { if (_mouseDownCallback) { _mouseDownCallback(_hoveredImageIndex, _callbackData); + actionHandled = true; } } } @@ -297,13 +300,17 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign if (up) { if (_isButtonDown) { if (_hoveredImageIndex == _pressedImageIndex && _pressedImageIndex != -1) { - if (_mouseUpCallback) + if (_mouseUpCallback) { _mouseUpCallback(_hoveredImageIndex, _callbackData); + actionHandled = true; + } } } _isButtonDown = false; _pressedImageIndex = -1; } + + return actionHandled; } void UIImagePicker::resetImage(int i) { diff --git a/engines/bladerunner/ui/ui_image_picker.h b/engines/bladerunner/ui/ui_image_picker.h index 00f2f3dfd9..d5b18b2f0a 100644 --- a/engines/bladerunner/ui/ui_image_picker.h +++ b/engines/bladerunner/ui/ui_image_picker.h @@ -91,7 +91,7 @@ public: void draw(Graphics::Surface &surface); void drawTooltip(Graphics::Surface &surface, int x, int y); - void handleMouseAction(int x, int y, bool down, bool up, bool ignore = false); + bool handleMouseAction(int x, int y, bool down, bool up, bool ignore = false); void resetImage(int i); bool hasHoveredImage(); -- cgit v1.2.3