aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/bladerunner/bladerunner.cpp35
-rw-r--r--engines/bladerunner/bladerunner.h6
-rw-r--r--engines/bladerunner/module.mk2
-rw-r--r--engines/bladerunner/scene.cpp4
-rw-r--r--engines/bladerunner/script/script.cpp12
-rw-r--r--engines/bladerunner/spinner.cpp291
-rw-r--r--engines/bladerunner/spinner.h77
-rw-r--r--engines/bladerunner/ui_image_picker.cpp265
-rw-r--r--engines/bladerunner/ui_image_picker.h92
9 files changed, 768 insertions, 16 deletions
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 83a07bbd69..348d5f7c1f 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -49,6 +49,7 @@
#include "bladerunner/shape.h"
#include "bladerunner/slice_animations.h"
#include "bladerunner/slice_renderer.h"
+#include "bladerunner/spinner.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/vqa_decoder.h"
#include "bladerunner/waypoints.h"
@@ -286,6 +287,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
// TODO: KIA
// TODO: Spinner Interface
+ _spinner = new Spinner(this);
// TODO: Elevators
@@ -534,6 +536,10 @@ bool BladeRunnerEngine::init2() {
return true;
}
+Common::Point BladeRunnerEngine::getMousePos() {
+ return _eventMan->getMousePos();
+}
+
void BladeRunnerEngine::gameLoop() {
_gameIsRunning = true;
do {
@@ -581,7 +587,13 @@ void BladeRunnerEngine::gameTick() {
// TODO: Autosave
// TODO: Kia
- // TODO: Spinner
+
+ if (_spinner->isOpen()) {
+ _spinner->tick();
+ _ambientSounds->tick();
+ return;
+ }
+
// TODO: Esper
// TODO: VK
// TODO: Elevators
@@ -636,7 +648,7 @@ void BladeRunnerEngine::gameTick() {
// TODO: Draw dialogue menu
- Common::Point p = _eventMan->getMousePos();
+ Common::Point p = getMousePos();
_mouse->tick(p.x, p.y);
_mouse->draw(_surfaceGame, p.x, p.y);
@@ -797,17 +809,32 @@ void BladeRunnerEngine::handleEvents() {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_RBUTTONDOWN:
- handleMouseClick(event.mouse.x, event.mouse.y);
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP: {
+ bool buttonLeft = event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP;
+ bool buttonDown = event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN;
+
+ handleMouseAction(event.mouse.x, event.mouse.y, buttonLeft, buttonDown);
+ }
default:
;
}
}
}
-void BladeRunnerEngine::handleMouseClick(int x, int y) {
+void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool buttonDown) {
if (!playerHasControl() || _mouse->isDisabled())
return;
+ if (_spinner->isOpen()) {
+ if (buttonDown) {
+ _spinner->handleMouseDown(x, y);
+ } else {
+ _spinner->handleMouseUp(x, y);
+ }
+ return;
+ }
+
Vector3 mousePosition = _mouse->getXYZ(x, y);
int isClickable;
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index b2e3df08ea..8fd43df211 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -69,6 +69,7 @@ class Settings;
class Shape;
class SliceAnimations;
class SliceRenderer;
+class Spinner;
class TextResource;
class View;
class Waypoints;
@@ -105,6 +106,7 @@ public:
Settings *_settings;
SliceAnimations *_sliceAnimations;
SliceRenderer *_sliceRenderer;
+ Spinner *_spinner;
SuspectsDatabase *_suspectsDatabase;
View *_view;
Waypoints *_waypoints;
@@ -164,11 +166,13 @@ public:
bool loadSplash();
bool init2();
+ Common::Point getMousePos();
+
void gameLoop();
void gameTick();
void actorsUpdate();
void handleEvents();
- void handleMouseClick(int x, int y);
+ void handleMouseAction(int x, int y, bool buttonLeft, bool buttonDown);
void handleMouseClickExit(int x, int y, int exitIndex);
void handleMouseClickRegion(int x, int y, int regionIndex);
void handleMouseClickItem(int x, int y, int itemId);
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index 3569efab12..9a43ef4457 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -167,8 +167,10 @@ MODULE_OBJS = \
shape.o \
slice_animations.o \
slice_renderer.o \
+ spinner.o \
suspects_database.o \
text_resource.o \
+ ui_image_picker.o \
view.o \
vqa_decoder.o \
vqa_player.o \
diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp
index 4af2c15d08..1e5297a4bf 100644
--- a/engines/bladerunner/scene.cpp
+++ b/engines/bladerunner/scene.cpp
@@ -33,6 +33,7 @@
#include "bladerunner/scene_objects.h"
#include "bladerunner/script/scene.h"
#include "bladerunner/slice_renderer.h"
+#include "bladerunner/spinner.h"
#include "common/str.h"
@@ -306,8 +307,7 @@ void Scene::loopEnded(int frame, int loopId) {
_playerWalkedIn = true;
}
if (_specialLoopMode == 3) {
- //TODO:
- //spinner::open(Spinner);
+ _vm->_spinner->setIsOpen();
}
_specialLoopMode = -1;
_specialLoop = -1;
diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp
index 9fcfb252c6..ba78a42b4c 100644
--- a/engines/bladerunner/script/script.cpp
+++ b/engines/bladerunner/script/script.cpp
@@ -43,6 +43,7 @@
#include "bladerunner/scene_objects.h"
#include "bladerunner/slice_animations.h"
#include "bladerunner/slice_renderer.h"
+#include "bladerunner/spinner.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/vector.h"
#include "bladerunner/waypoints.h"
@@ -1071,22 +1072,15 @@ bool ScriptBase::SDB_Add_Other_Clue(int suspectId, int clueId) {
}
void ScriptBase::Spinner_Set_Selectable_Destination_Flag(int a1, int a2) {
- //TODO
- warning("Spinner_Set_Selectable_Destination_Flag(%d, %d)", a1, a2);
+ _vm->_spinner->setSelectableDestinationFlag(a1, a2);
}
// ScriptBase::Spinner_Query_Selectable_Destination_Flag
int ScriptBase::Spinner_Interface_Choose_Dest(int a1, int a2) {
- //TODO
- warning("Spinner_Interface_Choose_Dest(%d, %d)", a1, a2);
- return -1;
+ return _vm->_spinner->interfaceChooseDest(a1, a2);
}
-// ScriptBase::Spinner_Set_Selectable_Destination_Flag
-// ScriptBase::Spinner_Query_Selectable_Destination_Flag
-// ScriptBase::Spinner_Interface_Choose_Dest
-
void ScriptBase::ESPER_Flag_To_Activate() {
//TODO
warning("ESPER_Flag_To_Activate()");
diff --git a/engines/bladerunner/spinner.cpp b/engines/bladerunner/spinner.cpp
new file mode 100644
index 0000000000..6c30a62bac
--- /dev/null
+++ b/engines/bladerunner/spinner.cpp
@@ -0,0 +1,291 @@
+/* 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/spinner.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "bladerunner/scene.h"
+#include "bladerunner/shape.h"
+#include "bladerunner/mouse.h"
+#include "bladerunner/vqa_player.h"
+#include "bladerunner/text_resource.h"
+#include "bladerunner/ui_image_picker.h"
+
+#include "common/rect.h"
+#include "common/system.h"
+
+namespace BladeRunner {
+
+Spinner::Spinner(BladeRunnerEngine *vm) : _vm(vm) {
+ reset();
+ _imagePicker = new UIImagePicker(vm, SPINNER_DESTINATIONS);
+}
+
+Spinner::~Spinner() {
+ delete _imagePicker;
+ reset();
+}
+
+void Spinner::setSelectableDestinationFlag(int destination, bool selectable) {
+ _isDestinationSelectable[destination] = selectable;
+}
+
+bool Spinner::querySelectableDestinationFlag(int destination) const {
+ return _isDestinationSelectable[destination];
+}
+
+SpinnerDestination SpinnerDestinationsNear[] = {
+ { 0, 0x0D2, 0x107, 0x107, 0x14C },
+ { 1, 0x133, 0x14A, 0x169, 0x17D },
+ { 2, 0x152, 0x089, 0x16A, 0x0A9 },
+ { 3, 0x0F8, 0x087, 0x121, 0x0A8 },
+ { 4, 0x160, 0x0DE, 0x17B, 0x0EE },
+ { -1, -1, -1, -1, -1 }
+};
+
+SpinnerDestination SpinnerDestinationsMedium[] = {
+ { 0, 0x0FC, 0x0F2, 0x117, 0x11B },
+ { 1, 0x12D, 0x111, 0x148, 0x130 },
+ { 2, 0x13F, 0x0B6, 0x150, 0x0C8 },
+ { 3, 0x10D, 0x0B5, 0x125, 0x0C8 },
+ { 4, 0x145, 0x0E3, 0x159, 0x0F0 },
+ { 5, 0x103, 0x04A, 0x17C, 0x077 },
+ { 6, 0x0CB, 0x07C, 0x0E0, 0x088 },
+ { 7, 0x0C8, 0x093, 0x0DE, 0x0AA },
+ { -1, -1, -1, -1, -1 }
+};
+
+SpinnerDestination SpinnerDestinationsFar[] = {
+ { 0, 0x0DC, 0x0E3, 0x0F6, 0x106 },
+ { 1, 0x104, 0x0FC, 0x11E, 0x117 },
+ { 2, 0x11E, 0x0B2, 0x12E, 0x0C4 },
+ { 3, 0x0F4, 0x0B2, 0x107, 0x0C3 },
+ { 4, 0x120, 0x0D8, 0x132, 0x0E4 },
+ { 5, 0x0F9, 0x04D, 0x161, 0x07C },
+ { 6, 0x0BE, 0x07F, 0x0D0, 0x08A },
+ { 7, 0x0B9, 0x095, 0x0CE, 0x0AA },
+ { 8, 0x18E, 0x0F9, 0x1A3, 0x10C },
+ { 9, 0x186, 0x0DA, 0x1A3, 0x0EC },
+ { -1, -1, -1, -1, -1 }
+};
+
+static void spinner_mouseInCallback(int, void*);
+static void spinner_mouseOutCallback(int, void*);
+static void spinner_mouseDownCallback(int, void*);
+static void spinner_mouseUpCallback(int, void*);
+
+int Spinner::interfaceChooseDest(int loopId, int loopFlag) {
+ _selectedDestination = 0;
+ if (!_vm->openArchive("MODE.MIX"))
+ return 0;
+
+ if (loopId < 0) {
+ _isOpen = true;
+ } else {
+ _vm->playerLosesControl();
+ _vm->_scene->loopStartSpecial(3, loopId, loopFlag);
+ while (!_isOpen) {
+ _vm->gameTick();
+ }
+ _vm->playerGainsControl();
+ }
+
+ _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceInterface);
+ if (!_vqaPlayer->open("SPINNER.VQA")) {
+ return 0;
+ }
+
+ _vm->_mouse->setCursor(0);
+
+ // Determine which map we need to show to include the active destinations
+ uint8 mapmask = 0;
+ uint8 mapmaskv[SPINNER_DESTINATIONS] = { 1, 1, 1, 1, 1, 3, 3, 3, 7, 7 };
+ for (int i = 0; i != SPINNER_DESTINATIONS; ++i) {
+ if (_isDestinationSelectable[i])
+ mapmask |= mapmaskv[i];
+ }
+
+ _destinations = nullptr;
+ int firstShapeId = 0;
+ int shapeCount = 0;
+ int spinnerLoopId = 4;
+
+ mapmask = 1;
+
+ if (mapmask & 4) {
+ _destinations = SpinnerDestinationsFar;
+ firstShapeId = 26;
+ shapeCount = 20;
+ spinnerLoopId = 4;
+ } else if (mapmask & 2) {
+ _destinations = SpinnerDestinationsMedium;
+ firstShapeId = 10;
+ shapeCount = 16;
+ spinnerLoopId = 2;
+ } else if (mapmask & 1) {
+ _destinations = SpinnerDestinationsNear;
+ firstShapeId = 0;
+ shapeCount = 10;
+ spinnerLoopId = 0;
+ } else {
+ return -1;
+ }
+
+ _vqaPlayer->setLoop(spinnerLoopId, -1, 2, nullptr, nullptr);
+ _vqaPlayer->setLoop(spinnerLoopId + 1, -1, 0, nullptr, nullptr);
+
+ for (int j = 0; j != shapeCount; ++j) {
+ _shapes.push_back(new Shape(_vm));
+ _shapes[j]->readFromContainer("SPINNER.SHP", firstShapeId + j);
+ }
+
+ _imagePicker->resetImages();
+
+ for (SpinnerDestination *dest = _destinations; dest->id != -1; ++dest) {
+ if (!_isDestinationSelectable[dest->id])
+ continue;
+
+ const char *tooltip = _vm->_textSpinnerDestinations->getText(dest->id);
+
+ _imagePicker->defineImage(
+ dest->id,
+ dest->left,
+ dest->top,
+ dest->right,
+ dest->bottom,
+ _shapes[dest->id],
+ _shapes[dest->id + _shapes.size() / 2],
+ _shapes[dest->id + _shapes.size() / 2],
+ tooltip
+ );
+ }
+
+ _imagePicker->setCallbacks(
+ spinner_mouseInCallback,
+ spinner_mouseOutCallback,
+ spinner_mouseDownCallback,
+ spinner_mouseUpCallback,
+ this
+ );
+
+ // TODO: Freeze game time
+ _selectedDestination = -1;
+ do {
+ _vm->gameTick();
+ } while (_selectedDestination == -1);
+
+ // TODO: Unfreeze game time
+ _isOpen = false;
+ // TODO: _vm->_scene->resume();
+
+ for (int i = 0; i != (int)_shapes.size(); ++i)
+ delete _shapes[i];
+ _shapes.clear();
+
+ return _selectedDestination;
+}
+
+static void spinner_mouseInCallback(int, void*) {
+}
+
+static void spinner_mouseOutCallback(int, void*) {
+}
+
+static void spinner_mouseDownCallback(int, void*) {
+}
+
+static void spinner_mouseUpCallback(int image, void *data) {
+ if (image >= 0 && image < 10) {
+ Spinner *spinner = (Spinner *)data;
+ spinner->setSelectedDestination(image);
+ }
+}
+
+void Spinner::setIsOpen() {
+ _isOpen = true;
+}
+
+bool Spinner::isOpen() const {
+ return _isOpen;
+}
+
+int Spinner::handleMouseUp(int x, int y) {
+ _imagePicker->handleMouseAction(x, y, false, true, 0);
+ return false;
+}
+
+int Spinner::handleMouseDown(int x, int y) {
+ _imagePicker->handleMouseAction(x, y, true, false, 0);
+ return false;
+}
+
+void Spinner::tick() {
+ if (!_vm->_gameIsRunning)
+ return;
+
+ int frame = _vqaPlayer->update();
+ assert(frame >= -1);
+
+ // vqaPlayer renders to _surfaceInterface
+ _vm->_surfaceGame.copyRectToSurface(_vm->_surfaceInterface, 0, 0, Common::Rect(640, 480));
+
+ _imagePicker->draw(_vm->_surfaceInterface);
+
+ Common::Point p = _vm->getMousePos();
+ _imagePicker->handleMouseAction(p.x, p.y, false, false, false);
+ if (_imagePicker->hasHoveredImage()) {
+ _vm->_mouse->setCursor(1);
+ } else {
+ _vm->_mouse->setCursor(0);
+ }
+ _vm->_mouse->draw(_vm->_surfaceGame, p.x, p.y);
+
+ _vm->_system->copyRectToScreen(_vm->_surfaceGame.getPixels(), _vm->_surfaceGame.pitch, 0, 0, 640, 480);
+ _vm->_system->updateScreen();
+ _vm->_system->delayMillis(10);
+}
+
+void Spinner::setSelectedDestination(int destination) {
+ _selectedDestination = destination;
+}
+
+void Spinner::reset() {
+ for (int i = 0; i != SPINNER_DESTINATIONS; ++i) {
+ _isDestinationSelectable[i] = 0;
+ }
+
+ _isOpen = false;
+ _destinations = nullptr;
+ _selectedDestination = -1;
+ _imagePicker = nullptr;
+
+ for (int i = 0; i != (int)_shapes.size(); ++i)
+ delete _shapes[i];
+ _shapes.clear();
+}
+
+void Spinner::resume() {
+ // TODO
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/spinner.h b/engines/bladerunner/spinner.h
new file mode 100644
index 0000000000..19021fac1a
--- /dev/null
+++ b/engines/bladerunner/spinner.h
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BLADERUNNER_SPINNER_H
+#define BLADERUNNER_SPINNER_H
+
+#include "common/array.h"
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class Shape;
+class VQAPlayer;
+class UIImagePicker;
+
+#define SPINNER_DESTINATIONS 10
+
+struct SpinnerDestination {
+ int id;
+ int left;
+ int top;
+ int right;
+ int bottom;
+};
+
+class Spinner {
+ BladeRunnerEngine *_vm;
+ bool _isDestinationSelectable[SPINNER_DESTINATIONS];
+ bool _isOpen;
+ VQAPlayer *_vqaPlayer;
+ SpinnerDestination *_destinations;
+ int _selectedDestination;
+ Common::Array<Shape*> _shapes;
+ UIImagePicker *_imagePicker;
+
+public:
+ Spinner(BladeRunnerEngine *vm);
+ ~Spinner();
+
+ void setSelectableDestinationFlag(int destination, bool selectable);
+ bool querySelectableDestinationFlag(int destination) const;
+
+ int interfaceChooseDest(int vqaLoopId, int loopFlag);
+
+ void setIsOpen();
+ bool isOpen() const;
+
+ int handleMouseUp(int x, int y);
+ int handleMouseDown(int x, int y);
+ void tick();
+ void setSelectedDestination(int destination);
+ void reset();
+ void resume();
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/ui_image_picker.cpp b/engines/bladerunner/ui_image_picker.cpp
new file mode 100644
index 0000000000..654129754c
--- /dev/null
+++ b/engines/bladerunner/ui_image_picker.cpp
@@ -0,0 +1,265 @@
+/* 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_image_picker.h"
+
+#include "bladerunner/bladerunner.h"
+
+#include "bladerunner/shape.h"
+
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+struct UIImagePickerImage {
+ int active;
+ Common::Rect rect;
+ Shape *shapeUp;
+ Shape *shapeHovered;
+ Shape *shapeDown;
+ const char *tooltip;
+};
+
+UIImagePicker::UIImagePicker(BladeRunnerEngine *vm, int imageCount) : _vm(vm) {
+ reset();
+ _images = new UIImagePickerImage[imageCount];
+ _imageCount = imageCount;
+ resetImages();
+}
+
+UIImagePicker::~UIImagePicker() {
+ delete[] _images;
+ _images = nullptr;
+ reset();
+}
+
+void UIImagePicker::resetImages() {
+ for (int i = 0; i != _imageCount; i++) {
+ resetImage(i);
+ }
+}
+
+bool UIImagePicker::defineImage(int i, int left, int top, int right, int bottom, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip) {
+ if (i < 0 || i >= _imageCount || _images[i].active)
+ return false;
+
+ UIImagePickerImage &img = _images[i];
+
+ img.rect.left = left;
+ img.rect.top = top;
+ img.rect.right = right + 1;
+ img.rect.bottom = bottom + 1;
+ img.shapeUp = shapeUp;
+ img.shapeHovered = shapeHovered;
+ img.shapeDown = shapeDown;
+ img.active = true;
+ img.tooltip = tooltip;
+
+ return true;
+}
+
+bool UIImagePicker::setImageTop(int i, int top) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ UIImagePickerImage &img = _images[i];
+
+ img.rect.moveTo(img.rect.left, top);
+
+ return true;
+}
+
+bool UIImagePicker::setImageLeft(int i, int left) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ UIImagePickerImage &img = _images[i];
+
+ img.rect.moveTo(left, img.rect.top);
+
+ return true;
+}
+
+bool UIImagePicker::setImageShapeUp(int i, Shape *shapeUp) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ _images[i].shapeUp = shapeUp;
+
+ return true;
+}
+
+bool UIImagePicker::setImageShapeHovered(int i, Shape *shapeHovered) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ _images[i].shapeHovered = shapeHovered;
+
+ return true;
+}
+
+bool UIImagePicker::setImageShapeDown(int i, Shape *shapeDown) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ _images[i].shapeDown = shapeDown;
+
+ return true;
+}
+
+bool UIImagePicker::setImageTooltip(int i, const char *tooltip) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ _images[i].tooltip = tooltip;
+
+ return true;
+}
+
+bool UIImagePicker::resetActiveImage(int i) {
+ if (i < 0 || i >= _imageCount || !_images[i].active)
+ return false;
+
+ resetImage(i);
+ return true;
+}
+
+void UIImagePicker::setCallbacks(UIImagePickerCallback *mouseInCallback,
+ UIImagePickerCallback *mouseOutCallback,
+ UIImagePickerCallback *mouseDownCallback,
+ UIImagePickerCallback *mouseUpCallback,
+ void *callbackData)
+{
+ _isButtonDown = false;
+ _mouseInCallback = mouseInCallback;
+ _mouseOutCallback = mouseOutCallback;
+ _mouseDownCallback = mouseDownCallback;
+ _mouseUpCallback = mouseUpCallback;
+ _callbackData = callbackData;
+ _hoverStartTimestamp = 0;
+ _isVisible = true;
+ _hoveredImageIndex = -1;
+ _pressedImageIndex = -1;
+}
+
+void UIImagePicker::resetCallbacks() {}
+
+// TODO
+void UIImagePicker::drawTooltip() {}
+
+void UIImagePicker::draw(Graphics::Surface &surface) {
+ if (!_isVisible)
+ return;
+
+ for (int i = 0; i != _imageCount; ++i) {
+ UIImagePickerImage &img = _images[i];
+ if (!img.active)
+ 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);
+ }
+ } else if (i == _hoveredImageIndex && !_isButtonDown) {
+ if (img.shapeHovered) {
+ img.shapeHovered->draw(surface, img.rect.left, img.rect.top);
+ }
+ } else {
+ if (img.shapeUp) {
+ img.shapeUp->draw(surface, img.rect.left, img.rect.top);
+ }
+ }
+ }
+}
+
+void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) {
+ if (!_isVisible || ignore) {
+ return;
+ }
+
+ int hoveredImageIndex = -1;
+ for (int i = 0; i != _imageCount; ++i) {
+ if (_images[i].rect.contains(x, y)) {
+ hoveredImageIndex = i;
+ break;
+ }
+ }
+
+ // If mouse moved to a new image
+ if (hoveredImageIndex != _hoveredImageIndex) {
+ if (!_isButtonDown) {
+ if (hoveredImageIndex == -1) {
+ if (_mouseOutCallback)
+ _mouseOutCallback(hoveredImageIndex, _callbackData);
+ } else {
+ if (_mouseInCallback)
+ _mouseInCallback(hoveredImageIndex, _callbackData);
+ }
+ }
+ _hoveredImageIndex = hoveredImageIndex;
+ }
+
+ // If mouse button changed to pressed
+ if (down && !_isButtonDown) {
+ _isButtonDown = true;
+ _pressedImageIndex = _hoveredImageIndex;
+ if (_mouseDownCallback)
+ _mouseDownCallback(_hoveredImageIndex, _callbackData);
+ }
+
+ // If mouse button changed to released
+ if (up) {
+ if (_isButtonDown) {
+ if (_hoveredImageIndex == _pressedImageIndex && _pressedImageIndex != -1) {
+ if (_mouseUpCallback)
+ _mouseUpCallback(_hoveredImageIndex, _callbackData);
+ }
+ }
+ _isButtonDown = false;
+ _pressedImageIndex = -1;
+ }
+}
+
+void UIImagePicker::resetImage(int i) {
+ assert(i >= 0 && i < _imageCount);
+ UIImagePickerImage &img = _images[i];
+
+ img.active = false;
+ img.rect.left = -1;
+ img.rect.top = -1;
+ img.rect.right = -1;
+ img.rect.bottom = -1;
+ img.shapeUp = nullptr;
+ img.shapeHovered = nullptr;
+ img.shapeDown = nullptr;
+ img.tooltip = nullptr;
+}
+
+bool UIImagePicker::hasHoveredImage() {
+ return _hoveredImageIndex >= 0;
+}
+
+void UIImagePicker::reset() {}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/ui_image_picker.h b/engines/bladerunner/ui_image_picker.h
new file mode 100644
index 0000000000..c55aa48a64
--- /dev/null
+++ b/engines/bladerunner/ui_image_picker.h
@@ -0,0 +1,92 @@
+/* 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_UI_IMAGE_PICKER_H
+#define BLADERUNNER_UI_IMAGE_PICKER_H
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class Shape;
+struct UIImagePickerImage;
+
+typedef void UIImagePickerCallback(int, void*);
+
+class UIImagePicker {
+ BladeRunnerEngine *_vm;
+
+ int _isVisible;
+ int _imageCount;
+ int _hoveredImageIndex;
+ int _pressedImageIndex;
+ int _hoverStartTimestamp;
+ int _isButtonDown;
+ UIImagePickerImage *_images;
+
+ UIImagePickerCallback *_mouseInCallback;
+ UIImagePickerCallback *_mouseOutCallback;
+ UIImagePickerCallback *_mouseDownCallback;
+ UIImagePickerCallback *_mouseUpCallback;
+ void *_callbackData;
+
+public:
+ UIImagePicker(BladeRunnerEngine *vm, int imageCount);
+ ~UIImagePicker();
+
+ void resetImages();
+ bool defineImage(int i, int left, int top, int right, int bottom, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip);
+
+ bool setImageTop(int i, int top);
+ bool setImageLeft(int i, int left);
+ bool setImageShapeUp(int i, Shape *shapeUp);
+ bool setImageShapeHovered(int i, Shape *shapeHovered);
+ bool setImageShapeDown(int i, Shape *shapeDown);
+ bool setImageTooltip(int i, const char *tooltip);
+
+ bool resetActiveImage(int i);
+
+ void setCallbacks(UIImagePickerCallback *mouseInCallback,
+ UIImagePickerCallback *mouseOutCallback,
+ UIImagePickerCallback *mouseDownCallback,
+ UIImagePickerCallback *mouseUpCallback,
+ void *callbackData);
+
+ void resetCallbacks();
+
+ void drawTooltip();
+ void draw(Graphics::Surface &surface);
+
+ void handleMouseAction(int x, int y, bool down, bool up, bool ignore = false);
+
+ void resetImage(int i);
+ bool hasHoveredImage();
+
+ void reset();
+};
+
+} // End of namespace BladeRunner
+
+#endif