From 0b484d51b838d740bbb1d6bc12c06c25d225c197 Mon Sep 17 00:00:00 2001 From: Peter Kohaut Date: Sat, 10 Feb 2018 20:34:28 +0100 Subject: BLADERUNNER: VK interface Code unification Removed few memory leaks --- engines/bladerunner/ui/elevator.cpp | 2 +- engines/bladerunner/ui/esper.cpp | 309 ++++---- engines/bladerunner/ui/esper.h | 16 +- engines/bladerunner/ui/kia.cpp | 6 +- engines/bladerunner/ui/kia.h | 4 +- engines/bladerunner/ui/kia_section_clues.cpp | 3 +- engines/bladerunner/ui/kia_section_crimes.cpp | 2 +- engines/bladerunner/ui/kia_section_suspects.cpp | 2 +- engines/bladerunner/ui/ui_image_picker.cpp | 24 +- engines/bladerunner/ui/vk.cpp | 917 ++++++++++++++++++++++++ engines/bladerunner/ui/vk.h | 174 +++++ 11 files changed, 1274 insertions(+), 185 deletions(-) create mode 100644 engines/bladerunner/ui/vk.cpp create mode 100644 engines/bladerunner/ui/vk.h (limited to 'engines/bladerunner/ui') diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp index 4c72be2345..2e6f036b4b 100644 --- a/engines/bladerunner/ui/elevator.cpp +++ b/engines/bladerunner/ui/elevator.cpp @@ -46,7 +46,7 @@ Elevator::Elevator(BladeRunnerEngine *vm) { Elevator::~Elevator() { delete _imagePicker; - reset(); + _imagePicker = nullptr; } int Elevator::activate(int elevatorId) { diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp index 3df6f7d11b..79b8f5d3ac 100644 --- a/engines/bladerunner/ui/esper.cpp +++ b/engines/bladerunner/ui/esper.cpp @@ -31,7 +31,7 @@ #include "bladerunner/game_info.h" #include "bladerunner/mouse.h" #include "bladerunner/shape.h" -#include "bladerunner/script/esper.h" +#include "bladerunner/script/esper_script.h" #include "bladerunner/text_resource.h" #include "bladerunner/ui/ui_image_picker.h" #include "bladerunner/vqa_player.h" @@ -39,8 +39,6 @@ #include "common/rect.h" #include "common/str.h" -#include "graphics/surface.h" - namespace BladeRunner { ESPER::ESPER(BladeRunnerEngine *vm) { @@ -55,12 +53,10 @@ ESPER::ESPER(BladeRunnerEngine *vm) { _isDrawingSelection = false; _isOpen = false; - _photoData = nullptr; - _viewportData = nullptr; _shapeButton = nullptr; _shapeThumbnail = nullptr; - _vqaMainPlayer = nullptr; - _vqaPhotoPlayer = nullptr; + _vqaPlayerMain = nullptr; + _vqaPlayerPhoto = nullptr; _script = nullptr; reset(); @@ -94,11 +90,9 @@ void ESPER::open(Graphics::Surface *surface) { return; } - _photoData = new Graphics::Surface(); - _photoData->create(kPhotoWidth, kPhotoHeight, createRGB555()); + _surfacePhoto.create(kPhotoWidth, kPhotoHeight, createRGB555()); - _viewportData = new Graphics::Surface(); - _viewportData->create(_screen.width(), _screen.height(), createRGB555()); + _surfaceViewport.create(_screen.width(), _screen.height(), createRGB555()); _viewportNext = _viewport; @@ -111,11 +105,11 @@ void ESPER::open(Graphics::Surface *surface) { _shapesPhotos.resize(10); - _vqaMainPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack); - if (!_vqaMainPlayer->open("ESPER.VQA")) { + _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack); + if (!_vqaPlayerMain->open("ESPER.VQA")) { return; } - _vqaMainPlayer->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr); + _vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr); _isOpen = true; _flash = false; @@ -143,16 +137,14 @@ void ESPER::close() { delete _shapeButton; _shapeButton = nullptr; - delete _photoData; - _photoData = nullptr; + _surfacePhoto.free(); - delete _viewportData; - _viewportData = nullptr; + _surfaceViewport.free(); - if (_vqaMainPlayer) { - _vqaMainPlayer->close(); - delete _vqaMainPlayer; - _vqaMainPlayer= nullptr; + if (_vqaPlayerMain) { + _vqaPlayerMain->close(); + delete _vqaPlayerMain; + _vqaPlayerMain= nullptr; } _vm->closeArchive("MODE.MIX"); @@ -163,7 +155,7 @@ void ESPER::close() { reset(); } -bool ESPER::isOpen() { +bool ESPER::isOpen() const { return _isOpen; } @@ -197,7 +189,7 @@ void ESPER::handleMouseDown(int x, int y, bool mainButton) { _isMouseDown = true; playSound(460, 100); } - if ( _mouseOverScroll >= 0 && _mouseOverScroll <= 3 && !_isScrolling) { + if (_mouseOverScroll >= 0 && _mouseOverScroll <= 3 && !_isScrolling) { scrollingStart(_mouseOverScroll); } tick(); @@ -210,7 +202,7 @@ void ESPER::handleMouseDown(int x, int y, bool mainButton) { } void ESPER::tick() { - if (!_vm->_windowIsActive) { + if (!_vm->_gameIsRunning) { return; } @@ -230,7 +222,8 @@ void ESPER::tick() { draw(_vm->_surfaceFront); _buttons->draw(_vm->_surfaceFront); - tickMouse(_vm->_surfaceFront); + drawMouse(_vm->_surfaceFront); + tickSound(); _vm->blitToScreen(_vm->_surfaceFront); @@ -315,11 +308,9 @@ void ESPER::mouseUpCallback(int buttonId, void *callbackData) { } void ESPER::reset() { - delete _photoData; - _photoData = nullptr; + _surfacePhoto.free(); - delete _viewportData; - _viewportData = nullptr; + _surfaceViewport.free(); delete _shapeButton; _shapeButton = nullptr; @@ -327,11 +318,11 @@ void ESPER::reset() { delete _shapeThumbnail; _shapeThumbnail = nullptr; - delete _vqaMainPlayer; - _vqaMainPlayer = nullptr; + delete _vqaPlayerMain; + _vqaPlayerMain = nullptr; - delete _vqaPhotoPlayer; - _vqaPhotoPlayer = nullptr; + delete _vqaPlayerPhoto; + _vqaPlayerPhoto = nullptr; delete _script; _script = nullptr; @@ -343,10 +334,10 @@ void ESPER::reset() { } void ESPER::resetData() { - if (_vqaPhotoPlayer) { - _vqaPhotoPlayer->close(); - delete _vqaPhotoPlayer; - _vqaPhotoPlayer = nullptr; + if (_vqaPlayerPhoto) { + _vqaPlayerPhoto->close(); + delete _vqaPlayerPhoto; + _vqaPlayerPhoto = nullptr; } if (_shapeThumbnail) { @@ -565,7 +556,7 @@ void ESPER::draw(Graphics::Surface &surface) { if (!_isOpen) { return; } - _vqaMainPlayer->update(false); + _vqaPlayerMain->update(false); switch (_stateMain) { case kEsperMainStateOpening: case kEsperMainStateList: @@ -669,7 +660,7 @@ void ESPER::drawPhotoOpening(Graphics::Surface &surface) { _timePhotoOpeningNext = timeNow + 20; } - copyImageScale(_photoData, _viewport, &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); + copyImageScale(&_surfacePhoto, _viewport, &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); surface.hLine(_screen.left, _photoOpeningHeight, _screen.right - 1, 0x03E0); surface.vLine(_photoOpeningWidth, _screen.top, _screen.bottom - 1, 0x03E0); @@ -819,13 +810,13 @@ void ESPER::drawPhotoSharpening(Graphics::Surface &surface) { } if (_regionSelectedAck && !_regions[_regionSelected].name.empty()) { - _vqaPhotoPlayer->update(true, false); - copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, _screen, _blur); - copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); + _vqaPlayerPhoto->update(true, false); + copyImageBlur(&_surfaceViewport, Common::Rect(0, 0, 299, 263), &surface, _screen, _blur); + copyImageBlit(&_surfaceViewport, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); } else { drawPhoto(surface); - copyImageScale(_photoData, _viewport, _viewportData, Common::Rect(0, 0, _screen.width(), _screen.height())); - copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); + copyImageScale(&_surfacePhoto, _viewport, &_surfaceViewport, Common::Rect(0, 0, _screen.width(), _screen.height())); + copyImageBlit(&_surfaceViewport, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(_screen.left, _screen.top, _photoOpeningWidth, _photoOpeningHeight)); } drawGrid(surface); @@ -871,14 +862,14 @@ void ESPER::drawPhotoZoomOut(Graphics::Surface &surface) { } void ESPER::drawVideoZooming(Graphics::Surface &surface) { - if (_vqaPhotoPlayer == nullptr) { - _vqaPhotoPlayer = new VQAPlayer(_vm, _viewportData); - if (!_vqaPhotoPlayer->open(Common::String(_regions[_regionSelected].name) + ".VQA")) { + if (_vqaPlayerPhoto == nullptr) { + _vqaPlayerPhoto = new VQAPlayer(_vm, &_surfaceViewport); + if (!_vqaPlayerPhoto->open(Common::String(_regions[_regionSelected].name) + ".VQA")) { setStatePhoto(kEsperPhotoStateShow); _vm->_mouse->enable(); - delete _vqaPhotoPlayer; - _vqaPhotoPlayer = nullptr; + delete _vqaPlayerPhoto; + _vqaPlayerPhoto = nullptr; return; } @@ -897,8 +888,8 @@ void ESPER::drawVideoZooming(Graphics::Surface &surface) { _blur += _zoomDelta * 5.0f; } - int frame = _vqaPhotoPlayer->update(true, advanceFrame); - if (frame == _vqaPhotoPlayer->getFrameCount() - 1) { + int frame = _vqaPlayerPhoto->update(true, advanceFrame); + if (frame == _vqaPlayerPhoto->getFrameCount() - 1) { _vqaLastFrame = frame; setStatePhoto(kEsperPhotoStatePhotoSharpening); } @@ -906,7 +897,7 @@ void ESPER::drawVideoZooming(Graphics::Surface &surface) { if (flash) { flashViewport(); } - copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, _screen, _blur); + copyImageBlur(&_surfaceViewport, Common::Rect(0, 0, 299, 263), &surface, _screen, _blur); drawGrid(surface); } @@ -918,8 +909,8 @@ void ESPER::drawVideoZoomOut(Graphics::Surface &surface) { _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; + _vqaPlayerPhoto->seekToFrame(_vqaLastFrame); + int nextFrame = _vqaPlayerPhoto->getFrameCount() / 4; if (nextFrame <= 0) { nextFrame = 1; } else if (nextFrame > 4) { @@ -930,29 +921,27 @@ void ESPER::drawVideoZoomOut(Graphics::Surface &surface) { _vqaLastFrame -= nextFrame; } - _vqaPhotoPlayer->update(true, advanceFrame); + _vqaPlayerPhoto->update(true, advanceFrame); if (flash) { flashViewport(); } - copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, _screen); + copyImageBlit(&_surfaceViewport, Common::Rect(0, 0, 0, 0), &surface, _screen); drawGrid(surface); if (timeNow > _timeZoomNext && _vqaLastFrame <= 0) { - _vqaPhotoPlayer->close(); - delete _vqaPhotoPlayer; - _vqaPhotoPlayer = nullptr; + _vqaPlayerPhoto->close(); + delete _vqaPlayerPhoto; + _vqaPlayerPhoto = nullptr; - //TODO: there is code to stop zooming, but it is not working properly in original game - // if (_isMouseDown) { - // zoomOutStart(); - // } else { - // zoomOutStop(); - // } - zoomOutStart(); + if (_vm->isMouseButtonDown()) { + zoomOutStart(); + } else { + zoomOutStop(); + } } } void ESPER::drawPhoto(Graphics::Surface &surface) { - copyImageBlur(_photoData, _viewport, &surface, _screen, _blur); + copyImageBlur(&_surfacePhoto, _viewport, &surface, _screen, _blur); } void ESPER::drawGrid(Graphics::Surface &surface) { @@ -966,7 +955,7 @@ void ESPER::drawGrid(Graphics::Surface &surface) { } void ESPER::drawPhotoWithGrid(Graphics::Surface &surface) { - copyImageScale(_photoData, _viewport, &surface, _screen); + copyImageScale(&_surfacePhoto, _viewport, &surface, _screen); drawGrid(surface); } @@ -1028,8 +1017,8 @@ void ESPER::drawSelection(Graphics::Surface &surface, bool crosshair, int style) } void ESPER::drawVideoFrame(Graphics::Surface &surface) { - _vqaPhotoPlayer->update(true, false); - copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, _screen); + _vqaPlayerPhoto->update(true, false); + copyImageBlit(&_surfaceViewport, Common::Rect(0, 0, 0, 0), &surface, _screen); } void ESPER::drawTextCoords(Graphics::Surface &surface) { @@ -1038,9 +1027,92 @@ void ESPER::drawTextCoords(Graphics::Surface &surface) { _vm->_mainFont->drawColor(Common::String::format("EW %04d", 12 * _viewport.left + 167), surface, 364, 364, 0x001F); } +void ESPER::drawMouse(Graphics::Surface &surface) { + if (_vm->_mouse->isDisabled()) { + return; + } + + int cursor = -1; + + Common::Point p = _vm->getMousePos(); + + _mouseOverScroll = 4; + if (_stateMain == kEsperMainStatePhoto) { + if (_screen.contains(p)) { + if ((_statePhoto == kEsperPhotoStateShow) && ( _zoom != 2.0f)) { + if (_isMouseDown) { + if (_isDrawingSelection) { + _selection.right = p.x; + _selection.bottom = p.y; + } else { + _selection.left = p.x; + _selection.top = p.y; + _selection.right = p.x + 1; + _selection.bottom = p.y + 1; + _isDrawingSelection = true; + } + } else { + if (_isDrawingSelection) { + _selection.right = p.x; + _selection.bottom = p.y; + + if (_selection.right < _selection.left) { + SWAP(_selection.left, _selection.right); + } + if (_selection.bottom < _selection.top) { + SWAP(_selection.bottom, _selection.top); + } + + if (_selection.right >= _selection.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((_screen.left + _screen.right) / 2, (_screen.top + _screen.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::flashViewport() { - uint16 *ptr = (uint16 *)_viewportData->getPixels(); - for (int i = 0; i < _viewportData->w * _viewportData->h; ++i) { + uint16 *ptr = (uint16 *)_surfaceViewport.getPixels(); + for (int i = 0; i < _surfaceViewport.w * _surfaceViewport.h; ++i) { int8 r = (*ptr >> 10) & 0x1F; int8 g = (*ptr >> 5) & 0x1F; int8 b = (*ptr ) & 0x1F; @@ -1303,89 +1375,6 @@ void ESPER::tickSound() { } } -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 (_screen.contains(p)) { - if ((_statePhoto == kEsperPhotoStateShow) && ( _zoom != 2.0f)) { - if (_isMouseDown) { - if (_isDrawingSelection) { - _selection.right = p.x; - _selection.bottom = p.y; - } else { - _selection.left = p.x; - _selection.top = p.y; - _selection.right = p.x + 1; - _selection.bottom = p.y + 1; - _isDrawingSelection = true; - } - } else { - if (_isDrawingSelection) { - _selection.right = p.x; - _selection.bottom = p.y; - - if (_selection.right < _selection.left) { - SWAP(_selection.left, _selection.right); - } - if (_selection.bottom < _selection.top) { - SWAP(_selection.bottom, _selection.top); - } - - if (_selection.right >= _selection.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((_screen.left + _screen.right) / 2, (_screen.top + _screen.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) { @@ -1425,20 +1414,20 @@ void ESPER::selectPhoto(int photoId) { reset(); } - int photoSize = _photoData->w * _photoData->h * _photoData->format.bytesPerPixel; + int photoSize = _surfacePhoto.w * _surfacePhoto.h * _surfacePhoto.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; + uint8 *photoCompressed = (uint8 *)_surfacePhoto.getPixels() + photoSize - photoCompressedSize; s->read(photoCompressed, photoCompressedSize); - decompress_lcw(photoCompressed, photoCompressedSize, (uint8 *)_photoData->getPixels(), photoSize); + decompress_lcw(photoCompressed, photoCompressedSize, (uint8 *)_surfacePhoto.getPixels(), photoSize); // apply palette for (uint j = 0; j < width * height; ++j) { - // _photoData[j] = Palette[_photoData[j]]; + // _surfacePhoto[j] = Palette[_surfacePhoto[j]]; } _shapeThumbnail = new Shape(_vm); diff --git a/engines/bladerunner/ui/esper.h b/engines/bladerunner/ui/esper.h index d63efa31bd..4a1c606498 100644 --- a/engines/bladerunner/ui/esper.h +++ b/engines/bladerunner/ui/esper.h @@ -26,9 +26,7 @@ #include "common/array.h" #include "common/rect.h" -namespace Graphics { -struct Surface; -} +#include "graphics/surface.h" namespace BladeRunner { @@ -96,11 +94,11 @@ class ESPER { UIImagePicker *_buttons; - Graphics::Surface *_photoData; - Graphics::Surface *_viewportData; + Graphics::Surface _surfacePhoto; + Graphics::Surface _surfaceViewport; - VQAPlayer *_vqaMainPlayer; - VQAPlayer *_vqaPhotoPlayer; + VQAPlayer *_vqaPlayerMain; + VQAPlayer *_vqaPlayerPhoto; int _vqaLastFrame; Shape *_shapeButton; @@ -190,7 +188,7 @@ public: void open(Graphics::Surface *surface); void close(); - bool isOpen(); + bool isOpen() const; void handleMouseUp(int x, int y, bool buttonLeft); void handleMouseDown(int x, int y, bool buttonLeft); @@ -242,6 +240,7 @@ private: void drawSelection(Graphics::Surface &surface, bool crosshair, int style); void drawVideoFrame(Graphics::Surface &surface); void drawTextCoords(Graphics::Surface &surface); + void drawMouse(Graphics::Surface &surface); void flashViewport(); @@ -250,7 +249,6 @@ private: 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(); diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp index 729bec8d84..c27a548b3a 100644 --- a/engines/bladerunner/ui/kia.cpp +++ b/engines/bladerunner/ui/kia.cpp @@ -33,7 +33,7 @@ #include "bladerunner/mouse.h" #include "bladerunner/scene.h" #include "bladerunner/shape.h" -#include "bladerunner/script/kia.h" +#include "bladerunner/script/kia_script.h" #include "bladerunner/settings.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/text_resource.h" @@ -192,7 +192,7 @@ void KIA::open(KIASections sectionId) { } } -bool KIA::isOpen() { +bool KIA::isOpen() const { return _currentSectionId != kKIASectionNone; } @@ -350,8 +350,8 @@ void KIA::tick() { _buttons->drawTooltip(_vm->_surfaceFront, mouse.x, mouse.y); } _vm->_mouse->draw(_vm->_surfaceFront, mouse.x, mouse.y); - _vm->blitToScreen(_vm->_surfaceFront); + _vm->blitToScreen(_vm->_surfaceFront); _vm->_system->delayMillis(10); _timeLast = timeNow; diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h index 0ac7a22600..1c2dc19d9f 100644 --- a/engines/bladerunner/ui/kia.h +++ b/engines/bladerunner/ui/kia.h @@ -25,7 +25,6 @@ #include "common/str.h" - namespace Common { struct KeyState; } @@ -68,7 +67,6 @@ enum KIASections { kKIASectionPogo = 10 }; - class KIA { static const char *kPogo; static const int kPlayerActorDialogueQueueCapacity = 31; @@ -130,7 +128,7 @@ public: void openLastOpened(); void open(KIASections sectionId); - bool isOpen(); + bool isOpen() const; void tick(); diff --git a/engines/bladerunner/ui/kia_section_clues.cpp b/engines/bladerunner/ui/kia_section_clues.cpp index fc6270b938..e003e9bb28 100644 --- a/engines/bladerunner/ui/kia_section_clues.cpp +++ b/engines/bladerunner/ui/kia_section_clues.cpp @@ -29,7 +29,7 @@ #include "bladerunner/game_flags.h" #include "bladerunner/game_info.h" #include "bladerunner/font.h" -#include "bladerunner/script/kia.h" +#include "bladerunner/script/kia_script.h" #include "bladerunner/text_resource.h" #include "bladerunner/ui/kia.h" #include "bladerunner/ui/kia_log.h" @@ -76,6 +76,7 @@ KIASectionClues::~KIASectionClues() { _uiContainer->clear(); delete _filterScrollBox; delete _cluesScrollBox; + delete _buttons; delete _uiContainer; } diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp index b70f790659..417a0588e0 100644 --- a/engines/bladerunner/ui/kia_section_crimes.cpp +++ b/engines/bladerunner/ui/kia_section_crimes.cpp @@ -30,7 +30,7 @@ #include "bladerunner/game_flags.h" #include "bladerunner/game_info.h" #include "bladerunner/shape.h" -#include "bladerunner/script/kia.h" +#include "bladerunner/script/kia_script.h" #include "bladerunner/suspects_database.h" #include "bladerunner/text_resource.h" #include "bladerunner/ui/kia.h" diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp index 0cad1123d7..af2c457d92 100644 --- a/engines/bladerunner/ui/kia_section_suspects.cpp +++ b/engines/bladerunner/ui/kia_section_suspects.cpp @@ -30,7 +30,7 @@ #include "bladerunner/game_flags.h" #include "bladerunner/game_info.h" #include "bladerunner/shape.h" -#include "bladerunner/script/kia.h" +#include "bladerunner/script/kia_script.h" #include "bladerunner/suspects_database.h" #include "bladerunner/text_resource.h" #include "bladerunner/ui/kia.h" diff --git a/engines/bladerunner/ui/ui_image_picker.cpp b/engines/bladerunner/ui/ui_image_picker.cpp index c0015f1721..639c02f592 100644 --- a/engines/bladerunner/ui/ui_image_picker.cpp +++ b/engines/bladerunner/ui/ui_image_picker.cpp @@ -136,7 +136,11 @@ bool UIImagePicker::setImageTooltip(int i, const char *tooltip) { return false; } - _images[i].tooltip = tooltip; + if (tooltip != nullptr) { + _images[i].tooltip = tooltip; + } else { + _images[i].tooltip.clear(); + } return true; } @@ -191,14 +195,17 @@ void UIImagePicker::draw(Graphics::Surface &surface) { continue; } - // TODO: Check interaction with Mouse::isDisabled if (i == _hoveredImageIndex && i == _pressedImageIndex && _isButtonDown) { - if (img.shapeDown) { - img.shapeDown->draw(surface, img.rect.left, img.rect.top); + if (!_vm->_mouse->isDisabled()) { + if (img.shapeDown) { + img.shapeDown->draw(surface, img.rect.left, img.rect.top); + } } } else if (i == _hoveredImageIndex && !_isButtonDown) { - if (img.shapeHovered) { - img.shapeHovered->draw(surface, img.rect.left, img.rect.top); + if (!_vm->_mouse->isDisabled()) { + if (img.shapeHovered) { + img.shapeHovered->draw(surface, img.rect.left, img.rect.top); + } } } else { if (img.shapeUp) { @@ -222,6 +229,11 @@ void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) { } Common::String &tooltip = _images[_hoveredImageIndex].tooltip; + + if (tooltip.empty()) { + return; + } + int width = _vm->_mainFont->getTextWidth(tooltip) + 1; int height = _vm->_mainFont->getTextHeight(tooltip) + 1; diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp new file mode 100644 index 0000000000..fab58e994f --- /dev/null +++ b/engines/bladerunner/ui/vk.cpp @@ -0,0 +1,917 @@ +/* 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/vk.h" + +#include "bladerunner/actor.h" +#include "bladerunner/ambient_sounds.h" +#include "bladerunner/audio_player.h" +#include "bladerunner/bladerunner.h" +#include "bladerunner/combat.h" +#include "bladerunner/font.h" +#include "bladerunner/game_constants.h" +#include "bladerunner/game_flags.h" +#include "bladerunner/game_info.h" +#include "bladerunner/mouse.h" +#include "bladerunner/music.h" +#include "bladerunner/scene.h" +#include "bladerunner/shape.h" +#include "bladerunner/script/vk_script.h" +#include "bladerunner/slice_renderer.h" +#include "bladerunner/text_resource.h" +#include "bladerunner/ui/ui_image_picker.h" +#include "bladerunner/vqa_player.h" + +#include "common/str.h" +#include "common/keyboard.h" + +namespace BladeRunner { + +VK::VK(BladeRunnerEngine *vm) { + _vm = vm; + + reset(); +} + +VK::~VK() { + reset(); +} + +void VK::open(int actorId, int calibrationRatio) { + if (!_vm->openArchive("MODE.MIX")) { + return; + } + + reset(); + + _questions.resize(3); + for (int i = 0; i < (int)_questions.size(); ++i) { + _questions[i].resize(18); + for (int j = 0; j < (int)_questions[i].size(); ++j) { + _questions[i][j].isPresent = false; + _questions[i][j].wasAsked = false; + } + } + + _volumeAmbient = _vm->_ambientSounds->getVolume(); + _volumeMusic = _vm->_music->getVolume(); + + _actorId = actorId; + _calibrationRatio = calibrationRatio; + _calibration = 0; + + _buttons = new UIImagePicker(_vm, 8); + + _shapes.resize(15); + for (int i = 0; i < (int)_shapes.size(); ++i) { + _shapes[i] = new Shape(_vm); + _shapes[i]->open("VK.SHP", i); + } + + _vqaPlayerMain = new VQAPlayer(_vm, &_vm->_surfaceBack); + if (!_vqaPlayerMain->open("VK.VQA")) { + return; + } + + Common::String eyeVqa; + switch (actorId) { + case kActorDektora: + eyeVqa = "VKDEKT.VQA"; + break; + case kActorLucy: + eyeVqa = "VKLUCY.VQA"; + break; + case kActorGrigorian: + eyeVqa = "VKKASH.VQA"; + break; + case kActorBulletBob: + eyeVqa = "VKBOB.VQA"; + break; + case kActorRunciter: + eyeVqa = "VKRUNC.VQA"; + break; + default: + return; + } + + _surfaceEye.create(172, 116, createRGB555()); + _vqaPlayerEye = new VQAPlayer(_vm, &_surfaceEye); + if (!_vqaPlayerEye->open(eyeVqa)) { + return; + } + if (!_vqaPlayerEye->setLoop(0, -1, kLoopSetModeEnqueue, nullptr, nullptr)) { + return; + } + + _isOpen = true; + + _script = new VKScript(_vm); + + //TODO: time->lock() + + init(); +} + +bool VK::isOpen() const { + return _isOpen; +} + +void VK::close() { + if (_vm->_audioPlayer->isActive(_soundTrackId1)) { + _vm->_audioPlayer->stop(_soundTrackId1, false); + } + + if (_vm->_audioPlayer->isActive(_soundTrackId3)) { + _vm->_audioPlayer->stop(_soundTrackId3, false); + } + + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(458), 33, 0, 0, 50, 0); + + _script->shutdown(_actorId, _humanProbability, _replicantProbability, _anxiety); + + delete _script; + _script = nullptr; + + + if (_buttons) { + _buttons->deactivate(); + _buttons->resetImages(); + delete _buttons; + _buttons = nullptr; + } + + if (_vqaPlayerEye) { + _vqaPlayerEye->close(); + delete _vqaPlayerEye; + _vqaPlayerEye = nullptr; + } + + if (_vqaPlayerMain) { + _vqaPlayerMain->close(); + delete _vqaPlayerMain; + _vqaPlayerMain = nullptr; + } + + _questions.clear(); + + for (int i = 0; i < (int)_shapes.size(); ++i) { + delete _shapes[i]; + } + _shapes.clear(); + + _vm->closeArchive("MODE.MIX"); + _vm->_music->setVolume(_volumeMusic); + _vm->_ambientSounds->setVolume(_volumeAmbient); + + // TODO: time->unlock(); + // _vm->_scene->resume(false); +} + +void VK::tick() { + int mouseX, mouseY; + _vm->_mouse->getXY(&mouseX, &mouseY); + if (!_vm->_mouse->isDisabled()) { + _buttons->handleMouseAction(mouseX, mouseY, false, false, false); + } + + draw(); + + _vm->blitToScreen(_vm->_surfaceFront); + _vm->_system->delayMillis(10); + + if (_isClosing && (int)_vm->getTotalPlayTime() >= _timeClose && !_script->isInsideScript()) { + close(); + _vm->_mouse->enable(); + reset(); + } +} + +void VK::handleMouseDown(int mouseX, int mouseY, bool mainButton) { + if (!_vm->_mouse->isDisabled()) { + if (!_buttons->handleMouseAction(mouseX, mouseY, true, false, false)) { + tick(); + } + } +} + +void VK::handleMouseUp(int mouseX, int mouseY, bool mainButton) { + if (!_vm->_mouse->isDisabled()) { + _buttons->handleMouseAction(mouseX, mouseY, false, true, false); + } +} + +void VK::addQuestion(int intensity, int sentenceId, int relatedSentenceId) { + for (int i = 0; i < (int)_questions[intensity].size(); ++i) { + if (!_questions[intensity][i].isPresent) { + _questions[intensity][i].isPresent = true; + _questions[intensity][i].sentenceId = sentenceId; + _questions[intensity][i].relatedSentenceId = relatedSentenceId; + return; + } + } +} + +void VK::playSpeechLine(int actorId, int sentenceId, float duration) { + _vm->gameWaitForActive(); + + _vm->_mouse->disable(); + + Actor *actor = _vm->_actors[actorId]; + actor->speechPlay(sentenceId, true); + + while (_vm->_gameIsRunning) { + // ActorSpeaking = 1; + _vm->_speechSkipped = false; + _vm->gameTick(); + // ActorSpeaking = 0; + if (_vm->_speechSkipped || !actor->isSpeeching()) { + actor->speechStop(); + break; + } + } + + if (duration > 0.0f && !_vm->_speechSkipped) { + int timeEnd = duration * 1000.0f + _vm->getTotalPlayTime(); + while (timeEnd > (int)_vm->getTotalPlayTime() && _vm->_gameIsRunning) { + _vm->gameTick(); + } + } + + _vm->_speechSkipped = false; + + _vm->_mouse->enable(); +} + +void VK::subjectReacts(int intensity, int humanResponse, int replicantResponse, int anxiety) { + humanResponse = CLIP(humanResponse, -20, 20); + replicantResponse = CLIP(replicantResponse, -20, 20); + + int timeNow = _vm->getTotalPlayTime(); + + if (intensity > 0) { + _needleValueTarget = 78 * intensity / 100; + _needleValueDelta = (_needleValueTarget - _needleValue) / 10; + _timeNextNeedleStep = timeNow + 66; + } + + if (humanResponse != 0) { + _humanProbability = CLIP(_humanProbability + humanResponse + _calibration, 0, 100); + if (_humanProbability >= 80 && !_isClosing) { + _isClosing = true; + _timeClose = timeNow + 3000; + _vm->_mouse->disable(); + } + _humanGaugeTarget = humanResponse; + _humanGaugeDelta = humanResponse / 10; + if (_humanGaugeDelta == 0) { + _humanGaugeDelta = humanResponse / abs(humanResponse); + } + } + + if (replicantResponse != 0) { + _replicantProbability = CLIP(_replicantProbability + replicantResponse - _calibration, 0, 100); + if (_replicantProbability >= 80 && !_isClosing) { + _isClosing = true; + _timeClose = timeNow + 3000; + _vm->_mouse->disable(); + } + _replicantGaugeTarget = replicantResponse; + _replicantGauge = replicantResponse / 10; + if (_replicantGaugeDelta == 0) { + _replicantGaugeDelta = replicantResponse / abs(replicantResponse); + } + } + + _anxiety = CLIP(_anxiety + anxiety, 0, 100); + if (_anxiety == 100 && !_isClosing) { + _isClosing = true; + _timeClose = timeNow + 3000; + _vm->_mouse->disable(); + } +} + +void VK::eyeAnimates(int loopId) { + _vqaPlayerEye->setLoop(loopId, -1, kLoopSetModeImmediate, nullptr, nullptr); + _vqaPlayerEye->setLoop(0, -1, kLoopSetModeEnqueue, nullptr, nullptr); +} + +void VK::mouseDownCallback(int buttonId, void *callbackData) { + VK *self = (VK *)callbackData; + + switch (buttonId) { + case 1: + self->startAdjustement(); + break; + case 2: + case 3: + case 4: + if (self->_calibrationStarted) { + self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(457), 100, 0, 0, 50, 0); + } + break; + case 5: + self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(457), 100, 0, 0, 50, 0); + break; + default: + return; + } +} + +void VK::mouseUpCallback(int buttonId, void *callbackData) { + VK *self = (VK *)callbackData; + + switch (buttonId) { + case 0: + self->calibrate(); + break; + case 1: + self->stopAdjustement(); + break; + case 2: + self->askQuestion(0); + break; + case 3: + self->askQuestion(1); + break; + case 4: + self->askQuestion(2); + break; + case 5: + self->_isClosing = true; + break; + default: + return; + } +} + +void VK::loopEnded(void *callbackData, int frame, int loopId) { + VK *self = (VK *)callbackData; + + self->_vqaLoopEnded = true; + self->_vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr); +} + +void VK::reset() { + _buttons = nullptr; + _vqaPlayerMain = nullptr; + _vqaPlayerEye = nullptr; + + _script = nullptr; + + _isOpen = false; + + _shapes.clear(); + + _calibrationCounter = 0; + _calibrationStarted = false; + _calibration = 0; + + _testStarted = false; + + _needleValue = 0; + _needleValueTarget = 0; + _needleValueDelta = 0; + _timeNextNeedleStep = 0; + _timeNeedleReturn = 0; + _timeNextNeedleOscillate = 0; + + _humanProbability = 0; + _humanGauge = 0; + _humanGaugeTarget = 0; + _humanGaugeDelta = 0; + _timeNextHumanGaugeStep = 0; + + _replicantProbability = 0; + _replicantGauge = 0; + _replicantGaugeTarget = 0; + _replicantGaugeDelta = 0; + _timeNextReplicantGaugeStep = 0; + + _anxiety = 0; + + _blinkState = 0; + _timeNextBlink = 0; + _timeNextGaugesBlink = 0; + + _isClosing = false; + _timeClose = 0; + + _isAdjusting = false; + _adjustment = 154; + _adjustmentTarget = 154; + _adjustmentDelta = 0; + + _eyeLineSelected = 1; + _eyeLineX = 315; + _eyeLineXLast = 315; + _eyeLineY = 281; + _eyeLineYLast = 281; + _eyeLineXDelta = 8; + _eyeLineYDelta = 8; + _timeNextEyeLineStep = 0; + _timeNextEyeLineStart = 0; + + _soundTrackId1 = -1; + _soundTrackId2 = -1; + _soundTrackId3 = -1; + _vqaLoopEnded = false; + + _surfaceEye.free(); +} + +void VK::init() { + _vm->_mouse->disable(); + + _buttons->activate(nullptr, nullptr, mouseDownCallback, mouseUpCallback, this); + _buttons->defineImage(0, Common::Rect(191, 364, 218, 373), nullptr, _shapes[2], _shapes[3], _vm->_textVK->getText(1)); + _buttons->defineImage(1, Common::Rect(154, 258, 161, 265), _shapes[4], _shapes[4], _shapes[5], _vm->_textVK->getText(2)); + _buttons->defineImage(2, Common::Rect(515, 368, 538, 398), nullptr, _shapes[6], _shapes[7], nullptr); + _buttons->defineImage(3, Common::Rect(548, 368, 571, 398), nullptr, _shapes[8], _shapes[9], nullptr); + _buttons->defineImage(4, Common::Rect(581, 368, 604, 398), nullptr, _shapes[10], _shapes[11], nullptr); + _buttons->defineImage(5, Common::Rect( 31, 363, 65, 392), nullptr, _shapes[0], _shapes[1], _vm->_textVK->getText(0)); + _buttons->defineImage(6, Common::Rect( 59, 262, 87, 277), nullptr, nullptr, nullptr, _vm->_textVK->getText(6)); + _buttons->defineImage(7, Common::Rect( 59, 306, 87, 322), nullptr, nullptr, nullptr, _vm->_textVK->getText(7)); + + _script->initialize(_actorId); + + _vqaPlayerMain->setLoop(0, -1, kLoopSetModeJustStart, nullptr, nullptr); + tick(); + _vqaPlayerMain->setLoop(1, -1, kLoopSetModeEnqueue, loopEnded, this); +} + +void VK::draw() { + if (!_isOpen || !_vm->_gameIsRunning) { + return; + } + + int frame = _vqaPlayerMain->update(); + if (frame >= 0) { + _vqaFrameMain = frame; + } + + if (frame == 0) { + _soundTrackId2 = _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(426), 33, 0, 0, 50, 0); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(431), 50, 0, 0, 50, 0); + } else if (frame == 26) { + _vm->_audioPlayer->stop(_soundTrackId2, false); + _soundTrackId1 = _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(429), 50, 30, 30, 50, kAudioPlayerLoop); + } else if (frame == 40) { + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(428), 33, 0, 0, 50, 0); + eyeAnimates(1); + } else if (frame == 59) { + _vm->_mouse->enable(); + _buttons->setImageShapeHovered(2, nullptr); + _buttons->setImageShapeDown(2, nullptr); + _buttons->setImageShapeHovered(3, nullptr); + _buttons->setImageShapeDown(3, nullptr); + _buttons->setImageShapeHovered(4, nullptr); + _buttons->setImageShapeDown(4, nullptr); + } else if (frame == 100) { + if (_vm->_rnd.getRandomNumberRng(0, 100) > 60) { + eyeAnimates(1); + } + } else if (frame == 140) { + if (_vm->_rnd.getRandomNumberRng(0, 10) > 6) { + _soundTrackId3 = _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(459), 83, 0, 0, 50, 0); + } + } + + blit(_vm->_surfaceBack, _vm->_surfaceFront); + + Graphics::Surface &surface = _vm->_surfaceFront; + + int timeNow = _vm->getTotalPlayTime(); + + if (_isAdjusting && !_testStarted && !_vm->isMouseButtonDown()) { + _isAdjusting = false; + } + + if (_vqaFrameMain >= 26) { + if (_isClosing && timeNow >= _timeNextGaugesBlink) { + if (_blinkState) { + _buttons->setImageShapeUp(6, nullptr); + _buttons->setImageShapeUp(7, nullptr); + _blinkState = 0; + } else { + if (_humanProbability >= 80) { + _buttons->setImageShapeUp(6, _shapes[13]); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(0), 100, 0, 0, 50, 0); + } + if (_replicantProbability >= 80) { + _buttons->setImageShapeUp(7, _shapes[14]); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(0), 100, 0, 0, 50, 0); + } + _blinkState = 1; + } + _timeNextGaugesBlink = timeNow + 600; + } + + _buttons->draw(surface); + + if (_humanGaugeDelta != 0 && timeNow >= _timeNextHumanGaugeStep) { + _humanGauge += _humanGaugeDelta; + + if ((_humanGaugeDelta > 0 && _humanGauge >= _humanGaugeTarget) + || (_humanGaugeDelta < 0 && _humanGauge <= _humanGaugeTarget)) { + _humanGauge = _humanGaugeTarget; + } + + if (_humanGauge == _humanGaugeTarget) { + if (_humanGaugeTarget != 0) { + _humanGaugeTarget = 0; + _humanGaugeDelta = -_humanGaugeDelta; + _timeNextHumanGaugeStep = timeNow + 500; + } else { + _humanGaugeDelta = 0; + } + } else { + _timeNextHumanGaugeStep = timeNow + 66; + } + } + drawHumanGauge(surface); + + if (_replicantGaugeDelta != 0 && timeNow >= _timeNextReplicantGaugeStep) { + _replicantGauge += _replicantGaugeDelta; + + if ((_replicantGaugeDelta > 0 && _replicantGauge >= _replicantGaugeTarget) + || (_replicantGaugeDelta < 0 && _replicantGauge <= _replicantGaugeTarget)) { + _replicantGauge = _replicantGaugeTarget; + } + + if (_replicantGauge == _replicantGaugeTarget) { + if (_replicantGaugeTarget != 0) { + _replicantGaugeTarget = 0; + _replicantGaugeDelta = -_replicantGaugeDelta; + _timeNextReplicantGaugeStep = timeNow + 500; + } else { + _replicantGaugeDelta = 0; + } + } else { + _timeNextReplicantGaugeStep = timeNow + 66; + } + } + drawReplicantGauge(surface); + + if (!_calibrationStarted && _vqaFrameMain >= 59 && timeNow >= _timeNextBlink) { + if (_blinkState) { + _buttons->setImageShapeUp(0, nullptr); + _blinkState = false; + } else { + _buttons->setImageShapeUp(0, _shapes[2]); + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(461), 50, 0, 0, 50, 0); + _blinkState = true; + } + _timeNextBlink = timeNow + 600; + } + + if (_adjustmentDelta != 0 && timeNow >= _timeNextAdjustementStep) { + if (_adjustmentDelta > 0) { + _adjustment += 3; + if (_adjustment >= _adjustmentTarget) { + _adjustment = _adjustmentTarget; + _adjustmentDelta = 0; + } + } else { + _adjustment -= 3; + if (_adjustment <= _adjustmentTarget) { + _adjustment = _adjustmentTarget; + _adjustmentDelta = 0; + } + } + setAdjustment(_adjustment + 4); + } + setAdjustmentFromMouse(); + + if (_calibrationStarted && !_testStarted && timeNow >= _timeNextBlink) { + if (_blinkState) { + _buttons->setImageShapeUp(2, nullptr); + _buttons->setImageShapeUp(3, nullptr); + _buttons->setImageShapeUp(4, nullptr); + _blinkState = 0; + } else { + _buttons->setImageShapeUp(2, _shapes[7]); + _buttons->setImageShapeUp(3, _shapes[9]); + _buttons->setImageShapeUp(4, _shapes[11]); + _blinkState = 1; + + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(462), 33, 0, 0, 50, 0); + } + _timeNextBlink = timeNow + 600; + } + + if (_needleValueDelta != 0 && timeNow >= _timeNextNeedleStep) { + if (_needleValueDelta > 0) { + _needleValue += 4; + if (_needleValue >= _needleValueTarget) { + _needleValue = _needleValueTarget; + _needleValueMax = _needleValueTarget; + _needleValueDelta = -_needleValueDelta; + _needleValueTarget = 0; + + _timeNeedleReturn = timeNow + 1800; + + if (!_testStarted) { + animateAdjustment(_needleValueMax + 165); + } + } + } else if (timeNow >= _timeNeedleReturn) { + _needleValue -= 4; + if (_needleValue <= _needleValueTarget) { + _needleValue = _needleValueTarget; + _needleValueDelta = 0; + } + } + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(455), 20, 0, 0, 50, 0); + _timeNextNeedleStep = timeNow + 66; + } + + drawNeedle(surface); + drawEye(surface); + drawEyeCrosshair(surface, timeNow); + if (timeNow >= _timeNextNeedleOscillate) { + _timeNextNeedleOscillate = timeNow + 66; + } + int mouseX, mouseY; + _vm->_mouse->getXY(&mouseX, &mouseY); + _buttons->drawTooltip(surface, mouseX, mouseY); + } + + drawMouse(surface); +} + +void VK::drawNeedle(Graphics::Surface &surface) { + int x = _needleValue + 165; + if ((int)_vm->getTotalPlayTime() >= _timeNextNeedleOscillate && x > 165) { + x = CLIP(x + (int)_vm->_rnd.getRandomNumberRng(0, 4) - 2, 165, 245); + } + + float needleOffset = abs(38.0f - _needleValue); + float y = 345 - sqrt(72.0f * 72.0f - needleOffset * needleOffset); + + float colorIntensity = MIN(78.0f, _needleValue + 39.0f) / 78.0f; + + int r = 6 * colorIntensity; + int g = 8 * colorIntensity; + int b = 12 * colorIntensity; + + surface.drawLine(203, 324, x - 2, y, ((7 - r ) << 10) | ((18 - g ) << 5) | (23 - b )); + surface.drawLine(203, 324, x + 2, y, ((7 - r ) << 10) | ((18 - g ) << 5) | (23 - b )); + surface.drawLine(203, 324, x - 1, y, ((7 - r / 2) << 10) | ((18 - g / 2) << 5) | (23 - b / 2)); + surface.drawLine(203, 324, x + 1, y, ((7 - r / 2) << 10) | ((18 - g / 2) << 5) | (23 - b / 2)); + surface.drawLine(203, 324, x, y - 1, ((7 - r / 2) << 10) | ((18 - g / 2) << 5) | (23 - b / 2)); + surface.drawLine(203, 324, x, y, 0x1E57); +} + +void VK::drawEye(Graphics::Surface &surface) { + _vqaPlayerEye->update(true); + surface.copyRectToSurface(_surfaceEye, 315, 281, Common::Rect(0, 0, _surfaceEye.w, _surfaceEye.h)); +} + +void VK::drawEyeCrosshair(Graphics::Surface &surface, int timeNow) { + surface.drawLine(315, _eyeLineY, 486, _eyeLineY, 0x848u); + surface.drawLine(315, _eyeLineY - 1, 486, _eyeLineY - 1, 0x848u); + surface.drawLine(315, _eyeLineY, _vm->_rnd.getRandomNumberRng(10, 20) + 315, _eyeLineY, 0x84Au); + surface.drawLine(486 - _vm->_rnd.getRandomNumberRng(10, 20), _eyeLineY, 486, _eyeLineY, 0x84Au); + surface.drawLine(486 - _vm->_rnd.getRandomNumberRng(10, 20), _eyeLineY - 1, 486, _eyeLineY - 1, 0x846u); + surface.drawLine(315, _eyeLineY - 1, _vm->_rnd.getRandomNumberRng(10, 20) + 315, _eyeLineY - 1, 0x846u); + + surface.drawLine(_eyeLineX, 281, _eyeLineX, 396, 0x848u); + surface.drawLine(_eyeLineX - 1, 281, _eyeLineX - 1, 396, 0x848u); + surface.drawLine(_eyeLineX, 281, _eyeLineX, _vm->_rnd.getRandomNumberRng(10, 20) + 281, 0x846u); + surface.drawLine(_eyeLineX, 396 - _vm->_rnd.getRandomNumberRng(10, 20), _eyeLineX, 396, 0x846u); + surface.drawLine(_eyeLineX - 1, 396 - _vm->_rnd.getRandomNumberRng(10, 20), _eyeLineX - 1, 396, 0x84Au); + surface.drawLine(_eyeLineX - 1, 281, _eyeLineX - 1, _vm->_rnd.getRandomNumberRng(10, 20) + 281, 0x84Au); + + if (timeNow >= _timeNextEyeLineStart) { + if (_eyeLineSelected) { + if (_eyeLineYLast != _eyeLineY) { + surface.drawLine(315, _eyeLineYLast, 486, _eyeLineYLast, 0x844u); + } + _eyeLineYLast = _eyeLineY; + if (timeNow >= _timeNextEyeLineStep) { + _eyeLineY += _eyeLineYDelta; + if (_eyeLineYDelta > 0) { + if (_eyeLineY >= 396) { + _eyeLineY = 396; + _eyeLineYDelta = -_eyeLineYDelta; + } + } else if (_eyeLineY <= 281) { + _eyeLineY = 281; + _eyeLineYDelta = -_eyeLineYDelta; + _eyeLineSelected = 0; + _timeNextEyeLineStart = timeNow + 1000; + } + _timeNextEyeLineStep = timeNow + 50; + } + } else { + if (_eyeLineXLast != _eyeLineX) { + surface.drawLine(_eyeLineXLast, 281, _eyeLineXLast, 396, 0x844u); + } + _eyeLineXLast = _eyeLineX; + if (timeNow >= _timeNextEyeLineStep) { + _eyeLineX += _eyeLineXDelta; + if ( _eyeLineXDelta > 0) { + if (_eyeLineX >= 486) { + _eyeLineX = 486; + _eyeLineXDelta = -_eyeLineXDelta; + } + } else if (_eyeLineX <= 315) { + _eyeLineX = 315; + _eyeLineXDelta = -_eyeLineXDelta; + _eyeLineSelected = 1; + _timeNextEyeLineStart = timeNow + 1000; + } + _timeNextEyeLineStep = timeNow + 50; + } + } + } +} + +void VK::drawMouse(Graphics::Surface &surface) { + if (_vm->_mouse->isDisabled()) { + return; + } + + Common::Point p = _vm->getMousePos(); + + if (_buttons->hasHoveredImage()) { + _vm->_mouse->setCursor(1); + } else { + _vm->_mouse->setCursor(0); + } + + _vm->_mouse->draw(surface, p.x, p.y); +} + +void VK::drawGauge(Graphics::Surface &surface, int value, int x, int y, int width) { + _shapes[12]->draw(surface, x + (width / 2) * value / 20 , y); +} + +void VK::drawHumanGauge(Graphics::Surface &surface) { + drawGauge(surface, _humanGauge, 72, 271, 87); +} + +void VK::drawReplicantGauge(Graphics::Surface &surface) { + drawGauge(surface, _replicantGauge, 72, 293, 87); +} + +void VK::calibrate() { + if (_calibrationCounter >= 3 || _testStarted) { + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(460), 100, 0, 0, 50, 0); + } else { + _vm->_mouse->disable(); + _script->calibrate(_actorId); + _vm->_mouse->enable(); + ++_calibrationCounter; + if (_calibrationCounter == 3) { + _buttons->setImageShapeHovered(0, nullptr); + _buttons->setImageShapeDown(0, nullptr); + _buttons->setImageTooltip(0, nullptr); + } + } + _calibrationStarted = true; + _buttons->setImageShapeUp(0, nullptr); + + _buttons->setImageShapeHovered(2, _shapes[6]); + _buttons->setImageShapeDown(2, _shapes[7]); + _buttons->setImageTooltip(2, _vm->_textVK->getText(3)); + + _buttons->setImageShapeHovered(3, _shapes[8]); + _buttons->setImageShapeDown(3, _shapes[9]); + _buttons->setImageTooltip(3, _vm->_textVK->getText(4)); + + _buttons->setImageShapeHovered(4, _shapes[10]); + _buttons->setImageShapeDown(4, _shapes[11]); + _buttons->setImageTooltip(4, _vm->_textVK->getText(5)); +} + +void VK::beginTest() { + if (_calibrationStarted && !_testStarted) { + _vm->_mouse->disable(); + _calibration = ((100.0f / (100.0f - 4.0f) * (_adjustment - 154.0f)) - _calibrationRatio) / 5.0f; + _script->beginTest(_actorId); + _testStarted = true; + _buttons->setImageShapeHovered(0, nullptr); + _buttons->setImageShapeDown(0, nullptr); + _buttons->setImageTooltip(0, nullptr); + _buttons->setImageShapeDown(1, _shapes[4]); + _buttons->setImageTooltip(1, nullptr); + _buttons->setImageShapeUp(2, nullptr); + _buttons->setImageShapeUp(3, nullptr); + _buttons->setImageShapeUp(4, nullptr); + _vm->_mouse->enable(); + } else { + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(460), 100, 0, 0, 50, 0); + } +} + +void VK::startAdjustement() { + if (_testStarted) { + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(460), 100, 0, 0, 50, 0); + } else { + _isAdjusting = true; + } +} + +void VK::stopAdjustement() { + if (_testStarted) { + _isAdjusting = false; + } +} + +void VK::animateAdjustment(int target) { + _adjustmentTarget = MAX(target - 4, 154); + _adjustmentDelta = (_adjustmentTarget - _adjustment) / 5; + _timeNextAdjustementStep = _vm->getTotalPlayTime() + 50; +} + +void VK::setAdjustment(int x) { + _adjustment = CLIP(x - 4, 154, 246); + float offset = abs(199.0f - _adjustment); + int y = sqrt(88.0f * 88.0f - offset * offset); + _buttons->setImageLeft(1, _adjustment); + _buttons->setImageTop(1, 345 - y); +} + +void VK::setAdjustmentFromMouse() { + if (_isAdjusting && !_testStarted) { + int mouseX, mouseY; + _vm->_mouse->getXY(&mouseX, &mouseY); + setAdjustment(mouseX); + if (_adjustmentTarget != _adjustment) { + _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(456), 100, 0, 0, 50, 0); + } + _adjustmentTarget = _adjustment; + _adjustmentDelta = 0; + } +} + +void VK::askQuestion(int intensity) { + assert(intensity < (int)_questions.size()); + + if (!_testStarted) { + beginTest(); + } + + if (!_testStarted) { + return; + } + + int foundQuestionIndex = -1; + int foundQuestionIndexLast = -1; + + for (int i = 0; i < (int)_questions[intensity].size(); ++i) { + if (_questions[intensity][i].isPresent && !_questions[intensity][i].wasAsked) { + int relatedQuestion = -1; + if (_questions[intensity][i].relatedSentenceId >= 0) { + // TODO: but not used in game + // relatedQuestion = vk::findQuestionById(this, questions, relatedQuestionId); + } + + if (relatedQuestion < 0 || _questions[intensity][relatedQuestion].wasAsked) { + foundQuestionIndexLast = i; + if (_vm->_rnd.getRandomNumberRng(0, 100) < 20) { + foundQuestionIndex = i; + break; + } + } + } + } + + if (foundQuestionIndex < 0) { + foundQuestionIndex = foundQuestionIndexLast; + } + + if (foundQuestionIndex >= 0) { + _vm->_mouse->disable(); + _questions[intensity][foundQuestionIndex].wasAsked = true; + _script->mcCoyAsksQuestion(_actorId, _questions[intensity][foundQuestionIndex].sentenceId); + _script->questionAsked(_actorId, _questions[intensity][foundQuestionIndex].sentenceId); + _vm->_mouse->enable(); + } else if (!_isClosing && !_script->isInsideScript()) { + _isClosing = true; + _vm->_mouse->disable(); + _timeClose = _vm->getTotalPlayTime() + 3000; + } +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/ui/vk.h b/engines/bladerunner/ui/vk.h new file mode 100644 index 0000000000..77a419afb5 --- /dev/null +++ b/engines/bladerunner/ui/vk.h @@ -0,0 +1,174 @@ +/* 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_VK_H +#define BLADERUNNER_VK_H + +#include "common/array.h" +#include "common/str.h" + +#include "graphics/surface.h" + +namespace BladeRunner { + +class BladeRunnerEngine; +class VKScript; +class Shape; +class UIImagePicker; +class VQAPlayer; + +class VK { + struct Question { + bool isPresent; + bool wasAsked; + int sentenceId; + int relatedSentenceId; + }; + + BladeRunnerEngine *_vm; + + VKScript *_script; + + UIImagePicker *_buttons; + Common::Array _shapes; + + VQAPlayer *_vqaPlayerMain; + VQAPlayer *_vqaPlayerEye; + int _vqaFrameMain; + bool _vqaLoopEnded; + + Graphics::Surface _surfaceEye; + + bool _isOpen; + int _actorId; + bool _calibrationStarted; + bool _testStarted; + + Common::Array > _questions; + + int _volumeAmbient; + int _volumeMusic; + int _soundTrackId1; + int _soundTrackId2; + int _soundTrackId3; + + int _calibration; + int _calibrationRatio; + int _calibrationCounter; + + int _humanProbability; + int _humanGauge; + int _humanGaugeTarget; + int _humanGaugeDelta; + int _timeNextHumanGaugeStep; + + int _replicantProbability; + int _replicantGauge; + int _replicantGaugeTarget; + int _replicantGaugeDelta; + int _timeNextReplicantGaugeStep; + + int _anxiety; + + int _needleValue; + int _needleValueMax; + int _needleValueTarget; + int _needleValueDelta; + int _timeNextNeedleStep; + int _timeNextNeedleOscillate; + int _timeNeedleReturn; + + bool _isClosing; + int _timeClose; + + int _blinkState; + int _timeNextBlink; + int _timeNextGaugesBlink; + + bool _isAdjusting; + int _adjustment; + int _adjustmentTarget; + int _adjustmentDelta; + int _timeNextAdjustementStep; + + int _eyeLineSelected; + int _eyeLineX; + int _eyeLineXLast; + int _eyeLineY; + int _eyeLineYLast; + int _eyeLineXDelta; + int _eyeLineYDelta; + int _timeNextEyeLineStep; + int _timeNextEyeLineStart; + +public: + VK(BladeRunnerEngine *vm); + ~VK(); + + void open(int actorId, int calibrationRatio); + bool isOpen() const; + void close(); + + void tick(); + + void resume(); + + void handleMouseDown(int mouseX, int mouseY, bool mainButton); + void handleMouseUp(int mouseX, int mouseY, bool mainButton); + + void playSpeechLine(int actorId, int sentenceId, float duration); + void addQuestion(int intensity, int sentenceId, int relatedSentenceId); + void subjectReacts(int intensity, int humanResponse, int replicantResponse, int anxiety); + void eyeAnimates(int loopId); + +private: + static void mouseDownCallback(int buttonId, void *callbackData); + static void mouseUpCallback(int buttonId, void *callbackData); + static void loopEnded(void *callbackData, int frame, int loopId); + + void reset(); + void init(); + + void draw(); + + void drawNeedle(Graphics::Surface &surface); + void drawEye(Graphics::Surface &surface); + void drawEyeCrosshair(Graphics::Surface &surface, int timeNow); + void drawMouse(Graphics::Surface &surface); + void drawGauge(Graphics::Surface &surface, int value, int x, int y, int width); + void drawHumanGauge(Graphics::Surface &surface); + void drawReplicantGauge(Graphics::Surface &surface); + + void calibrate(); + void beginTest(); + + void startAdjustement(); + void stopAdjustement(); + void animateAdjustment(int target); + void setAdjustment(int x); + void setAdjustmentFromMouse(); + + void askQuestion(int intensity); +}; + +} // End of namespace BladeRunner +#endif -- cgit v1.2.3