[Scummvm-git-logs] scummvm master -> d49bddfec6f4138aea1e44c8e61160276ebe20fd

madmoose thomas at fach-pedersen.net
Mon Jul 31 00:18:47 CEST 2017


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
d3e64544b3 BLADERUNNER: Move surfaces out of VQA player
42b469d0e8 BLADERUNNER: Add missing scene scripts
d49bddfec6 BLADERUNNER: Add Spinner support


Commit: d3e64544b3065273aa72f2066edf74bc6f71e86a
    https://github.com/scummvm/scummvm/commit/d3e64544b3065273aa72f2066edf74bc6f71e86a
Author: Thomas Fach-Pedersen (thomas at fach-pedersen.net)
Date: 2017-07-31T00:15:33+02:00

Commit Message:
BLADERUNNER: Move surfaces out of VQA player

Rename the surfaces to more meaningful names and move ownership out
of the VQA classes. This means that the VQA classes no longer
create their own internal surfaces but instead draw on externally
provided surfaces.

Changed paths:
    engines/bladerunner/actor.cpp
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/item.cpp
    engines/bladerunner/item_pickup.cpp
    engines/bladerunner/outtake.cpp
    engines/bladerunner/scene.cpp
    engines/bladerunner/scene.h
    engines/bladerunner/vqa_decoder.cpp
    engines/bladerunner/vqa_decoder.h
    engines/bladerunner/vqa_player.cpp
    engines/bladerunner/vqa_player.h


diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index 49a66d9..6265a04 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -672,7 +672,7 @@ bool Actor::draw(Common::Rect *screenRect) {
 
 	// TODO: Handle SHORTY mode
 
-	_vm->_sliceRenderer->drawInWorld(_animationId, _animationFrame, drawPosition, drawAngle, drawScale, _vm->_surface2, _vm->_zbuffer->getData());
+	_vm->_sliceRenderer->drawInWorld(_animationId, _animationFrame, drawPosition, drawAngle, drawScale, _vm->_surfaceGame, _vm->_zbuffer->getData());
 	_vm->_sliceRenderer->getScreenRectangle(screenRect, _animationId, _animationFrame, drawPosition, drawAngle, drawScale);
 
 	return !screenRect->isEmpty();
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 17e117b..83a07bb 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -110,9 +110,6 @@ BladeRunnerEngine::~BladeRunnerEngine() {
 	// delete _audioPlayer;
 	// delete _ambientSounds;
 
-	// _surface1.free();
-	// _surface2.free();
-
 	delete _zbuffer;
 
 	delete _itemPickup;
@@ -154,7 +151,9 @@ Common::Error BladeRunnerEngine::run() {
 bool BladeRunnerEngine::startup(bool hasSavegames) {
 	bool r;
 
-	_surface1.create(640, 480, createRGB555());
+	_surfaceGame.create(640, 480, createRGB555());
+	_surfaceInterface.create(640, 480, createRGB555());
+	_surface4.create(640, 480, createRGB555());
 
 	r = openArchive("STARTUP.MIX");
 	if (!r)
@@ -506,8 +505,9 @@ void BladeRunnerEngine::shutdown() {
 	_gameInfo = nullptr;
 
 	// TODO: Delete graphics surfaces here
-	_surface1.free();
-	_surface2.free();
+	_surface4.free();
+	_surfaceInterface.free();
+	_surfaceGame.free();
 
 	if (isArchiveOpen("STARTUP.MIX"))
 		closeArchive("STARTUP.MIX");
@@ -522,9 +522,9 @@ bool BladeRunnerEngine::loadSplash() {
 	if (!img.open("SPLASH.IMG"))
 		return false;
 
-	img.copyToSurface(&_surface1);
+	img.copyToSurface(&_surfaceGame);
 
-	_system->copyRectToScreen(_surface1.getPixels(), _surface1.pitch, 0, 0, _surface1.w, _surface1.h);
+	_system->copyRectToScreen(_surfaceGame.getPixels(), _surfaceGame.pitch, 0, 0, _surfaceGame.w, _surfaceGame.h);
 	_system->updateScreen();
 
 	return true;
@@ -598,13 +598,13 @@ void BladeRunnerEngine::gameTick() {
 		_ambientSounds->tick();
 
 		bool backgroundChanged = false;
-		int frame = _scene->advanceFrame(_surface1);
+		int frame = _scene->advanceFrame();
 		if (frame >= 0) {
 			_sceneScript->SceneFrameAdvanced(frame);
 			backgroundChanged = true;
 		}
 		(void)backgroundChanged;
-		_surface2.copyFrom(_surface1);
+		_surfaceGame.copyRectToSurface(_surfaceInterface.getPixels(), _surfaceInterface.pitch, 0, 0, 640, 480);
 		// TODO: remove zbuffer draw
 		//_surface2.copyRectToSurface(_zbuffer->getData(), 1280, 0, 0, 640, 480);
 
@@ -638,7 +638,7 @@ void BladeRunnerEngine::gameTick() {
 
 			Common::Point p = _eventMan->getMousePos();
 			_mouse->tick(p.x, p.y);
-			_mouse->draw(_surface2, p.x, p.y);
+			_mouse->draw(_surfaceGame, p.x, p.y);
 
 			// TODO: Process AUD
 			// TODO: Footstep sound
@@ -762,7 +762,7 @@ void BladeRunnerEngine::gameTick() {
 			}
 #endif
 
-			_system->copyRectToScreen((const byte *)_surface2.getBasePtr(0, 0), _surface2.pitch, 0, 0, 640, 480);
+			_system->copyRectToScreen(_surfaceGame.getPixels(), _surfaceGame.pitch, 0, 0, 640, 480);
 			_system->updateScreen();
 			_system->delayMillis(10);
 		}
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 3b0359b..b2e3df0 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -125,8 +125,10 @@ public:
 
 	int in_script_counter;
 
-	Graphics::Surface  _surface1;
-	Graphics::Surface  _surface2;
+	Graphics::Surface  _surfaceGame;
+	Graphics::Surface  _surfaceInterface;
+	Graphics::Surface  _surface4;
+
 	ZBuffer           *_zbuffer;
 
 	Common::RandomSource _rnd;
diff --git a/engines/bladerunner/item.cpp b/engines/bladerunner/item.cpp
index 3ad2868..0b84aae 100644
--- a/engines/bladerunner/item.cpp
+++ b/engines/bladerunner/item.cpp
@@ -86,7 +86,7 @@ bool Item::tick(Common::Rect *screenRect, bool special) {
 
 	Vector3 position(_position.x, -_position.z, _position.y);
 	int animationId = _animationId + (special ? 1 : 0);
-	_vm->_sliceRenderer->drawInWorld(animationId, 0, position, M_PI - _angle, 1.0f, _vm->_surface2, _vm->_zbuffer->getData());
+	_vm->_sliceRenderer->drawInWorld(animationId, 0, position, M_PI - _angle, 1.0f, _vm->_surfaceGame, _vm->_zbuffer->getData());
 	_vm->_sliceRenderer->getScreenRectangle(&_screenRectangle, animationId, 0, position, M_PI - _angle, 1.0f);
 
 	if (!_screenRectangle.isEmpty()) {
diff --git a/engines/bladerunner/item_pickup.cpp b/engines/bladerunner/item_pickup.cpp
index 7549c8d..b1f557d 100644
--- a/engines/bladerunner/item_pickup.cpp
+++ b/engines/bladerunner/item_pickup.cpp
@@ -104,6 +104,6 @@ void ItemPickup::draw() {
 		return;
 	}
 
-	_vm->_sliceRenderer->drawOnScreen(_animationId, _animationFrame, _screenX, _screenY, _facing, _scale, _vm->_surface2, _vm->_zbuffer->getData());
+	_vm->_sliceRenderer->drawOnScreen(_animationId, _animationFrame, _screenX, _screenY, _facing, _scale, _vm->_surfaceGame, _vm->_zbuffer->getData());
 }
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index b81b212..80e4d78 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -43,7 +43,7 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 	else
 		resName = name + "_E.VQA";
 
-	VQAPlayer vqa_player(_vm);
+	VQAPlayer vqa_player(_vm, &_vm->_surfaceGame);
 
 	vqa_player.open(resName);
 
@@ -59,8 +59,7 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 			break;
 
 		if (frame >= 0) {
-			const Graphics::Surface *surface = vqa_player.getSurface();
-			_vm->_system->copyRectToScreen((const byte *)surface->getBasePtr(0, 0), surface->pitch, 0, 0, 640, 480);
+			_vm->_system->copyRectToScreen(_vm->_surfaceGame.getPixels(), _vm->_surfaceGame.pitch, 0, 0, 640, 480);
 			_vm->_system->updateScreen();
 		}
 
diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp
index be5be7c..4af2c15 100644
--- a/engines/bladerunner/scene.cpp
+++ b/engines/bladerunner/scene.cpp
@@ -75,7 +75,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) {
 	if (_vqaPlayer != nullptr)
 		delete _vqaPlayer;
 
-	_vqaPlayer = new VQAPlayer(_vm);
+	_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceInterface);
 
 	Common::String sceneName = _vm->_gameInfo->getSceneName(sceneId);
 	if (!_vm->_sceneScript->Open(sceneName))
@@ -105,7 +105,7 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) {
 		_defaultLoopSet = true;
 		_specialLoopAtEnd = false;
 	}
-	_vm->_scene->advanceFrame(_vm->_surface1);
+	_vm->_scene->advanceFrame();
 
 	_vm->_playerActor->setAtXYZ(_actorStartPosition, _actorStartFacing);
 	_vm->_playerActor->setSetId(setId);
@@ -168,10 +168,10 @@ bool Scene::close(bool isLoadingGame) {
 	return result;
 }
 
-int Scene::advanceFrame(Graphics::Surface &surface) {
+int Scene::advanceFrame() {
 	int frame = _vqaPlayer->update();
 	if (frame >= 0) {
-		surface.copyFrom(*_vqaPlayer->getSurface());
+		_vm->_surfaceGame.copyRectToSurface(_vm->_surfaceInterface.getPixels(), _vm->_surfaceInterface.pitch, 0, 0, 640, 480);
 		_vqaPlayer->updateZBuffer(_vm->_zbuffer);
 		_vqaPlayer->updateView(_vm->_view);
 		_vqaPlayer->updateLights(_vm->_lights);
diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h
index 90cb936..c90258c 100644
--- a/engines/bladerunner/scene.h
+++ b/engines/bladerunner/scene.h
@@ -89,7 +89,7 @@ public:
 
 	bool open(int setId, int sceneId, bool isLoadingGame);
 	bool close(bool isLoadingGame);
-	int  advanceFrame(Graphics::Surface &surface);
+	int  advanceFrame();
 	void setActorStart(Vector3 position, int facing);
 
 	void loopSetDefault(int a);
diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp
index 01ccfd5..c43e4c3 100644
--- a/engines/bladerunner/vqa_decoder.cpp
+++ b/engines/bladerunner/vqa_decoder.cpp
@@ -115,7 +115,9 @@ const char *strTag(uint32 tag) {
 	return s;
 }
 
-VQADecoder::VQADecoder() : _s(nullptr),
+VQADecoder::VQADecoder(Graphics::Surface *surface) :
+	  _s(nullptr),
+	  _surface(surface),
 	  _frameInfo(nullptr),
 	  _videoTrack(nullptr),
 	  _audioTrack(nullptr),
@@ -173,7 +175,7 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
 		}
 	} while (chd.id != kFINF);
 
-	_videoTrack = new VQAVideoTrack(this);
+	_videoTrack = new VQAVideoTrack(this, _surface);
 	_audioTrack = new VQAAudioTrack(this);
 
 #if 0
@@ -188,8 +190,8 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
 	return true;
 }
 
-const Graphics::Surface *VQADecoder::decodeVideoFrame() {
-	return _videoTrack->decodeVideoFrame();
+void VQADecoder::decodeVideoFrame() {
+	_videoTrack->decodeVideoFrame();
 }
 
 void VQADecoder::decodeZBuffer(ZBuffer *zbuffer) {
@@ -533,10 +535,10 @@ bool VQADecoder::readMFCI(Common::SeekableReadStream *s, uint32 size) {
 	return true;
 }
 
-VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder) {
+VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface) {
 	VQADecoder::Header *header = &vqaDecoder->_header;
 
-	_surface = nullptr;
+	_surface = surface;
 	_hasNewFrame = false;
 
 	_numFrames = header->numFrames;
@@ -565,9 +567,6 @@ VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder) {
 
 	_zbufChunk = new uint8[roundup(_maxZBUFChunkSize)];
 
-	_surface = new Graphics::Surface();
-	_surface->create(_width, _height, createRGB555());
-
 	_viewData = nullptr;
 	_lightsData = nullptr;
 }
@@ -578,10 +577,6 @@ VQADecoder::VQAVideoTrack::~VQAVideoTrack() {
 	delete[] _zbufChunk;
 	delete[] _vpointer;
 
-	if (_surface)
-		_surface->free();
-	delete _surface;
-
 	if (_viewData)
 		delete[] _viewData;
 	if (_lightsData)
@@ -596,10 +591,6 @@ uint16 VQADecoder::VQAVideoTrack::getHeight() const {
 	return _height;
 }
 
-Graphics::PixelFormat VQADecoder::VQAVideoTrack::getPixelFormat() const {
-	return _surface->format;
-}
-
 int VQADecoder::VQAVideoTrack::getCurFrame() const {
 	return _curFrame;
 }
@@ -612,13 +603,12 @@ Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const {
 	return _frameRate;
 }
 
-const Graphics::Surface *VQADecoder::VQAVideoTrack::decodeVideoFrame() {
+void VQADecoder::VQAVideoTrack::decodeVideoFrame() {
 	if (_hasNewFrame) {
 		decodeFrame((uint16*)_surface->getPixels());
 		_curFrame++;
 		_hasNewFrame = false;
 	}
-	return _surface;
 }
 
 bool VQADecoder::VQAVideoTrack::readVQFL(Common::SeekableReadStream *s, uint32 size) {
diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h
index a808543..d7086d1 100644
--- a/engines/bladerunner/vqa_decoder.h
+++ b/engines/bladerunner/vqa_decoder.h
@@ -44,14 +44,14 @@ class ZBuffer;
 
 class VQADecoder {
 public:
-	VQADecoder();
+	VQADecoder(Graphics::Surface *surface);
 	~VQADecoder();
 
 	bool loadStream(Common::SeekableReadStream *s);
 
 	void readFrame(int frame, int skipFlags);
 
-	const Graphics::Surface    *decodeVideoFrame();
+	void                        decodeVideoFrame();
 	void                        decodeZBuffer(ZBuffer *zbuffer);
 	Audio::SeekableAudioStream *decodeAudioFrame();
 	void                        decodeView(View *view);
@@ -126,6 +126,7 @@ private:
 	class VQAAudioTrack;
 
 	Common::SeekableReadStream *_s;
+	Graphics::Surface *_surface;
 
 	Header   _header;
 	LoopInfo _loopInfo;
@@ -153,15 +154,14 @@ private:
 
 	class VQAVideoTrack {
 	public:
-		VQAVideoTrack(VQADecoder *vqaDecoder);
+		VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface);
 		~VQAVideoTrack();
 
 		uint16 getWidth() const;
 		uint16 getHeight() const;
-		Graphics::PixelFormat getPixelFormat() const;
 		int getCurFrame() const;
 		int getFrameCount() const;
-		const Graphics::Surface *decodeVideoFrame();
+		void decodeVideoFrame();
 		void decodeZBuffer(ZBuffer *zbuffer);
 		void decodeView(View *view);
 		void decodeLights(Lights *lights);
diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp
index 845e2ee..129bb77 100644
--- a/engines/bladerunner/vqa_player.cpp
+++ b/engines/bladerunner/vqa_player.cpp
@@ -99,7 +99,7 @@ int VQAPlayer::update() {
 				_callbackLoopEnded(_callbackData, 0, _loop);
 			}
 		}
-		_surface = nullptr;
+
 		return -1;
 	}
 
@@ -114,7 +114,7 @@ int VQAPlayer::update() {
 	if (now >= _frameNextTime) {
 		int frame = _frameNext;
 		_decoder.readFrame(_frameNext, 0x2);
-		_surface = _decoder.decodeVideoFrame();
+		_decoder.decodeVideoFrame();
 
 		if (_hasAudio) {
 			if (!_audioStarted) {
@@ -142,14 +142,9 @@ int VQAPlayer::update() {
 		return frame;
 	}
 
-	_surface = nullptr;
 	return -1;
 }
 
-const Graphics::Surface *VQAPlayer::getSurface() const {
-	return _surface;
-}
-
 void VQAPlayer::updateZBuffer(ZBuffer *zbuffer) {
 	_decoder.decodeZBuffer(zbuffer);
 }
diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h
index 8c2653e..b60f648 100644
--- a/engines/bladerunner/vqa_player.h
+++ b/engines/bladerunner/vqa_player.h
@@ -48,8 +48,8 @@ class ZBuffer;
 class VQAPlayer {
 	BladeRunnerEngine           *_vm;
 	Common::SeekableReadStream  *_s;
-	VQADecoder                   _decoder;
 	const Graphics::Surface     *_surface;
+	VQADecoder                   _decoder;
 	const uint16                *_zBuffer;
 	Audio::QueuingAudioStream   *_audioStream;
 
@@ -75,10 +75,11 @@ class VQAPlayer {
 
 public:
 
-	VQAPlayer(BladeRunnerEngine *vm)
+	VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface)
 		: _vm(vm),
 		  _s(nullptr),
-		  _surface(nullptr),
+		  _surface(surface),
+		  _decoder(surface),
 		  _zBuffer(nullptr),
 		  _audioStream(nullptr),
 		  _frameNext(-1),
@@ -104,7 +105,6 @@ public:
 	void close();
 
 	int  update();
-	const Graphics::Surface *getSurface() const;
 	void updateZBuffer(ZBuffer *zbuffer);
 	void updateView(View *view);
 	void updateLights(Lights *lights);


Commit: 42b469d0e802b81f1c4f94d11313ae8214d91c8e
    https://github.com/scummvm/scummvm/commit/42b469d0e802b81f1c4f94d11313ae8214d91c8e
Author: Thomas Fach-Pedersen (thomas at fach-pedersen.net)
Date: 2017-07-31T00:15:33+02:00

Commit Message:
BLADERUNNER: Add missing scene scripts

Add the remaining scene scripts to SceneScript::open and module.mk

Changed paths:
    engines/bladerunner/module.mk
    engines/bladerunner/script/scene.cpp


diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index 82a7c21..3569efa 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -93,6 +93,7 @@ MODULE_OBJS = \
 	script/scene/hf04.o \
 	script/scene/hf05.o \
 	script/scene/hf06.o \
+	script/scene/hf07.o \
 	script/scene/kp01.o \
 	script/scene/kp02.o \
 	script/scene/kp03.o \
diff --git a/engines/bladerunner/script/scene.cpp b/engines/bladerunner/script/scene.cpp
index 1ca6408..646fa48 100644
--- a/engines/bladerunner/script/scene.cpp
+++ b/engines/bladerunner/script/scene.cpp
@@ -27,11 +27,120 @@ namespace BladeRunner {
 bool SceneScript::Open(const Common::String &name) {
 	delete _currentScript;
 
+	if (name == "AR01") { _currentScript = new SceneScriptAR01(_vm); return true; }
+	if (name == "AR02") { _currentScript = new SceneScriptAR02(_vm); return true; }
+	if (name == "BB01") { _currentScript = new SceneScriptBB01(_vm); return true; }
+	if (name == "BB02") { _currentScript = new SceneScriptBB02(_vm); return true; }
+	if (name == "BB03") { _currentScript = new SceneScriptBB03(_vm); return true; }
+	if (name == "BB04") { _currentScript = new SceneScriptBB04(_vm); return true; }
+	if (name == "BB05") { _currentScript = new SceneScriptBB05(_vm); return true; }
+	if (name == "BB06") { _currentScript = new SceneScriptBB06(_vm); return true; }
+	if (name == "BB07") { _currentScript = new SceneScriptBB07(_vm); return true; }
+	if (name == "BB08") { _currentScript = new SceneScriptBB08(_vm); return true; }
+	if (name == "BB09") { _currentScript = new SceneScriptBB09(_vm); return true; }
+	if (name == "BB10") { _currentScript = new SceneScriptBB10(_vm); return true; }
+	if (name == "BB11") { _currentScript = new SceneScriptBB11(_vm); return true; }
+	if (name == "BB12") { _currentScript = new SceneScriptBB12(_vm); return true; }
+	if (name == "BB51") { _currentScript = new SceneScriptBB51(_vm); return true; }
+	if (name == "CT01") { _currentScript = new SceneScriptCT01(_vm); return true; }
+	if (name == "CT02") { _currentScript = new SceneScriptCT02(_vm); return true; }
+	if (name == "CT03") { _currentScript = new SceneScriptCT03(_vm); return true; }
+	if (name == "CT04") { _currentScript = new SceneScriptCT04(_vm); return true; }
+	if (name == "CT05") { _currentScript = new SceneScriptCT05(_vm); return true; }
+	if (name == "CT06") { _currentScript = new SceneScriptCT06(_vm); return true; }
+	if (name == "CT07") { _currentScript = new SceneScriptCT07(_vm); return true; }
+	if (name == "CT08") { _currentScript = new SceneScriptCT08(_vm); return true; }
+	if (name == "CT09") { _currentScript = new SceneScriptCT09(_vm); return true; }
+	if (name == "CT10") { _currentScript = new SceneScriptCT10(_vm); return true; }
+	if (name == "CT11") { _currentScript = new SceneScriptCT11(_vm); return true; }
+	if (name == "CT12") { _currentScript = new SceneScriptCT12(_vm); return true; }
+	if (name == "CT51") { _currentScript = new SceneScriptCT51(_vm); return true; }
+	if (name == "DR01") { _currentScript = new SceneScriptDR01(_vm); return true; }
+	if (name == "DR02") { _currentScript = new SceneScriptDR02(_vm); return true; }
+	if (name == "DR03") { _currentScript = new SceneScriptDR03(_vm); return true; }
+	if (name == "DR04") { _currentScript = new SceneScriptDR04(_vm); return true; }
+	if (name == "DR05") { _currentScript = new SceneScriptDR05(_vm); return true; }
+	if (name == "DR06") { _currentScript = new SceneScriptDR06(_vm); return true; }
+	if (name == "HC01") { _currentScript = new SceneScriptHC01(_vm); return true; }
+	if (name == "HC02") { _currentScript = new SceneScriptHC02(_vm); return true; }
+	if (name == "HC03") { _currentScript = new SceneScriptHC03(_vm); return true; }
+	if (name == "HC04") { _currentScript = new SceneScriptHC04(_vm); return true; }
+	if (name == "HF01") { _currentScript = new SceneScriptHF01(_vm); return true; }
+	if (name == "HF02") { _currentScript = new SceneScriptHF02(_vm); return true; }
+	if (name == "HF03") { _currentScript = new SceneScriptHF03(_vm); return true; }
+	if (name == "HF04") { _currentScript = new SceneScriptHF04(_vm); return true; }
+	if (name == "HF05") { _currentScript = new SceneScriptHF05(_vm); return true; }
+	if (name == "HF06") { _currentScript = new SceneScriptHF06(_vm); return true; }
+	if (name == "HF07") { _currentScript = new SceneScriptHF07(_vm); return true; }
+	if (name == "KP01") { _currentScript = new SceneScriptKP01(_vm); return true; }
+	if (name == "KP02") { _currentScript = new SceneScriptKP02(_vm); return true; }
+	if (name == "KP03") { _currentScript = new SceneScriptKP03(_vm); return true; }
+	if (name == "KP04") { _currentScript = new SceneScriptKP04(_vm); return true; }
+	if (name == "KP05") { _currentScript = new SceneScriptKP05(_vm); return true; }
+	if (name == "KP06") { _currentScript = new SceneScriptKP06(_vm); return true; }
+	if (name == "KP07") { _currentScript = new SceneScriptKP07(_vm); return true; }
+	if (name == "MA01") { _currentScript = new SceneScriptMA01(_vm); return true; }
+	if (name == "MA02") { _currentScript = new SceneScriptMA02(_vm); return true; }
+	if (name == "MA04") { _currentScript = new SceneScriptMA04(_vm); return true; }
+	if (name == "MA05") { _currentScript = new SceneScriptMA05(_vm); return true; }
+	if (name == "MA06") { _currentScript = new SceneScriptMA06(_vm); return true; }
+	if (name == "MA07") { _currentScript = new SceneScriptMA07(_vm); return true; }
+	if (name == "MA08") { _currentScript = new SceneScriptMA08(_vm); return true; }
+	if (name == "NR01") { _currentScript = new SceneScriptNR01(_vm); return true; }
+	if (name == "NR02") { _currentScript = new SceneScriptNR02(_vm); return true; }
+	if (name == "NR03") { _currentScript = new SceneScriptNR03(_vm); return true; }
+	if (name == "NR04") { _currentScript = new SceneScriptNR04(_vm); return true; }
+	if (name == "NR05") { _currentScript = new SceneScriptNR05(_vm); return true; }
+	if (name == "NR06") { _currentScript = new SceneScriptNR06(_vm); return true; }
+	if (name == "NR07") { _currentScript = new SceneScriptNR07(_vm); return true; }
+	if (name == "NR08") { _currentScript = new SceneScriptNR08(_vm); return true; }
+	if (name == "NR09") { _currentScript = new SceneScriptNR09(_vm); return true; }
+	if (name == "NR10") { _currentScript = new SceneScriptNR10(_vm); return true; }
+	if (name == "NR11") { _currentScript = new SceneScriptNR11(_vm); return true; }
+	if (name == "PS01") { _currentScript = new SceneScriptPS01(_vm); return true; }
+	if (name == "PS02") { _currentScript = new SceneScriptPS02(_vm); return true; }
+	if (name == "PS03") { _currentScript = new SceneScriptPS03(_vm); return true; }
+	if (name == "PS04") { _currentScript = new SceneScriptPS04(_vm); return true; }
+	if (name == "PS05") { _currentScript = new SceneScriptPS05(_vm); return true; }
+	if (name == "PS06") { _currentScript = new SceneScriptPS06(_vm); return true; }
+	if (name == "PS07") { _currentScript = new SceneScriptPS07(_vm); return true; }
+	if (name == "PS09") { _currentScript = new SceneScriptPS09(_vm); return true; }
+	if (name == "PS10") { _currentScript = new SceneScriptPS10(_vm); return true; }
+	if (name == "PS11") { _currentScript = new SceneScriptPS11(_vm); return true; }
+	if (name == "PS12") { _currentScript = new SceneScriptPS12(_vm); return true; }
+	if (name == "PS13") { _currentScript = new SceneScriptPS13(_vm); return true; }
+	if (name == "PS14") { _currentScript = new SceneScriptPS14(_vm); return true; }
+	if (name == "PS15") { _currentScript = new SceneScriptPS15(_vm); return true; }
 	if (name == "RC01") { _currentScript = new SceneScriptRC01(_vm); return true; }
 	if (name == "RC02") { _currentScript = new SceneScriptRC02(_vm); return true; }
 	if (name == "RC03") { _currentScript = new SceneScriptRC03(_vm); return true; }
 	if (name == "RC04") { _currentScript = new SceneScriptRC04(_vm); return true; }
 	if (name == "RC51") { _currentScript = new SceneScriptRC51(_vm); return true; }
+	if (name == "TB02") { _currentScript = new SceneScriptTB02(_vm); return true; }
+	if (name == "TB03") { _currentScript = new SceneScriptTB03(_vm); return true; }
+	if (name == "TB05") { _currentScript = new SceneScriptTB05(_vm); return true; }
+	if (name == "TB06") { _currentScript = new SceneScriptTB06(_vm); return true; }
+	if (name == "TB07") { _currentScript = new SceneScriptTB07(_vm); return true; }
+	if (name == "UG01") { _currentScript = new SceneScriptUG01(_vm); return true; }
+	if (name == "UG02") { _currentScript = new SceneScriptUG02(_vm); return true; }
+	if (name == "UG03") { _currentScript = new SceneScriptUG03(_vm); return true; }
+	if (name == "UG04") { _currentScript = new SceneScriptUG04(_vm); return true; }
+	if (name == "UG05") { _currentScript = new SceneScriptUG05(_vm); return true; }
+	if (name == "UG06") { _currentScript = new SceneScriptUG06(_vm); return true; }
+	if (name == "UG07") { _currentScript = new SceneScriptUG07(_vm); return true; }
+	if (name == "UG08") { _currentScript = new SceneScriptUG08(_vm); return true; }
+	if (name == "UG09") { _currentScript = new SceneScriptUG09(_vm); return true; }
+	if (name == "UG10") { _currentScript = new SceneScriptUG10(_vm); return true; }
+	if (name == "UG12") { _currentScript = new SceneScriptUG12(_vm); return true; }
+	if (name == "UG13") { _currentScript = new SceneScriptUG13(_vm); return true; }
+	if (name == "UG14") { _currentScript = new SceneScriptUG14(_vm); return true; }
+	if (name == "UG15") { _currentScript = new SceneScriptUG15(_vm); return true; }
+	if (name == "UG16") { _currentScript = new SceneScriptUG16(_vm); return true; }
+	if (name == "UG17") { _currentScript = new SceneScriptUG17(_vm); return true; }
+	if (name == "UG18") { _currentScript = new SceneScriptUG18(_vm); return true; }
+	if (name == "UG19") { _currentScript = new SceneScriptUG19(_vm); return true; }
+
+	error("SceneScript::Open: Unknown script %s", name.c_str());
 
 	return false;
 }


Commit: d49bddfec6f4138aea1e44c8e61160276ebe20fd
    https://github.com/scummvm/scummvm/commit/d49bddfec6f4138aea1e44c8e61160276ebe20fd
Author: Thomas Fach-Pedersen (thomas at fach-pedersen.net)
Date: 2017-07-31T00:15:33+02:00

Commit Message:
BLADERUNNER: Add Spinner support

Changed paths:
  A engines/bladerunner/spinner.cpp
  A engines/bladerunner/spinner.h
  A engines/bladerunner/ui_image_picker.cpp
  A engines/bladerunner/ui_image_picker.h
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/module.mk
    engines/bladerunner/scene.cpp
    engines/bladerunner/script/script.cpp


diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 83a07bb..348d5f7 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 b2e3df0..8fd43df 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 3569efa..9a43ef4 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 4af2c15..1e5297a 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 9fcfb25..ba78a42 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 0000000..6c30a62
--- /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 0000000..19021fa
--- /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 0000000..6541297
--- /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 0000000..c55aa48
--- /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





More information about the Scummvm-git-logs mailing list