aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Kohaut2018-01-31 00:37:19 +0100
committerPeter Kohaut2018-02-04 17:34:43 +0100
commit6e9a340640686fe7dd95efbea34cbf3b7f4209af (patch)
treefe0adec0b85f3e2b387097c4378ce8337b731460
parent0300979bdd8d2ca451110d77cb8e05c84365bbec (diff)
downloadscummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.tar.gz
scummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.tar.bz2
scummvm-rg350-6e9a340640686fe7dd95efbea34cbf3b7f4209af.zip
BLADERUNNER: ESPER interface
-rw-r--r--engines/bladerunner/actor_clues.cpp1
-rw-r--r--engines/bladerunner/actor_walk.cpp1
-rw-r--r--engines/bladerunner/archive.cpp2
-rw-r--r--engines/bladerunner/bladerunner.cpp122
-rw-r--r--engines/bladerunner/bladerunner.h23
-rw-r--r--engines/bladerunner/combat.cpp1
-rw-r--r--engines/bladerunner/dialogue_menu.cpp2
-rw-r--r--engines/bladerunner/game_constants.h22
-rw-r--r--engines/bladerunner/game_flags.cpp2
-rw-r--r--engines/bladerunner/image.cpp1
-rw-r--r--engines/bladerunner/light.h2
-rw-r--r--engines/bladerunner/lights.h2
-rw-r--r--engines/bladerunner/module.mk1
-rw-r--r--engines/bladerunner/scene_objects.h2
-rw-r--r--engines/bladerunner/script/esper.cpp21
-rw-r--r--engines/bladerunner/script/esper.h9
-rw-r--r--engines/bladerunner/script/scene/rc01.cpp5
-rw-r--r--engines/bladerunner/script/script.cpp19
-rw-r--r--engines/bladerunner/script/script.h2
-rw-r--r--engines/bladerunner/set.h3
-rw-r--r--engines/bladerunner/shape.cpp12
-rw-r--r--engines/bladerunner/shape.h2
-rw-r--r--engines/bladerunner/text_resource.cpp2
-rw-r--r--engines/bladerunner/ui/elevator.cpp2
-rw-r--r--engines/bladerunner/ui/esper.cpp1791
-rw-r--r--engines/bladerunner/ui/esper.h285
-rw-r--r--engines/bladerunner/ui/kia.cpp188
-rw-r--r--engines/bladerunner/ui/kia.h35
-rw-r--r--engines/bladerunner/ui/kia_section_crimes.cpp2
-rw-r--r--engines/bladerunner/ui/kia_section_suspects.cpp2
-rw-r--r--engines/bladerunner/ui/kia_shapes.cpp2
-rw-r--r--engines/bladerunner/ui/spinner.cpp3
-rw-r--r--engines/bladerunner/ui/ui_image_picker.cpp17
-rw-r--r--engines/bladerunner/ui/ui_image_picker.h2
-rw-r--r--engines/bladerunner/vqa_decoder.cpp41
-rw-r--r--engines/bladerunner/vqa_decoder.h16
-rw-r--r--engines/bladerunner/vqa_player.cpp16
-rw-r--r--engines/bladerunner/vqa_player.h8
-rw-r--r--engines/bladerunner/waypoints.h2
39 files changed, 2447 insertions, 224 deletions
diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp
index 39fbc77d4e..e1841fe817 100644
--- a/engines/bladerunner/actor_clues.cpp
+++ b/engines/bladerunner/actor_clues.cpp
@@ -23,6 +23,7 @@
#include "bladerunner/actor_clues.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/game_constants.h"
#include "bladerunner/game_info.h"
#include "bladerunner/crimes_database.h"
diff --git a/engines/bladerunner/actor_walk.cpp b/engines/bladerunner/actor_walk.cpp
index 70624221f5..fccc8e22b3 100644
--- a/engines/bladerunner/actor_walk.cpp
+++ b/engines/bladerunner/actor_walk.cpp
@@ -25,6 +25,7 @@
#include "bladerunner/bladerunner.h"
#include "bladerunner/actor.h"
+#include "bladerunner/game_constants.h"
#include "bladerunner/game_info.h"
#include "bladerunner/obstacles.h"
#include "bladerunner/scene.h"
diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp
index 8088e1ac67..6468acf56e 100644
--- a/engines/bladerunner/archive.cpp
+++ b/engines/bladerunner/archive.cpp
@@ -22,6 +22,8 @@
#include "bladerunner/archive.h"
+#include "bladerunner/game_constants.h"
+
#include "common/debug.h"
namespace BladeRunner {
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 266a8f9c6b..efc8a4f7e7 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -61,6 +61,7 @@
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/elevator.h"
+#include "bladerunner/ui/esper.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/spinner.h"
#include "bladerunner/vqa_decoder.h"
@@ -219,8 +220,6 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
_actorDialogueQueue = new ActorDialogueQueue(this);
- // TODO: esper script
-
_settings = new Settings(this);
_itemPickup = new ItemPickup(this);
@@ -392,11 +391,11 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
for (int i = 0; i != 43; ++i) {
Shape *shape = new Shape(this);
- shape->readFromContainer("SHAPES.SHP", i);
+ shape->open("SHAPES.SHP", i);
_shapes.push_back(shape);
}
- // TODO: Esper
+ _esper = new ESPER(this);
// TODO: VK
@@ -467,7 +466,8 @@ void BladeRunnerEngine::shutdown() {
// TODO: Shutdown VK
- // TODO: Shutdown Esper
+ delete _esper;
+ _esper = nullptr;
delete _mouse;
_mouse = nullptr;
@@ -736,7 +736,7 @@ void BladeRunnerEngine::gameTick() {
//probably not needed, this version of tick is just loading data from buffer
//_audioMixer->tick();
- if (_kia->_currentSectionId) {
+ if (_kia->isOpen()) {
_kia->tick();
return;
}
@@ -747,7 +747,11 @@ void BladeRunnerEngine::gameTick() {
return;
}
- // TODO: Esper
+ if (_esper->isOpen()) {
+ _esper->tick();
+ return;
+ }
+
// TODO: VK
if (_elevator->isOpen()) {
@@ -1031,32 +1035,101 @@ void BladeRunnerEngine::handleKeyUp(Common::Event &event) {
_speechSkipped = true;
}
- // TODO(peterkohaut):
+ // TODO:
if (!playerHasControl() /*|| ActorInWalkingLoop*/) {
return;
}
- if (_kia->_currentSectionId) {
+ if (_kia->isOpen()) {
_kia->handleKeyUp(event.kbd);
return;
}
- if (event.kbd.keycode == Common::KEYCODE_TAB) {
- _kia->openLastOpened();
- } else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
- _kia->openOptions();
- } else if (event.kbd.keycode == Common::KEYCODE_SPACE) {
- // TODO(peterkohaut):
- // combat::switchCombatMode(&Combat);
+ if (_spinner->isOpen()) {
+ return;
+ }
+
+ if (_elevator->isOpen()) {
+ return;
+ }
+
+ if (_esper->isOpen()) {
+ return;
+ }
+
+ if (_dialogueMenu->isOpen()) {
+ return;
+ }
+
+ //TODO: scores
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_TAB:
+ _kia->openLastOpened();
+ break;
+ case Common::KEYCODE_ESCAPE:
+ _kia->open(kKIASectionSettings);
+ break;
+ case Common::KEYCODE_SPACE:
+ // TODO: combat::switchCombatMode(&Combat);
+ break;
+ default:
+ break;
}
}
void BladeRunnerEngine::handleKeyDown(Common::Event &event) {
- // if ( PlayerHasControl <= 0 && ActorWalkingLoop != 1 && PlayingSpeechLine != 1 && VqaIsPlaying != 1 ) {
- if (_kia->_currentSectionId) {
+ //TODO:
+ if (!playerHasControl() /* || ActorWalkingLoop || PlayingSpeechLine || VqaIsPlaying */) {
+ return;
+ }
+
+ if (_kia->isOpen()) {
_kia->handleKeyDown(event.kbd);
}
- // }
+
+ if (_spinner->isOpen()) {
+ return;
+ }
+
+ if (_elevator->isOpen()) {
+ return;
+ }
+
+ if (_esper->isOpen()) {
+ return;
+ }
+
+ if (_dialogueMenu->isOpen()) {
+ return;
+ }
+
+ //TODO: scores
+
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_F1:
+ _kia->open(kKIASectionHelp);
+ break;
+ case Common::KEYCODE_F2:
+ _kia->open(kKIASectionSave);
+ break;
+ case Common::KEYCODE_F3:
+ _kia->open(kKIASectionLoad);
+ break;
+ case Common::KEYCODE_F4:
+ _kia->open(kKIASectionCrimes);
+ break;
+ case Common::KEYCODE_F5:
+ _kia->open(kKIASectionSuspects);
+ break;
+ case Common::KEYCODE_F6:
+ _kia->open(kKIASectionClues);
+ break;
+ case Common::KEYCODE_F10:
+ _kia->open(kKIASectionQuit);
+ break;
+ default:
+ break;
+ }
}
void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool buttonDown) {
@@ -1064,7 +1137,7 @@ void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool bu
return;
}
- if (_kia->_currentSectionId) {
+ if (_kia->isOpen()) {
if (buttonDown) {
_kia->handleMouseDown(x, y, buttonLeft);
} else {
@@ -1082,6 +1155,15 @@ void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool bu
return;
}
+ if (_esper->isOpen()) {
+ if (buttonDown) {
+ _esper->handleMouseDown(x, y, buttonLeft);
+ } else {
+ _esper->handleMouseUp(x, y, buttonLeft);
+ }
+ return;
+ }
+
if (_elevator->isOpen()) {
if (buttonDown) {
_elevator->handleMouseDown(x, y);
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 593f414bc9..ba47f2c15e 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -33,11 +33,6 @@
#include "graphics/surface.h"
-// remove these when game is playable
-#define BLADERUNNER_DEBUG_RENDERING 0
-#define BLADERUNNER_DEBUG_CONSOLE 0
-#define BLADERUNNER_DEBUG_GAME 0
-
namespace Common {
struct Event;
}
@@ -46,22 +41,6 @@ struct ADGameDescription;
namespace BladeRunner {
-enum AnimationModes {
- kAnimationModeIdle = 0,
- kAnimationModeWalk = 1,
- kAnimationModeRun = 2,
- kAnimationModeCombatIdle = 4,
- kAnimationModeCombatWalk = 7,
- kAnimationModeCombatRun = 8
-};
-
-enum SceneLoopMode {
- kSceneLoopModeLoseControl = 0,
- kSceneLoopModeChangeSet = 1,
- kSceneLoopMode2 = 2,
- kSceneLoopModeSpinner = 3
-};
-
class Actor;
class ActorDialogueQueue;
class ScreenEffects;
@@ -75,6 +54,7 @@ class CrimesDatabase;
class Combat;
class DialogueMenu;
class Elevator;
+class ESPER;
class Font;
class GameFlags;
class GameInfo;
@@ -125,6 +105,7 @@ public:
Combat *_combat;
DialogueMenu *_dialogueMenu;
Elevator *_elevator;
+ ESPER *_esper;
GameFlags *_gameFlags;
GameInfo *_gameInfo;
ItemPickup *_itemPickup;
diff --git a/engines/bladerunner/combat.cpp b/engines/bladerunner/combat.cpp
index 79491e702a..26b8b5d785 100644
--- a/engines/bladerunner/combat.cpp
+++ b/engines/bladerunner/combat.cpp
@@ -25,6 +25,7 @@
#include "bladerunner/actor.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/game_constants.h"
#include "bladerunner/settings.h"
namespace BladeRunner {
diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp
index d23d8e0d25..9f43f5542a 100644
--- a/engines/bladerunner/dialogue_menu.cpp
+++ b/engines/bladerunner/dialogue_menu.cpp
@@ -42,7 +42,7 @@ DialogueMenu::DialogueMenu(BladeRunnerEngine *vm) {
_shapes.reserve(8);
for (int i = 0; i != 8; ++i) {
_shapes.push_back(Shape(_vm));
- bool r = _shapes[i].readFromContainer("DIALOG.SHP", i);
+ bool r = _shapes[i].open("DIALOG.SHP", i);
assert(r);
(void)r;
}
diff --git a/engines/bladerunner/game_constants.h b/engines/bladerunner/game_constants.h
index 9915bc9e1d..bf7e10113e 100644
--- a/engines/bladerunner/game_constants.h
+++ b/engines/bladerunner/game_constants.h
@@ -23,6 +23,11 @@
#ifndef BLADERUNNER_GAME_CONSTANTS_H
#define BLADERUNNER_GAME_CONSTANTS_H
+//TODO: remove these when game is playable
+#define BLADERUNNER_DEBUG_RENDERING 0
+#define BLADERUNNER_DEBUG_CONSOLE 0
+#define BLADERUNNER_DEBUG_GAME 0
+
namespace BladeRunner {
enum Actors {
@@ -471,6 +476,23 @@ enum Outtakes {
kOuttakeBladeRunner = 41
};
+enum AnimationModes {
+ kAnimationModeIdle = 0,
+ kAnimationModeWalk = 1,
+ kAnimationModeRun = 2,
+ kAnimationModeCombatIdle = 4,
+ kAnimationModeCombatWalk = 7,
+ kAnimationModeCombatRun = 8
+};
+
+enum SceneLoopMode {
+ kSceneLoopModeLoseControl = 0,
+ kSceneLoopModeChangeSet = 1,
+ kSceneLoopMode2 = 2,
+ kSceneLoopModeSpinner = 3
+};
+
+
} // End of namespace BladeRunner
#endif
diff --git a/engines/bladerunner/game_flags.cpp b/engines/bladerunner/game_flags.cpp
index 81fe6a0a4f..f6ae7b198a 100644
--- a/engines/bladerunner/game_flags.cpp
+++ b/engines/bladerunner/game_flags.cpp
@@ -22,6 +22,8 @@
#include "bladerunner/game_flags.h"
+#include "bladerunner/game_constants.h"
+
#include "common/debug.h"
namespace BladeRunner {
diff --git a/engines/bladerunner/image.cpp b/engines/bladerunner/image.cpp
index cc11e5b956..925b0269e3 100644
--- a/engines/bladerunner/image.cpp
+++ b/engines/bladerunner/image.cpp
@@ -23,7 +23,6 @@
#include "bladerunner/image.h"
#include "bladerunner/bladerunner.h"
-
#include "bladerunner/decompress_lcw.h"
#include "common/rect.h"
diff --git a/engines/bladerunner/light.h b/engines/bladerunner/light.h
index 8ad86368ea..03be06499f 100644
--- a/engines/bladerunner/light.h
+++ b/engines/bladerunner/light.h
@@ -37,9 +37,7 @@ namespace BladeRunner {
class Lights;
class Light {
-#if BLADERUNNER_DEBUG_RENDERING
friend class BladeRunnerEngine;
-#endif
friend class Lights;
friend class SliceRenderer;
diff --git a/engines/bladerunner/lights.h b/engines/bladerunner/lights.h
index 904a96f425..ee89fbb5be 100644
--- a/engines/bladerunner/lights.h
+++ b/engines/bladerunner/lights.h
@@ -32,9 +32,7 @@
namespace BladeRunner {
class Lights {
-#if BLADERUNNER_DEBUG_RENDERING
friend class BladeRunnerEngine;
-#endif
friend class SliceRendererLights;
BladeRunnerEngine *_vm;
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index 51251eea02..3b461d5a64 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -176,6 +176,7 @@ MODULE_OBJS = \
suspects_database.o \
text_resource.o \
ui/elevator.o \
+ ui/esper.o \
ui/kia.o \
ui/kia_log.o \
ui/kia_section_base.o \
diff --git a/engines/bladerunner/scene_objects.h b/engines/bladerunner/scene_objects.h
index b873980d05..f6aa528958 100644
--- a/engines/bladerunner/scene_objects.h
+++ b/engines/bladerunner/scene_objects.h
@@ -46,9 +46,7 @@ enum SceneObjectOffset {
};
class SceneObjects {
-#if BLADERUNNER_DEBUG_RENDERING
friend class BladeRunnerEngine;
-#endif
static const int kSceneObjectCount = 115;
struct SceneObject {
diff --git a/engines/bladerunner/script/esper.cpp b/engines/bladerunner/script/esper.cpp
index ca779d6e3d..e6c0390cbb 100644
--- a/engines/bladerunner/script/esper.cpp
+++ b/engines/bladerunner/script/esper.cpp
@@ -23,9 +23,28 @@
#include "bladerunner/script/esper.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/mouse.h"
namespace BladeRunner {
+void ESPERScript::initialize() {
+ _vm->_mouse->disable();
+ SCRIPT_ESPER_DLL_Initialize();
+ _vm->_mouse->enable();
+}
+
+void ESPERScript::photoSelected(int photoId) {
+ _vm->_mouse->disable();
+ SCRIPT_ESPER_DLL_Photo_Selected(photoId);
+ _vm->_mouse->enable();
+}
+
+void ESPERScript::specialRegionSelected(int photoId, int regionId) {
+ _vm->_mouse->disable();
+ SCRIPT_ESPER_DLL_Special_Region_Selected(photoId, regionId);
+ _vm->_mouse->enable();
+}
+
void ESPERScript::SCRIPT_ESPER_DLL_Initialize() {
int v0 = 0;
if (Actor_Clue_Query(kActorMcCoy, kClueRuncitersVideo)) {
@@ -39,7 +58,7 @@ void ESPERScript::SCRIPT_ESPER_DLL_Initialize() {
if (!Actor_Clue_Query(kActorMcCoy, kClueRuncitersViewB)) {
Actor_Clue_Acquire(kActorMcCoy, kClueRuncitersViewB, 1, kActorRunciter);
}
- ESPER_Add_Photo("RC02_FA.IMG", 1, 1);
+ ESPER_Add_Photo("RC02_RA.IMG", 1, 1);
}
if (Actor_Clue_Query(kActorMcCoy, kClueEarlyQsClub)) {
if (!Actor_Clue_Query(kActorMcCoy, kClueOuterDressingRoom)) {
diff --git a/engines/bladerunner/script/esper.h b/engines/bladerunner/script/esper.h
index 683ecf6998..63e30e55e0 100644
--- a/engines/bladerunner/script/esper.h
+++ b/engines/bladerunner/script/esper.h
@@ -31,10 +31,13 @@ class BladeRunnerEngine;
class ESPERScript : ScriptBase {
public:
- ESPERScript(BladeRunnerEngine *vm)
- : ScriptBase(vm) {
- }
+ ESPERScript(BladeRunnerEngine *vm) : ScriptBase(vm) {}
+ void initialize();
+ void photoSelected(int photoId);
+ void specialRegionSelected(int photoId, int regionId);
+
+private:
void SCRIPT_ESPER_DLL_Initialize();
void SCRIPT_ESPER_DLL_Photo_Selected(int photo);
bool SCRIPT_ESPER_DLL_Special_Region_Selected(int photo, int region);
diff --git a/engines/bladerunner/script/scene/rc01.cpp b/engines/bladerunner/script/scene/rc01.cpp
index dd793ad440..347f0e8dcb 100644
--- a/engines/bladerunner/script/scene/rc01.cpp
+++ b/engines/bladerunner/script/scene/rc01.cpp
@@ -30,8 +30,9 @@ void SceneScriptRC01::InitializeScene() {
Game_Flag_Set(kFlagIntroPlayed); // force skip intro
Game_Flag_Set(kFlagRC02toRC01); // no landing
// Game_Flag_Set(kFlagRC01PoliceDone);
- // Game_Flag_Set(249);
- Game_Flag_Set(kFlagKIAPrivacyAddon);
+ // Game_Flag_Set(kFlagKIAPrivacyAddon);
+
+ // ESPER_Flag_To_Activate();
#endif
if (!Game_Flag_Query(kFlagIntroPlayed)) {
diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp
index 91a755bb41..3c0c8e1866 100644
--- a/engines/bladerunner/script/script.cpp
+++ b/engines/bladerunner/script/script.cpp
@@ -50,6 +50,7 @@
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/elevator.h"
+#include "bladerunner/ui/esper.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/spinner.h"
#include "bladerunner/vector.h"
@@ -1076,8 +1077,12 @@ int ScriptBase::Spinner_Interface_Choose_Dest(int loopId, bool immediately) {
}
void ScriptBase::ESPER_Flag_To_Activate() {
- //TODO
- warning("ESPER_Flag_To_Activate()");
+ if (!_vm->_esper->isOpen()) {
+ _vm->_esper->open(&_vm->_surfaceBack);
+ while (_vm->_esper->isOpen()) {
+ _vm->gameTick();
+ }
+ }
}
bool ScriptBase::Voight_Kampff_Activate(int a1, int a2){
@@ -1282,14 +1287,12 @@ void ScriptBase::KIA_Play_Photograph(int photographId) {
_vm->_kia->playPhotograph(photographId);
}
-void ScriptBase::ESPER_Add_Photo(const char *fileName, int a2, int a3) {
- //TODO
- warning("ESPER_Add_Photo(%s, %d, %d)", fileName, a2, a3);
+void ScriptBase::ESPER_Add_Photo(const char *name, int photoId, int shapeId) {
+ _vm->_esper->addPhoto(name, photoId, shapeId);
}
-void ScriptBase::ESPER_Define_Special_Region(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, const char *name) {
- //TODO
- warning("ESPER_Define_Special_Region(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s)", a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, name);
+void ScriptBase::ESPER_Define_Special_Region(int regionId, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, const char *name) {
+ _vm->_esper->defineRegion(regionId, Common::Rect(a2, a3, a4, a5), Common::Rect(a6, a7, a8, a9), Common::Rect(a10, a11, a12, a13), name);
}
void ScriptBase::VK_Add_Question(int a1, int a2, int a3) {
diff --git a/engines/bladerunner/script/script.h b/engines/bladerunner/script/script.h
index dabdec6b59..574f3a8f65 100644
--- a/engines/bladerunner/script/script.h
+++ b/engines/bladerunner/script/script.h
@@ -272,7 +272,7 @@ protected:
void AI_Movement_Track_Append(int actorId, int waypointId, int delay);
void AI_Movement_Track_Flush(int actorId);
- void ESPER_Add_Photo(const char *fileName, int a2, int a3);
+ void ESPER_Add_Photo(const char *name, int photoId, int shapeId);
void ESPER_Define_Special_Region(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, const char *name);
void KIA_Play_Actor_Dialogue(int actorId, int sentenceId);
diff --git a/engines/bladerunner/set.h b/engines/bladerunner/set.h
index 17d06263b9..d888bff697 100644
--- a/engines/bladerunner/set.h
+++ b/engines/bladerunner/set.h
@@ -37,9 +37,8 @@ class SetEffects;
class SceneObjects;
class Set {
-#if BLADERUNNER_DEBUG_RENDERING
friend class BladeRunnerEngine;
-#endif
+
struct Object {
char name[20];
BoundingBox bbox;
diff --git a/engines/bladerunner/shape.cpp b/engines/bladerunner/shape.cpp
index f5a68045da..45843447ff 100644
--- a/engines/bladerunner/shape.cpp
+++ b/engines/bladerunner/shape.cpp
@@ -43,16 +43,16 @@ Shape::~Shape() {
delete[] _data;
}
-bool Shape::readFromContainer(const Common::String &container, int index) {
+bool Shape::open(const Common::String &container, int index) {
Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->getResourceStream(container));
if (!stream) {
- debug("Shape::readFromContainer failed to open '%s'", container.c_str());
+ debug("Shape::open failed to open '%s'", container.c_str());
return false;
}
uint32 count = stream->readUint32LE();
if (index < 0 || (uint32)index >= count) {
- debug("Shape::readFromContainer invalid index %d (count %u)", index, count);
+ debug("Shape::open invalid index %d (count %u)", index, count);
return false;
}
@@ -63,7 +63,7 @@ bool Shape::readFromContainer(const Common::String &container, int index) {
size = stream->readUint32LE();
if (size != width * height * 2) {
- debug("Shape::readFromContainer size mismatch (w %d, h %d, sz %d)", width, height, size);
+ debug("Shape::open size mismatch (w %d, h %d, sz %d)", width, height, size);
return false;
}
@@ -74,7 +74,7 @@ bool Shape::readFromContainer(const Common::String &container, int index) {
// Enfoce a reasonable size limit
if (width >= 2048 || height >= 2048) {
- warning("Shape::readFromContainer shape too big (%d, %d)", width, height);
+ warning("Shape::open shape too big (%d, %d)", width, height);
}
_width = width;
@@ -82,7 +82,7 @@ bool Shape::readFromContainer(const Common::String &container, int index) {
_data = new byte[size];
if (stream->read(_data, size) != size) {
- debug("Shape::readFromContainer error reading shape %d (w %d, h %d, sz %d)", index, width, height, size);
+ debug("Shape::open error reading shape %d (w %d, h %d, sz %d)", index, width, height, size);
return false;
}
diff --git a/engines/bladerunner/shape.h b/engines/bladerunner/shape.h
index 799826cb4b..fa286e590d 100644
--- a/engines/bladerunner/shape.h
+++ b/engines/bladerunner/shape.h
@@ -44,7 +44,7 @@ public:
Shape(BladeRunnerEngine *vm);
~Shape();
- bool readFromContainer(const Common::String &container, int index);
+ bool open(const Common::String &container, int index);
void draw(Graphics::Surface &surface, int x, int y) const;
diff --git a/engines/bladerunner/text_resource.cpp b/engines/bladerunner/text_resource.cpp
index 42d30038bc..ec5706e234 100644
--- a/engines/bladerunner/text_resource.cpp
+++ b/engines/bladerunner/text_resource.cpp
@@ -20,10 +20,10 @@
*
*/
-
#include "bladerunner/text_resource.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/game_constants.h"
#include "common/debug.h"
#include "common/util.h"
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index 1ee4115fab..4c72be2345 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -76,7 +76,7 @@ int Elevator::activate(int elevatorId) {
for (int i = 0; i != 16; ++i) {
_shapes.push_back(new Shape(_vm));
- _shapes[i]->readFromContainer("ELEVATOR.SHP", i);
+ _shapes[i]->open("ELEVATOR.SHP", i);
}
_imagePicker->resetImages();
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
new file mode 100644
index 0000000000..ea1df6bd38
--- /dev/null
+++ b/engines/bladerunner/ui/esper.cpp
@@ -0,0 +1,1791 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "bladerunner/ui/esper.h"
+
+#include "bladerunner/actor.h"
+#include "bladerunner/ambient_sounds.h"
+#include "bladerunner/audio_player.h"
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/decompress_lcw.h"
+#include "bladerunner/font.h"
+#include "bladerunner/game_info.h"
+#include "bladerunner/game_constants.h"
+#include "bladerunner/mouse.h"
+#include "bladerunner/shape.h"
+#include "bladerunner/script/esper.h"
+#include "bladerunner/text_resource.h"
+#include "bladerunner/ui/ui_image_picker.h"
+#include "bladerunner/vqa_player.h"
+
+#include "common/rect.h"
+#include "common/str.h"
+
+#include "graphics/surface.h"
+
+namespace BladeRunner {
+
+const Common::Rect ESPER::kScreen = Common::Rect(135, 123, 435, 387);
+
+ESPER::ESPER(BladeRunnerEngine *vm) {
+ _vm = vm;
+
+ _isWaiting = false;
+ _shapeButton = nullptr;
+ _shapeThumbnail = nullptr;
+ _regionSelectedAck = false;
+ _isDrawingSelection = false;
+
+ _isOpen = false;
+ _photoData = nullptr;
+ _viewportData = nullptr;
+ _shapeButton = nullptr;
+ _shapeThumbnail = nullptr;
+ _vqaMainPlayer = nullptr;
+ _vqaPhotoPlayer = nullptr;
+ _script = nullptr;
+
+ reset();
+ _buttons = new UIImagePicker(vm, 16);
+}
+
+ESPER::~ESPER() {
+ delete _buttons;
+ reset();
+}
+
+void ESPER::open(Graphics::Surface *surface) {
+ // CD-changing logic has been removed
+
+ while (!_vm->playerHasControl()) {
+ _vm->playerGainsControl();
+ }
+
+ while (_vm->_mouse->isDisabled()) {
+ _vm->_mouse->enable();
+ }
+
+ //TODO: time->lock()
+ _ambientVolume = _vm->_ambientSounds->getVolume();
+ _vm->_ambientSounds->setVolume(_ambientVolume / 2);
+
+ reset();
+
+ if (!_vm->openArchive("MODE.MIX")) {
+ return;
+ }
+
+ _photoData = new Graphics::Surface();
+ _photoData->create(kPhotoWidth, kPhotoHeight, createRGB555());
+
+ _viewportData = new Graphics::Surface();
+ _viewportData->create(kScreen.width(), kScreen.height(), createRGB555());
+
+ _viewportNext = _viewport;
+
+ _vm->_mainFont->setColor(0x001F);
+
+ _shapeButton = new Shape(_vm);
+ if (!_shapeButton->open("ESPBUTTN.SHP", 0)) {
+ return;
+ }
+
+ _shapesPhotos.resize(10);
+
+ _vqaMainPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
+ if (!_vqaMainPlayer->open("ESPER.VQA")) {
+ return;
+ }
+ _vqaMainPlayer->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr);
+
+ _isOpen = true;
+ _flash = false;
+
+ _script = new ESPERScript(_vm);
+ activate(true);
+}
+
+void ESPER::close() {
+ // CD-changing logic has been removed
+ delete _script;
+ _script = nullptr;
+
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(425), 25, 0, 0, 50, 0);
+
+ unloadPhotos();
+ _shapesPhotos.clear();
+
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+
+ _buttons->deactivate();
+ _buttons->resetImages();
+
+ delete _shapeButton;
+ _shapeButton = nullptr;
+
+ delete _photoData;
+ _photoData = nullptr;
+
+ delete _viewportData;
+ _viewportData = nullptr;
+
+ if (_vqaMainPlayer) {
+ _vqaMainPlayer->close();
+ delete _vqaMainPlayer;
+ _vqaMainPlayer= nullptr;
+ }
+
+ _vm->closeArchive("MODE.MIX");
+
+ //TODO: time->unlock()
+ _vm->_ambientSounds->setVolume(_ambientVolume);
+ //TODO: _vm->_scene->resume(false);
+ reset();
+}
+
+bool ESPER::isOpen() {
+ return _isOpen;
+}
+
+void ESPER::handleMouseUp(int x, int y, bool mainButton) {
+ bool actionHandled = _buttons->handleMouseAction(x, y, false, true, false);
+ if (mainButton) {
+ _isMouseDown = false;
+ if (!actionHandled) {
+ if (_isScrolling) {
+ scrollingStop();
+ } else if (_isDrawingSelection && _mouseOverScroll == 4) {
+ _isDrawingSelection = false;
+ resetSelectionRect();
+ }
+ }
+ } else if (_statePhoto == kEsperPhotoStatePhotoZoomOut) {
+ zoomOutStop();
+ }
+}
+
+void ESPER::handleMouseDown(int x, int y, bool mainButton) {
+ bool actionHandled = _buttons->handleMouseAction(x, y, true, false, false);
+
+ if (actionHandled || _vm->_mouse->isDisabled()) {
+ return;
+ }
+
+ if (mainButton) {
+ if (_statePhoto != kEsperPhotoStateVideoZoomOut) {
+ if (kScreen.contains(x, y)) {
+ _isMouseDown = true;
+ playSound(460, 100);
+ }
+ if ( _mouseOverScroll >= 0 && _mouseOverScroll <= 3 && !_isScrolling) {
+ scrollingStart(_mouseOverScroll);
+ }
+ tick();
+ }
+ } else {
+ if (_statePhoto == kEsperPhotoStateShow || _statePhoto == kEsperPhotoStateVideoShow) {
+ zoomOutStart();
+ }
+ }
+}
+
+void ESPER::tick() {
+ if (!_vm->_windowIsActive) {
+ return;
+ }
+
+ tickSound();
+
+ blit(_vm->_surfaceBack, _vm->_surfaceFront);
+
+ int mouseX, mouseY;
+ _vm->_mouse->getXY(&mouseX, &mouseY);
+ if (!_vm->_mouse->isDisabled()) {
+ _buttons->handleMouseAction(mouseX, mouseY, false, false, false);
+ }
+
+ if (!_isOpen) {
+ return;
+ }
+
+ draw(_vm->_surfaceFront);
+ _buttons->draw(_vm->_surfaceFront);
+ tickMouse(_vm->_surfaceFront);
+ tickSound();
+
+ _vm->blitToScreen(_vm->_surfaceFront);
+
+ // TODO: implement 60hz lock for smoother experience
+ _vm->_system->delayMillis(10);
+
+ if (_statePhoto == kEsperPhotoStateVideoShow) {
+ if (_regionSelectedAck) {
+ _regionSelectedAck = false;
+ _script->specialRegionSelected(_photoIdSelected, _regions[_regionSelected].regionId);
+ }
+ }
+}
+
+void ESPER::resume() {
+ //TODO
+}
+
+void ESPER::addPhoto(const char *name, int photoId, int shapeId) {
+ int i = findEmptyPhoto();
+ if (i >= 0) {
+ _photos[i].shapeId = shapeId;
+ _photos[i].isPresent = true;
+ _photos[i].photoId = photoId;
+ strcpy(_photos[i].name, name);
+
+ assert((uint)shapeId < _shapesPhotos.size());
+ _shapesPhotos[shapeId] = new Shape(_vm);
+ _shapesPhotos[shapeId]->open("ESPTHUMB.SHP", shapeId);
+
+ _buttons->defineImage(i,
+ Common::Rect(
+ 100 * (i % 3) + kScreen.left + 3,
+ 66 * (i / 3) + kScreen.top + 3,
+ 100 * (i % 3) + kScreen.left + 100 - 3,
+ 66 * (i / 3) + kScreen.top + 66 - 3
+ ),
+ _shapesPhotos[shapeId],
+ _shapesPhotos[shapeId],
+ _shapesPhotos[shapeId],
+ nullptr);
+ }
+ playSound(420, 25);
+ wait(300);
+ tick();
+}
+
+void ESPER::defineRegion(int regionId, Common::Rect inner, Common::Rect outer, Common::Rect selection, const char *name) {
+ int i = findEmptyRegion();
+ if (i >= 0) {
+ _regions[i].isPresent = true;
+ _regions[i].regionId = regionId;
+ _regions[i].rectInner = inner;
+ _regions[i].rectOuter = outer;
+ _regions[i].rectSelection = selection;
+ strncpy(_regions[i].name, name, 13);
+ _regions[i].name[13] = 0;
+ }
+}
+
+void ESPER::mouseDownCallback(int buttonId, void *callbackData) {
+ ESPER *self = ((ESPER *)callbackData);
+ if (self->_statePhoto != kEsperPhotoStateVideoZoomOut && buttonId == kPhotoCount + 2) {
+ self->zoomOutStart();
+ }
+}
+
+void ESPER::mouseUpCallback(int buttonId, void *callbackData) {
+ ESPER *self = (ESPER *)callbackData;
+ if (buttonId < kPhotoCount) {
+ self->selectPhoto(buttonId);
+ } else if (self->_statePhoto != kEsperPhotoStateVideoZoomOut) {
+ if (buttonId == kPhotoCount + 1) {
+ // TODO: is it even used?
+ } else if (buttonId == kPhotoCount + 2) {
+ self->zoomOutStop();
+ } else if (buttonId == kPhotoCount + 3) {
+ self->goBack();
+ }
+ }
+}
+
+void ESPER::reset() {
+ delete _photoData;
+ _photoData = nullptr;
+
+ delete _viewportData;
+ _viewportData = nullptr;
+
+ delete _shapeButton;
+ _shapeButton = nullptr;
+
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+
+ delete _vqaMainPlayer;
+ _vqaMainPlayer = nullptr;
+
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ delete _script;
+ _script = nullptr;
+
+ _isOpen = false;
+
+ _shapesPhotos.clear();
+ resetData();
+}
+
+void ESPER::resetData() {
+ if (_vqaPhotoPlayer) {
+ _vqaPhotoPlayer->close();
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+ }
+ if (_shapeThumbnail) {
+ delete _shapeThumbnail;
+ _shapeThumbnail = nullptr;
+ }
+
+ _viewport.left = 0;
+ _viewport.top = 0;
+
+ _regionSelectedAck = false;
+ _mouseOverScroll = -1;
+ _isMouseDown = 0;
+ _viewportNext.left = 0;
+ _viewportNext.top = 0;
+
+ _selectionRect.left = -1;
+ _selectionRect.top = -1;
+ _selectionRect.right = -1;
+ _selectionRect.bottom = -1;
+
+ _selectionCrosshairX = -1;
+ _selectionCrosshairY = -1;
+
+ _stateMain = kEsperMainStatePhoto;
+ _statePhoto = kEsperPhotoStateShow;
+
+ _isDrawingSelection = false;
+ _flash = false;
+ _isScrolling = false;
+
+ _scrollingDirection = -1;
+ _timeScrollNext = 0;
+
+ resetPhotos();
+ resetRegions();
+ resetViewport();
+ resetSelectionBlinking();
+ prepareZoom();
+ resetPhotoZooming();
+ resetPhotoOpening();
+
+ _soundId1 = -1;
+ _soundId2 = -1;
+ _soundId3 = -1;
+}
+
+void ESPER::resetPhotos() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ _photos[i].isPresent = false;
+ _photos[i].photoId = -1;
+ }
+}
+
+void ESPER::resetRegions() {
+ for (int i = 0; i < kRegionCount; ++i) {
+ _regions[i].isPresent = false;
+ _regions[i].regionId = -1;
+ }
+}
+
+void ESPER::resetViewport() {
+ _zoomHorizontal = (float)(kScreen.width()) / (float)kPhotoWidth;
+ _zoomVertical = (float)(kScreen.height()) / (float)kPhotoHeight;
+ _zoom = _zoomVertical;
+ _zoomMin = _zoom;
+
+ _timeZoomOutNext = 0;
+ _viewportPositionX = kPhotoWidth / 2;
+ _viewportPositionY = kPhotoHeight / 2;
+
+ updateViewport();
+
+ _screenHalfWidth = kScreen.width() / 2;
+ _screenHalfHeight = kScreen.height() / 2;
+}
+
+void ESPER::resetSelectionRect() {
+ _selectionRect = kScreen;
+ _selectionCrosshairX = -1;
+ _selectionCrosshairY = -1;
+}
+
+void ESPER::resetSelectionBlinking() {
+ _selectionBlinkingCounter = 0;
+ _selectionBlinkingStyle = 0;
+ _timeSelectionBlinkingNext = 0;
+}
+
+void ESPER::resetPhotoZooming() {
+ _zoomStep = 0;
+ _timeZoomNext = 0;
+}
+
+void ESPER::resetPhotoOpening() {
+ _photoOpeningWidth = kScreen.left + 1;
+ _photoOpeningHeight = kScreen.top + 1;
+ _timePhotoOpeningNext = 0;
+}
+
+void ESPER::updateViewport() {
+ float halfWidth = (1.0f / 2.0f) * ((float)kPhotoWidth * (_zoomHorizontal / _zoom));
+ _viewport.left = _viewportPositionX - halfWidth;
+ _viewport.right = _viewportPositionX + halfWidth;
+ if (_viewport.left < 0) {
+ _viewport.right -= _viewport.left;
+ _viewport.left = 0;
+ }
+ if (_viewport.right >= kPhotoWidth) {
+ _viewport.left -= _viewport.right - (kPhotoWidth - 1);
+ if (_viewport.left < 0) {
+ _viewport.left = 0;
+ }
+ _viewport.right = kPhotoWidth - 1;
+ }
+
+ float halfHeight = 1.0f / 2.0f * ((float)kPhotoHeight * (_zoomVertical / _zoom));
+ _viewport.top = _viewportPositionY - halfHeight;
+ _viewport.bottom = _viewportPositionY + halfHeight;
+ if (_viewport.top < 0) {
+ _viewport.bottom -= _viewport.top;
+ _viewport.top = 0;
+ }
+ if (_viewport.bottom >= kPhotoHeight) {
+ _viewport.top -= _viewport.bottom - (kPhotoHeight - 1);
+ if (_viewport.top < 0) {
+ _viewport.top = 0;
+ }
+ _viewport.bottom = kPhotoHeight - 1;
+ }
+
+ _viewportWidth = _viewport.right + 1 - _viewport.left;
+ _viewportHeight = _viewport.bottom + 1 - _viewport.top;
+
+ int centerX = (_viewport.left + _viewport.right) / 2;
+ int centerY = (_viewport.top + _viewport.bottom) / 2;
+
+ float v50 = _zoom / _zoomHorizontal * 1.0f;
+ if ((_viewportPositionX > centerX + v50) || (_viewportPositionX < centerX - v50)) {
+ _viewportPositionX = centerX;
+ }
+
+ float v51 = _zoom / _zoomVertical * 1.0f;
+ if ((_viewportPositionY > centerY + v51) || (_viewportPositionY < centerY - v51)) {
+ _viewportPositionY = centerY;
+ }
+}
+
+void ESPER::activate(bool withOpening) {
+ _vm->_mouse->disable();
+
+ _buttons->resetImages();
+
+ if (withOpening) {
+ setStateMain(kEsperMainStateOpening);
+ playSound(413, 25);
+ wait(1000);
+ playSound(414, 25);
+ wait(2000);
+ } else {
+ _buttons->deactivate();
+ setStateMain(kEsperMainStateClear);
+ }
+
+ _buttons->activate(nullptr, nullptr, mouseDownCallback, mouseUpCallback, this);
+ _buttons->defineImage(kPhotoCount + 3, Common::Rect(42, 403, 76, 437), nullptr, nullptr, _shapeButton, nullptr);
+
+ playSound(415, 25);
+ wait(1000);
+
+ setStateMain(kEsperMainStateList);
+ resetPhotos();
+ _script->initialize();
+
+ _vm->_mouse->enable();
+}
+
+void ESPER::setStateMain(EsperMainStates state) {
+ if (_isOpen) {
+ _stateMain = state;
+ debug("ESPER main state: %d", _stateMain);
+
+ }
+}
+
+void ESPER::setStatePhoto(EsperPhotoStates state) {
+ _statePhoto = state;
+ debug("ESPER photo state: %d", _statePhoto);
+}
+
+void ESPER::wait(int timeout) {
+ if (!_isWaiting) {
+ _isWaiting = true;
+ uint timeEnd = timeout + _vm->getTotalPlayTime();
+ while (_vm->getTotalPlayTime() < timeEnd) {
+ _vm->gameTick();
+ }
+ _isWaiting = false;
+ }
+}
+
+void ESPER::playSound(int soundId, int volume) {
+ if (_soundId1 == -1) {
+ _soundId1 = soundId;
+ _volume1 = volume;
+ } else if (_soundId2 == -1) {
+ _soundId2 = soundId;
+ _volume2 = volume;
+ } else if (_soundId3 == -1) {
+ _soundId3 = soundId;
+ _volume3 = volume;
+ }
+}
+
+void ESPER::draw(Graphics::Surface &surface) {
+ if (!_isOpen) {
+ return;
+ }
+ _vqaMainPlayer->update(false);
+ switch (_stateMain) {
+ case kEsperMainStateOpening:
+ case kEsperMainStateList:
+ return;
+ case kEsperMainStatePhotoOpening:
+ drawPhotoOpening(surface);
+ break;
+ case kEsperMainStateClear:
+ surface.fillRect(kScreen, 0x0000);
+ break;
+ case kEsperMainStatePhoto:
+ if (_isScrolling) {
+ tickScroll();
+ }
+ switch (_statePhoto) {
+ case kEsperPhotoStateShow:
+ drawPhotoWithGrid(surface);
+ if (_isDrawingSelection) {
+ drawSelection(surface, true, 1);
+ }
+#if BLADERUNNER_DEBUG_RENDERING
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (_regions[i].isPresent) {
+ surface.frameRect(
+ Common::Rect(
+ viewportXToScreenX(_regions[i].rectInner.left),
+ viewportYToScreenY(_regions[i].rectInner.top),
+ viewportXToScreenX(_regions[i].rectInner.right),
+ viewportYToScreenY(_regions[i].rectInner.bottom)
+ ),
+ 0x7FE0
+ );
+ surface.frameRect(
+ Common::Rect(
+ viewportXToScreenX(_regions[i].rectOuter.left),
+ viewportYToScreenY(_regions[i].rectOuter.top),
+ viewportXToScreenX(_regions[i].rectOuter.right),
+ viewportYToScreenY(_regions[i].rectOuter.bottom)
+ ),
+ 0x7FE0
+ );
+ }
+ }
+#endif
+ break;
+ case kEsperPhotoStateScrolling:
+ scrollUpdate();
+ drawPhotoWithGrid(surface);
+ break;
+ case kEsperPhotoStateSelectionZooming:
+ drawPhotoWithGrid(surface);
+ if (!drawSelectionZooming(surface)) {
+ setStatePhoto(kEsperPhotoStateSelectionBlinking);
+ playSound(418, 25);
+ }
+ break;
+ case kEsperPhotoStateSelectionBlinking:
+ drawPhotoWithGrid(surface);
+ if (!drawSelectionBlinking(surface)) {
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+ break;
+ case kEsperPhotoStatePhotoZooming:
+ drawPhotoZooming(surface);
+ break;
+ case kEsperPhotoStatePhotoSharpening:
+ drawPhotoSharpening(surface);
+ break;
+ case kEsperPhotoStatePhotoZoomOut:
+ drawPhotoZoomOut(surface);
+ break;
+ case kEsperPhotoStateVideoZooming:
+ drawVideoZooming(surface);
+ break;
+ case kEsperPhotoStateVideoShow:
+ drawVideoFrame(surface);
+ drawGrid(surface);
+ break;
+ case kEsperPhotoStateVideoZoomOut:
+ drawVideoZoomOut(surface);
+ break;
+ default:
+ break;
+ }
+ drawTextCoords(surface);
+
+ break;
+ }
+}
+
+void ESPER::drawPhotoOpening(Graphics::Surface &surface) {
+ bool needMoreZooming = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow >= _timePhotoOpeningNext) {
+ _photoOpeningWidth = MIN(_photoOpeningWidth + 8, kScreen.right - 1);
+ _photoOpeningHeight = MIN(_photoOpeningHeight + 7, kScreen.bottom - 1);
+
+ if (_photoOpeningWidth == kScreen.right - 1 && _photoOpeningHeight == kScreen.bottom - 1) {
+ needMoreZooming = false;
+ }
+
+ _timePhotoOpeningNext = timeNow + 20;
+ }
+ copyImageScale(_photoData, _viewport, &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+
+ surface.hLine(kScreen.left, _photoOpeningHeight, kScreen.right - 1, 0x03E0);
+ surface.vLine(_photoOpeningWidth, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, _photoOpeningHeight - 1, kScreen.right - 1, 0x0240);
+ surface.vLine(_photoOpeningWidth - 1, kScreen.top, kScreen.bottom - 1, 0x0240);
+
+ drawGrid(surface);
+
+ if (!needMoreZooming) {
+ setStateMain(kEsperMainStatePhoto);
+ setStatePhoto(kEsperPhotoStateShow);
+ _vm->_mouse->enable();
+ }
+}
+
+bool ESPER::drawSelectionZooming(Graphics::Surface &surface) {
+ bool zooming = false;
+ bool needMoreZooming = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeSelectionZoomNext) {
+ zooming = true;
+ _selectionRect.left += _selectionRectDelta.left;
+ _selectionRect.top += _selectionRectDelta.top;
+ _selectionRect.right += _selectionRectDelta.right;
+ _selectionRect.bottom += _selectionRectDelta.bottom;
+ ++_selectionZoomStep;
+ _timeSelectionZoomNext = timeNow + 150;
+ if (_selectionZoomStep > kSelectionZoomSteps) {
+ needMoreZooming = false;
+ _selectionRect.left = _selectionRectTarget.left;
+ _selectionRect.top = _selectionRectTarget.top;
+ _selectionRect.right = _selectionRectTarget.right;
+ _selectionRect.bottom = _selectionRectTarget.bottom;
+ }
+ }
+ drawSelection(surface, false, 1);
+ if (!needMoreZooming) {
+ _statePhoto = kEsperPhotoStatePhotoZooming;
+ resetPhotoZooming();
+ zooming = false;
+ }
+ if (zooming) {
+ playSound(416, 20);
+ }
+ return needMoreZooming;
+}
+
+bool ESPER::drawSelectionBlinking(Graphics::Surface &surface) {
+ bool needMoreBlinking = true;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeSelectionBlinkingNext) {
+ _timeSelectionBlinkingNext = timeNow + 100;
+ _selectionBlinkingStyle ^= 1;
+ ++_selectionBlinkingCounter;
+ if (_selectionBlinkingCounter > 10) {
+ needMoreBlinking = false;
+ _selectionBlinkingStyle = 0;
+ }
+ }
+ drawSelection(surface, false, _selectionBlinkingStyle);
+ if (!needMoreBlinking) {
+ resetSelectionBlinking();
+ }
+ return needMoreBlinking;
+}
+
+void ESPER::drawPhotoZooming(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ if ((timeNow > _timeZoomNext) && (_zoomStep < _zoomSteps)) {
+ _flash = true;
+
+ _viewportPositionXCurrent += _viewportPositionXDelta;
+ _viewportPositionYCurrent += _viewportPositionYDelta;
+ _viewportPositionX = _viewportPositionXCurrent;
+ _viewportPositionY = _viewportPositionYCurrent;
+
+ _zoom += _zoomDelta;
+ if (_zoomDelta > 0.0f) {
+ if (_zoom > _zoomTarget) {
+ _zoom = _zoomTarget;
+ _zoomStep = _zoomSteps;
+ } else {
+ _blur += _zoomDelta * 2.0f;
+ }
+ } else if (_zoomDelta < 0.0f) {
+ if (_zoom < _zoomTarget) {
+ _zoom = _zoomTarget;
+ _zoomStep = _zoomSteps;
+ }
+ }
+ ++_zoomStep;
+ if (_zoomStep >= _zoomSteps) {
+ _zoom = _zoomTarget;
+ _viewportPositionX = _viewportPositionXTarget;
+ _viewportPositionY = _viewportPositionYTarget;
+ }
+ updateViewport();
+ _timeZoomNext = timeNow + 300;
+ }
+
+ if (_zoomDelta >= 0.0f) {
+ drawPhoto(surface);
+ } else {
+ drawPhotoWithGrid(surface);
+ }
+ drawGrid(surface);
+
+ if ((timeNow > _timeZoomNext) && (_zoomStep >= _zoomSteps)) {
+ if (_regionSelectedAck) {
+ if (_regions[_regionSelected].name[0]) {
+ if (_zoomDelta < 0.0f) {
+ _blur = 1.0f;
+ _zoomDelta = (_zoom * 1.5f - _zoom) / (float)_zoomSteps; // 0.5f * _zoom ???
+ }
+ setStatePhoto(kEsperPhotoStateVideoZooming);
+ _timeZoomNext += 300;
+ } else {
+ _regionSelectedAck = false;
+ _selectionRect.left = viewportXToScreenX(_regions[_regionSelected].rectInner.left);
+ _selectionRect.right = viewportXToScreenX(_regions[_regionSelected].rectInner.right);
+ _selectionRect.top = viewportYToScreenY(_regions[_regionSelected].rectInner.top);
+ _selectionRect.bottom = viewportYToScreenY(_regions[_regionSelected].rectInner.bottom);
+ prepareZoom();
+ resetPhotoZooming();
+ updateSelection();
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+ } else {
+ setStatePhoto(kEsperPhotoStatePhotoSharpening);
+ }
+ resetPhotoOpening();
+ }
+}
+
+void ESPER::drawPhotoSharpening(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ bool needMoreSharpening = true;
+ if (timeNow >= _timePhotoOpeningNext) {
+ _photoOpeningWidth = MIN(_photoOpeningWidth + 8, kScreen.right - 1);
+ _photoOpeningHeight = MIN(_photoOpeningHeight + 7, kScreen.bottom - 1);
+
+ if (_photoOpeningWidth == kScreen.right - 1 && _photoOpeningHeight == kScreen.bottom - 1) {
+ needMoreSharpening = false;
+ }
+
+ _timePhotoOpeningNext = timeNow + 50;
+ }
+
+ if (_regionSelectedAck && _regions[_regionSelected].name[0]) {
+ _vqaPhotoPlayer->update(true, false);
+ copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, kScreen, _blur);
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+ } else {
+ drawPhoto(surface);
+ copyImageScale(_photoData, _viewport, _viewportData, Common::Rect(0, 0, kScreen.width(), kScreen.height()));
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, Common::Rect(kScreen.left, kScreen.top, _photoOpeningWidth, _photoOpeningHeight));
+
+ }
+ drawGrid(surface);
+ surface.hLine(kScreen.left, _photoOpeningHeight, kScreen.right - 1, 0x03E0);
+ surface.vLine(_photoOpeningWidth, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, _photoOpeningHeight - 1, kScreen.right - 1, 0x0240);
+ surface.vLine(_photoOpeningWidth - 1, kScreen.top, kScreen.bottom - 1, 0x0240);
+ if (!needMoreSharpening) {
+ if (_regionSelectedAck && _regions[_regionSelected].name[0]){
+ setStatePhoto(kEsperPhotoStateVideoShow);
+ } else {
+ setStatePhoto(kEsperPhotoStateShow);
+ }
+ resetPhotoZooming();
+ resetPhotoOpening();
+ _vm->_mouse->enable();
+ }
+}
+
+void ESPER::drawPhotoZoomOut(Graphics::Surface &surface) {
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow >= _timeZoomOutNext) {
+ _timeZoomOutNext = timeNow + 300;
+
+ if (_zoom > _zoomMin) {
+ _zoom /= 1.3f;
+ _flash = true;
+ if (_zoomHorizontal <= _zoomVertical) {
+ if (_zoom < _zoomVertical) {
+ _zoom = _zoomVertical;
+ }
+ } else {
+ if (_zoom < _zoomHorizontal) {
+ _zoom = _zoomHorizontal;
+ }
+ }
+ updateViewport();
+ } else {
+ _statePhoto = kEsperPhotoStateShow;
+ }
+ }
+ drawPhotoWithGrid(surface);
+}
+
+void ESPER::drawVideoZooming(Graphics::Surface &surface) {
+ if (_vqaPhotoPlayer == nullptr) {
+ _vqaPhotoPlayer = new VQAPlayer(_vm, _viewportData);
+ if (!_vqaPhotoPlayer->open(Common::String(_regions[_regionSelected].name) + ".VQA")) {
+ setStatePhoto(kEsperPhotoStateShow);
+ _vm->_mouse->enable();
+
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ return;
+ }
+
+ _timeZoomNext = 0;
+ }
+
+ bool flash = false;
+ bool advanceFrame = false;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeZoomNext) {
+ _timeZoomNext = timeNow + 300;
+ playSound(419, 25);
+ flash = true;
+ advanceFrame = true;
+ _blur += _zoomDelta * 5.0f;
+ }
+
+ int frame = _vqaPhotoPlayer->update(true, advanceFrame);
+ if (frame == _vqaPhotoPlayer->getFrameCount() - 1) {
+ _vqaLastFrame = frame;
+ setStatePhoto(kEsperPhotoStatePhotoSharpening);
+ }
+
+ if (flash) {
+ flashViewport();
+ }
+ copyImageBlur(_viewportData, Common::Rect(0, 0, 299, 263), &surface, kScreen, _blur);
+ drawGrid(surface);
+}
+
+void ESPER::drawVideoZoomOut(Graphics::Surface &surface) {
+ bool flash = false;
+ bool advanceFrame = false;
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow > _timeZoomNext && _vqaLastFrame > 0) {
+ _timeZoomNext = timeNow + 300;
+ playSound(419, 25);
+ //TODO: implement frame loading after seek, then advanceFrame can be removed
+ _vqaPhotoPlayer->seekToFrame(_vqaLastFrame);
+ int nextFrame = _vqaPhotoPlayer->getFrameCount() / 4;
+ if (nextFrame <= 0) {
+ nextFrame = 1;
+ } else if (nextFrame > 4) {
+ nextFrame = 4;
+ }
+ flash = true;
+ advanceFrame = true;
+ _vqaLastFrame -= nextFrame;
+ }
+
+ _vqaPhotoPlayer->update(true, advanceFrame);
+ if (flash) {
+ flashViewport();
+ }
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, kScreen);
+ drawGrid(surface);
+ if (timeNow > _timeZoomNext && _vqaLastFrame <= 0) {
+ _vqaPhotoPlayer->close();
+ delete _vqaPhotoPlayer;
+ _vqaPhotoPlayer = nullptr;
+
+ //TODO: there is code to stop zooming, but it is not working properly in original game
+ // if (_isMouseDown) {
+ // zoomOutStart();
+ // } else {
+ // zoomOutStop();
+ // }
+ zoomOutStart();
+ }
+}
+
+void ESPER::drawPhoto(Graphics::Surface &surface) {
+ copyImageBlur(_photoData, _viewport, &surface, kScreen, _blur);
+}
+
+void ESPER::drawGrid(Graphics::Surface &surface) {
+ for (int i = 0; i < 7; ++i) {
+ surface.drawLine(kScreen.left + i * 50, kScreen.top, kScreen.left + i * 50, kScreen.bottom - 1, 0x109C);
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ surface.drawLine(kScreen.left, kScreen.top + i * 44, kScreen.right - 1, kScreen.top + i * 44, 0x109C);
+ }
+}
+
+void ESPER::drawPhotoWithGrid(Graphics::Surface &surface) {
+ copyImageScale(_photoData, _viewport, &surface, kScreen);
+ drawGrid(surface);
+}
+
+void ESPER::drawSelection(Graphics::Surface &surface, bool crosshair, int style) {
+ int left = CLIP(_selectionRect.left, kScreen.left, (int16)(kScreen.right - 1));
+ int top = CLIP(_selectionRect.top, kScreen.top, (int16)(kScreen.bottom - 1));
+ int right = CLIP(_selectionRect.right, kScreen.left, (int16)(kScreen.right - 1));
+ int bottom = CLIP(_selectionRect.bottom, kScreen.top, (int16)(kScreen.bottom - 1));
+
+ int color = 0x0240;
+ if (style) {
+ color = 0x03E0;
+ }
+
+ // selection rectangle
+ Common::Rect selectedRect(MIN(left, right), MIN(top, bottom), MAX(left, right) + 1, MAX(top, bottom) + 1);
+ Common::Rect selectedRect2 = selectedRect;
+ selectedRect2.grow(-1);
+ surface.frameRect(selectedRect, color);
+ surface.frameRect(selectedRect2, color);
+
+ if (crosshair) {
+ if (_selectionCrosshairX == -1) {
+ if (_selectionRect.left < (kScreen.left + kScreen.right) / 2) {
+ _selectionCrosshairX = kScreen.left;
+ } else {
+ _selectionCrosshairX = kScreen.right - 1;
+ }
+ }
+ if (_selectionCrosshairY == -1) {
+ if (_selectionRect.top < (kScreen.top + kScreen.bottom) / 2) {
+ _selectionCrosshairY = kScreen.top;
+ } else {
+ _selectionCrosshairY = kScreen.bottom - 1;
+ }
+ }
+
+ // ghosting
+ if (_selectionCrosshairX != right) {
+ surface.vLine(_selectionCrosshairX, kScreen.top, kScreen.bottom - 1, 0x0240);
+ if (abs(_selectionCrosshairX - right) <= 1) {
+ _selectionCrosshairX = right;
+ } else {
+ _selectionCrosshairX = (_selectionCrosshairX + right) / 2;
+ }
+ }
+ if (_selectionCrosshairY != bottom) {
+ surface.hLine(kScreen.left, _selectionCrosshairY, kScreen.right - 1, 0x0240);
+ if (abs(_selectionCrosshairY - bottom) <= 1) {
+ _selectionCrosshairY = bottom;
+ } else {
+ _selectionCrosshairY = (_selectionCrosshairY + bottom) / 2;
+ }
+ }
+
+ surface.vLine(right, kScreen.top, kScreen.bottom - 1, 0x03E0);
+ surface.hLine(kScreen.left, bottom, kScreen.right - 1, 0x03E0);
+ }
+}
+
+void ESPER::drawVideoFrame(Graphics::Surface &surface) {
+ _vqaPhotoPlayer->update(true, false);
+ copyImageBlit(_viewportData, Common::Rect(0, 0, 0, 0), &surface, kScreen);
+}
+
+void ESPER::drawTextCoords(Graphics::Surface &surface) {
+ _vm->_mainFont->drawColor(Common::String::format("ZM %04.0f", _zoom / _zoomMin * 2.0f ), surface, 155, 364, 0x001F);
+ _vm->_mainFont->drawColor(Common::String::format("NS %04d", 12 * _viewport.top + 98 ), surface, 260, 364, 0x001F);
+ _vm->_mainFont->drawColor(Common::String::format("EW %04d", 12 * _viewport.left + 167), surface, 364, 364, 0x001F);
+}
+
+void ESPER::flashViewport() {
+ uint16 *ptr = (uint16 *)_viewportData->getPixels();
+ for (int i = 0; i < _viewportData->w * _viewportData->h; ++i) {
+ int8 r = (*ptr >> 10) & 0x1F;
+ int8 g = (*ptr >> 5) & 0x1F;
+ int8 b = (*ptr ) & 0x1F;
+ b = MIN(b * 2, 31);
+ *ptr = r << 10 | g << 5 | b;
+
+ ++ptr;
+ }
+}
+
+void ESPER::copyImageScale(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect) {
+ if (_flash) {
+ playSound(419, 25);
+ }
+
+ int srcDstWidthRatio = srcRect.width() / dstRect.width();
+ int srcDstWidthRest = srcRect.width() % dstRect.width();
+ int srcDstHeightRatio = srcRect.height() / dstRect.height();
+ int srcDstHeightRest = srcRect.height() % dstRect.height();
+
+ if (srcRect.width() > dstRect.width() && srcRect.height() > dstRect.height()) {
+ // reduce
+ int srcY = srcRect.top;
+ int srcYCounter = 0;
+ for (int dstY = dstRect.top; dstY < dstRect.bottom; ++dstY) {
+ int srcX = srcRect.left;
+ int srcXCounter = 0;
+ for (int dstX = dstRect.left; dstX < dstRect.right; ++dstX) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ srcX += srcDstWidthRatio;
+ srcXCounter += srcDstWidthRest;
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ ++srcX;
+ }
+ }
+
+ srcY += srcDstHeightRatio;
+ srcYCounter += srcDstHeightRest;
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ ++srcY;
+ }
+ }
+ } else {
+ // enlarge
+ int srcY = srcRect.top;
+ int srcYCounter = 0;
+ for (int dstY = dstRect.top; dstY < dstRect.bottom; ++dstY) {
+ int srcX = srcRect.left;
+ int srcXCounter = 0;
+ for (int dstX = dstRect.left; dstX < dstRect.right; ++dstX) {
+ srcXCounter += srcRect.width();
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ ++srcX;
+ }
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+ }
+
+ srcYCounter += srcRect.height();
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ ++srcY;
+ }
+ }
+ }
+ _flash = false;
+}
+
+void ESPER::copyImageBlur(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect, float blur) {
+ if (_flash) {
+ playSound(419, 25);
+ }
+
+ int srcDstWidthRatio = srcRect.width() / dstRect.width();
+ int srcDstWidthRest = srcRect.width() % dstRect.width();
+ int srcDstHeightRatio = srcRect.height() / dstRect.height();
+ int srcDstHeightRest = srcRect.height() % dstRect.height();
+
+ int skipStep = (blur - (int)blur) * 1000.0f;
+ if (srcRect.width() > dstRect.width() && srcRect.height() > dstRect.height()) {
+ // reduce
+ int srcY = srcRect.top;
+ int dstY = dstRect.top;
+ int srcYCounter = 0;
+ int skipYMaxCounter = 0;
+ while (dstY < dstRect.bottom) {
+ skipYMaxCounter += skipStep;
+ int skipYMax = blur;
+ if (skipYMaxCounter >= 1000) {
+ skipYMaxCounter -= 1000;
+ ++skipYMax;
+ }
+ int skipY = 0;
+ while (dstY < dstRect.bottom && skipY < skipYMax) {
+ int srcX = srcRect.left;
+ int dstX = dstRect.left;
+ int srcXCounter = 0;
+ int skipXMaxCounter = 0;
+ while (dstX < dstRect.right) {
+ skipXMaxCounter += skipStep;
+ int skipXMax = blur;
+ if (skipXMaxCounter >= 1000) {
+ skipXMaxCounter -= 1000;
+ ++skipXMax;
+ }
+ int skipX = 0;
+ while (dstX < dstRect.right && skipX < skipXMax) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ ++dstX;
+ ++skipX;
+ }
+ srcXCounter += srcDstWidthRest;
+ srcX += srcDstWidthRatio * skipX;
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ srcX += skipX;
+ }
+ }
+
+ ++dstY;
+ ++skipY;
+ }
+
+ srcYCounter += srcDstHeightRest;
+ srcY += srcDstHeightRatio * skipY;
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ srcY += skipY;
+ }
+ }
+ } else {
+ // enlarge
+ int srcY = srcRect.top;
+ int dstY = dstRect.top;
+ int srcYCounter = srcRect.height(); // TODO: look at this again because in original source this is 0, but then first line is doubled
+ int skipYMaxCounter = 0;
+ while (dstY < dstRect.bottom) {
+ skipYMaxCounter += skipStep;
+ int skipYMax = blur;
+ if (skipYMaxCounter >= 1000) {
+ skipYMaxCounter -= 1000;
+ ++skipYMax;
+ }
+ int skipY = 0;
+ while (dstY < dstRect.bottom && skipY < skipYMax) {
+ int srcX = srcRect.left;
+ int dstX = dstRect.left;
+ int srcXCounter = 0;
+ int skipXMaxCounter = 0;
+ while (dstX < dstRect.right) {
+ skipXMaxCounter += skipStep;
+ int skipXMax = blur;
+ if (skipXMaxCounter >= 1000) {
+ skipXMaxCounter -= 1000;
+ ++skipXMax;
+ }
+ int skipX = 0;
+ while (dstX < dstRect.right && skipX < skipXMax) {
+ srcXCounter += srcRect.width();
+ if (srcXCounter >= dstRect.width()) {
+ srcXCounter -= dstRect.width();
+ srcX += 1; // bug in original game? Is using 1 instead of skipX as for Y
+ }
+
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcX, srcY);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstX, dstY);
+
+ if (_flash) {
+ int8 r = (*srcPtr >> 10) & 0x1F;
+ int8 g = (*srcPtr >> 5) & 0x1F;
+ int8 b = (*srcPtr ) & 0x1F;
+ // add blue-ish tint
+ b = MIN(b * 2, 31);
+ *dstPtr = r << 10 | g << 5 | b;
+ } else {
+ *dstPtr = *srcPtr;
+ }
+
+ ++dstX;
+ ++skipX;
+ }
+ }
+
+ ++dstY;
+ ++skipY;
+ }
+
+ srcYCounter += srcRect.height();
+ if (srcYCounter >= dstRect.height()) {
+ srcYCounter -= dstRect.height();
+ srcY += skipY;
+ }
+ }
+ }
+ _flash = false;
+}
+
+void ESPER::copyImageBlit(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect) {
+ for (int y = 0; y < dstRect.height(); ++y) {
+ for (int x = 0; x < dstRect.width(); ++x) {
+ uint16 *srcPtr = (uint16 *)src->getBasePtr(srcRect.left + x, srcRect.top + y);
+ uint16 *dstPtr = (uint16 *)dst->getBasePtr(dstRect.left + x, dstRect.top + y);
+ *dstPtr = *srcPtr;
+ }
+ }
+}
+
+void ESPER::tickSound() {
+ if (_soundId1 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId1), _volume1, 0, 0, 50, 0);
+ _soundId1 = -1;
+ }
+ if (_soundId2 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId2), _volume2, 0, 0, 50, 0);
+ _soundId2 = -1;
+ }
+ if (_soundId3 != -1) {
+ _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(_soundId3), _volume3, 0, 0, 50, 0);
+ _soundId3 = -1;
+ }
+}
+
+void ESPER::tickMouse(Graphics::Surface &surface) {
+ if (_vm->_mouse->isDisabled()) {
+ return;
+ }
+
+ int cursor = -1;
+
+ Common::Point p = _vm->getMousePos();
+
+ _mouseOverScroll = 4;
+ if (_stateMain == kEsperMainStatePhoto) {
+ if (kScreen.contains(p)) {
+ if (_statePhoto == kEsperPhotoStateShow) {
+ if ( _zoom != 2.0f) {
+ if (_isMouseDown) {
+ if (_isDrawingSelection) {
+ _selectionRect.right = p.x;
+ _selectionRect.bottom = p.y;
+ } else {
+ _selectionRect.left = p.x;
+ _selectionRect.top = p.y;
+ _selectionRect.right = p.x + 1;
+ _selectionRect.bottom = p.y + 1;
+ _isDrawingSelection = true;
+ }
+ } else {
+ if (_isDrawingSelection) {
+ _selectionRect.right = p.x;
+ _selectionRect.bottom = p.y;
+ if (_selectionRect.right < _selectionRect.left) {
+ SWAP(_selectionRect.left, _selectionRect.right);
+ }
+ if (_selectionRect.bottom < _selectionRect.top) {
+ SWAP(_selectionRect.bottom, _selectionRect.top);
+ }
+
+ if (_selectionRect.right >= _selectionRect.left + 3) {
+ updateSelection();
+ _vm->_mouse->disable();
+ zoomingStart();
+ } else {
+ resetSelectionRect();
+ }
+ }
+ _isDrawingSelection = false;
+ }
+ }
+ }
+ surface.vLine(p.x, p.y - 8, p.y - 1, 0x03E0);
+ surface.vLine(p.x, p.y + 8, p.y + 1, 0x03E0);
+ surface.hLine(p.x - 8, p.y, p.x - 1, 0x03E0);
+ surface.hLine(p.x + 8, p.y, p.x + 1, 0x03E0);
+ _mouseOverScroll = -1;
+ } else if (p.x >= 85 && p.y >= 73 && p.x <= 484 && p.y <= 436) {
+ if (!_isDrawingSelection && _statePhoto != kEsperPhotoStateVideoShow && _zoom != 2.0f) {
+ _mouseOverScroll = (angle_1024((kScreen.left + kScreen.right) / 2, (kScreen.top + kScreen.bottom) / 2, p.x, p.y) + 128) / 256;
+ if (_mouseOverScroll >= 4) {
+ _mouseOverScroll = 0;
+ }
+ if (_mouseOverScroll == 0 && this->_viewport.top == 0) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 1 && this->_viewport.right == kPhotoWidth - 1) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 2 && this->_viewport.bottom == kPhotoHeight - 1) {
+ _mouseOverScroll = 4;
+ } else if (_mouseOverScroll == 3 && this->_viewport.left == 0) {
+ _mouseOverScroll = 4;
+ }
+ if (_mouseOverScroll != 4) {
+ cursor = _mouseOverScroll + 2;
+ }
+ }
+ }
+ }
+
+ if (_mouseOverScroll == 4) {
+ cursor = _buttons->hasHoveredImage() ? 1 : 0;
+ }
+ if (cursor != -1) {
+ _vm->_mouse->setCursor(cursor);
+ _vm->_mouse->draw(surface, p.x, p.y);
+ }
+}
+
+void ESPER::tickScroll() {
+ int timeNow = _vm->getTotalPlayTime();
+ if (timeNow <= _timeScrollNext) {
+ return;
+ }
+ _timeScrollNext = timeNow + 300;
+
+ if (_scrollingDirection == 0) {
+ scrollUp();
+ } else if (_scrollingDirection == 1) {
+ scrollRight();
+ } else if (_scrollingDirection == 2) {
+ scrollDown();
+ } else if (_scrollingDirection == 3) {
+ scrollLeft();
+ }
+}
+
+int ESPER::findEmptyPhoto() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ if (!_photos[i].isPresent) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void ESPER::selectPhoto(int photoId) {
+ _vm->_mouse->disable();
+ _photoIdSelected = _photos[photoId].photoId;
+ unloadPhotos();
+ _script->photoSelected(_photoIdSelected);
+
+ Common::ScopedPtr<Common::SeekableReadStream> s(_vm->getResourceStream(_photos[photoId].name));
+
+ if (!s) {
+ reset();
+ }
+
+ int photoSize = _photoData->w * _photoData->h * _photoData->format.bytesPerPixel;
+
+ s->skip(3); // not used, but there is compression type
+ uint width = s->readUint32LE();
+ uint height = s->readUint32LE();
+ int photoCompressedSize = s->size() - s->pos();
+ uint8 *photoCompressed = (uint8 *)_photoData->getPixels() + photoSize - photoCompressedSize;
+ s->read(photoCompressed, photoCompressedSize);
+
+ decompress_lcw(photoCompressed, photoCompressedSize, (uint8 *)_photoData->getPixels(), photoSize);
+
+ // apply palette
+ for (uint j = 0; j < width * height; ++j) {
+ // _photoData[j] = Palette[_photoData[j]];
+ }
+
+ _shapeThumbnail = new Shape(_vm);
+ _shapeThumbnail->open("ESPTHUMB.SHP", _photos[photoId].shapeId);
+ _buttons->resetImages();
+ _buttons->defineImage(kPhotoCount + 2, Common::Rect(480, 350, 578, 413), _shapeThumbnail, _shapeThumbnail, _shapeThumbnail, nullptr);
+ _buttons->defineImage(kPhotoCount + 3, Common::Rect(42, 403, 76, 437), nullptr, nullptr, _shapeButton, nullptr);
+
+ resetPhotoOpening();
+ resetViewport();
+ setStateMain(kEsperMainStatePhotoOpening);
+ setStatePhoto(kEsperPhotoStateOpening);
+ playSound(422, 25);
+ playSound(423, 25);
+}
+
+void ESPER::unloadPhotos() {
+ for (int i = 0; i < kPhotoCount; ++i) {
+ if (_photos[i].isPresent) {
+ _buttons->resetImage(i);
+ delete _shapesPhotos[i];
+ _shapesPhotos[i] = nullptr;
+ _photos[i].isPresent = false;
+ }
+ }
+}
+
+int ESPER::findEmptyRegion() {
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (!_regions[i].isPresent) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int ESPER::findRegion(Common::Rect where) {
+ for (int i = 0; i < kRegionCount; ++i) {
+ if (_regions[i].isPresent && _regions[i].rectOuter.contains(where) && where.contains(_regions[i].rectInner)){
+ return i;
+ }
+ }
+ return -1;
+}
+
+void ESPER::zoomingStart() {
+ prepareZoom();
+ setStatePhoto(kEsperPhotoStateSelectionZooming);
+}
+
+void ESPER::zoomOutStart() {
+ if (_statePhoto == kEsperPhotoStateVideoShow) {
+ resetPhotoZooming();
+ setStatePhoto(kEsperPhotoStateVideoZoomOut);
+ } else {
+ zoomOutStop();
+ if (_zoomMin < _zoom) {
+ _isZoomingOut = true;
+ setStatePhoto(kEsperPhotoStatePhotoZoomOut);
+ }
+ }
+}
+
+void ESPER::zoomOutStop() {
+ _isZoomingOut = false;
+ _statePhoto = kEsperPhotoStateShow;
+}
+
+void ESPER::scrollingStart(int direction) {
+ scrollingStop();
+ if ((direction != 3 || _viewport.left > 0)
+ && (direction != 0 || _viewport.top > 0)
+ && (direction != 1 || _viewport.right != kPhotoWidth - 1)
+ && (direction != 2 || _viewport.bottom != kPhotoHeight - 1)) {
+ _isScrolling = true;
+ _scrollingDirection = direction;
+ }
+}
+
+void ESPER::scrollingStop() {
+ _isScrolling = false;
+ _scrollingDirection = -1;
+}
+
+void ESPER::scrollUpdate() {
+ if ((_viewport.left == _viewportNext.left) && (_viewportNext.top == _viewport.top)) {
+ setStatePhoto(kEsperPhotoStateShow);
+ return;
+ }
+
+ if (_viewport.left != _viewportNext.left) {
+ _viewport.left = _viewportNext.left;
+ _viewport.right = _viewportNext.right;
+ _viewportPositionX = (_viewportNext.left + _viewportNext.right) / 2;
+ }
+
+ if (_viewport.top != _viewportNext.top) {
+ _viewport.top = _viewportNext.top;
+ _viewport.bottom = _viewportNext.bottom;
+ _viewportPositionY = (_viewportNext.top + _viewportNext.bottom) / 2;
+ }
+}
+
+void ESPER::scrollLeft() {this->_flash = 1;
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.left = _viewport.left - 40;
+ _viewportNext.right = _viewport.right - 40;
+ if (_viewportNext.left < 0) {
+ _viewportNext.right -= _viewportNext.left;
+ _viewportNext.left = 0;
+ scrollingStop();
+ }
+ _viewportNext.top = _viewport.top;
+ _viewportNext.bottom = _viewport.bottom;
+}
+
+void ESPER::scrollUp() {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.top = _viewport.top - 40;
+ _viewportNext.bottom = _viewport.bottom - 40;
+ if (_viewportNext.top < 0) {
+ _viewportNext.bottom -= _viewportNext.top;
+ _viewportNext.top = 0;
+ scrollingStop();
+ }
+ _viewportNext.left = _viewport.left;
+ _viewportNext.right = _viewport.right;
+}
+
+void ESPER::scrollRight() {
+ if (_viewport.right < kPhotoWidth - 1) {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.left = _viewport.left + 40;
+ _viewportNext.right = _viewport.right + 40;
+ _viewportNext.top = _viewport.top;
+ _viewportNext.bottom = _viewport.bottom;
+
+ if (_viewportNext.right > kPhotoWidth - 1) {
+ _viewportNext.left -= _viewportNext.right - (kPhotoWidth - 1);
+ _viewportNext.right = kPhotoWidth - 1;
+ scrollingStop();
+ }
+ }
+}
+
+void ESPER::scrollDown() {
+ if (_viewport.bottom < kPhotoHeight - 1) {
+ _flash = true;
+ setStatePhoto(kEsperPhotoStateScrolling);
+
+ _viewportNext.top = _viewport.top + 40;
+ _viewportNext.bottom = _viewport.bottom + 40;
+ _viewportNext.left = _viewport.left;
+ _viewportNext.right = _viewport.right;
+
+ if (_viewportNext.bottom > kPhotoHeight - 1) {
+ _viewportNext.top -= _viewportNext.bottom - (kPhotoHeight - 1);
+ _viewportNext.bottom = kPhotoHeight - 1;
+ scrollingStop();
+ }
+ }
+}
+
+void ESPER::goBack() {
+ // CD-changing logic has been removed
+
+ if (_stateMain == kEsperMainStateList) {
+ close();
+ } else {
+ resetData();
+ activate(false);
+ }
+}
+
+void ESPER::prepareZoom() {
+ _selectionZoomStep = 0;
+ _timeSelectionZoomNext = 0;
+
+ _selectionRectTarget = _selectionRect;
+ resetSelectionRect();
+ _selectionRectDelta.left = (_selectionRectTarget.left - _selectionRect.left) / kSelectionZoomSteps;
+ _selectionRectDelta.top = (_selectionRectTarget.top - _selectionRect.top) / kSelectionZoomSteps;
+ _selectionRectDelta.right = (_selectionRectTarget.right - _selectionRect.right) / kSelectionZoomSteps;
+ _selectionRectDelta.bottom = (_selectionRectTarget.bottom - _selectionRect.bottom) / kSelectionZoomSteps;
+
+ Common::Rect rect = _selectionRectTarget;
+ if (_regionSelectedAck) {
+ rect.left = viewportXToScreenX(_regions[_regionSelected].rectSelection.left);
+ rect.top = viewportYToScreenY(_regions[_regionSelected].rectSelection.top);
+ rect.right = viewportXToScreenX(_regions[_regionSelected].rectSelection.right);
+ rect.bottom = viewportYToScreenY(_regions[_regionSelected].rectSelection.bottom);
+ }
+
+ _zoomSteps = 10;
+ float ratio = (rect.width() + 1.0f) / (float)kScreen.width();
+ if (ratio == 0.0f) {
+ _zoomTarget = ratio;
+ _zoomDelta = 0.0f;
+ } else {
+ _zoomTarget = CLIP(_zoom / ratio, _zoomMin, 2.0f);
+ _zoomSteps = CLIP((int)(_zoomTarget / _zoom) - 1, 0, 5) + 5;
+ _zoomDelta = (_zoomTarget - _zoom) / (float)_zoomSteps;
+ }
+
+ _blur = 1.0f;
+
+ _viewportPositionXTarget = _viewport.left + ((rect.left + rect.right) / 2 - kScreen.left) * _viewport.width() / kScreen.width();
+ _viewportPositionYTarget = _viewport.top + ((rect.top + rect.bottom) / 2 - kScreen.top ) * _viewport.height() / kScreen.height();
+ _viewportPositionXDelta = (_viewportPositionXTarget - _viewportPositionX) / (float)_zoomSteps;
+ _viewportPositionYDelta = (_viewportPositionYTarget - _viewportPositionY) / (float)_zoomSteps;
+ _viewportPositionXCurrent = _viewportPositionX;
+ _viewportPositionYCurrent = _viewportPositionY;
+}
+
+void ESPER::updateSelection() {
+ int selectionWidth = abs(_selectionRect.right + 1 - _selectionRect.left);
+ int selectionHeight = abs(_selectionRect.bottom + 1 - _selectionRect.top);
+
+ int photoSelectedWidth = _viewport.width() * selectionWidth / kScreen.width();
+ if (photoSelectedWidth < _screenHalfWidth) {
+ // minimal width of selection
+ selectionWidth = kScreen.width() * _screenHalfWidth / _viewport.width();
+ }
+
+ photoSelectedWidth = _viewport.height() * selectionHeight / kScreen.height();
+ if (photoSelectedWidth < _screenHalfHeight) {
+ // minimal height of selection
+ selectionHeight = kScreen.height() * _screenHalfHeight / _viewport.height();
+ }
+
+ // correct aspect ratio
+ if (selectionWidth / (float)kScreen.width() <= selectionHeight / (float)kScreen.height()) {
+ while (selectionWidth / (float)kScreen.width() <= selectionHeight / (float)kScreen.height()) {
+ ++selectionWidth;
+ }
+ } else {
+ while (selectionHeight / (float)kScreen.height() <= selectionWidth / (float)kScreen.width()) {
+ ++selectionHeight;
+ }
+ }
+
+ if (selectionWidth > kScreen.width()) {
+ selectionWidth = kScreen.width();
+ }
+ if (selectionHeight > kScreen.height()) {
+ selectionHeight = kScreen.height();
+ }
+
+ int left = _viewport.right - (kScreen.right - 1 - _selectionRect.left) * _viewport.width() / kScreen.width();
+ int right = _viewport.left + (_selectionRect.right - kScreen.left ) * _viewport.width() / kScreen.width();
+ int top = _viewport.bottom - (kScreen.bottom - 1 - _selectionRect.top ) * _viewport.height() / kScreen.height();
+ int bottom = _viewport.top + (_selectionRect.bottom - kScreen.top ) * _viewport.height() / kScreen.height();
+
+ bool stop = false;
+ bool alternate = false;
+
+ while (selectionWidth > abs(_selectionRect.right + 1 - _selectionRect.left)) {
+ if (alternate) {
+ --_selectionRect.left;
+ if (_selectionRect.left < 0) {
+ left = _viewport.right - (kScreen.right - 1 + 100 - _selectionRect.left) * _viewport.width() / kScreen.width();
+ if (left < 0) {
+ left = 0;
+ ++_selectionRect.left;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = false;
+ }
+ }
+ } else {
+ ++_selectionRect.right;
+ if (_selectionRect.right > kScreen.right - 1) {
+ right = _viewport.left + (_selectionRect.right - kScreen.left) * _viewport.width() / kScreen.width();
+ if (right > kPhotoWidth - 1) {
+ right = kPhotoWidth - 1;
+ --_selectionRect.right;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = true;
+ }
+ }
+ }
+ if (!stop) {
+ alternate = !alternate;
+ }
+ }
+
+ alternate = false;
+ stop = false;
+ while (selectionHeight > abs(_selectionRect.bottom + 1 - _selectionRect.top)) {
+ if (alternate) {
+ --_selectionRect.top;
+ if (_selectionRect.top < 0) {
+ top = _viewport.bottom - (kScreen.bottom - 1 - _selectionRect.top) * _viewport.height() / kScreen.height();
+ if (top < 0) {
+ top = 0;
+ ++_selectionRect.top;
+ if (stop) {
+ break;
+ }
+ stop = true;
+ alternate = false;
+ }
+ }
+ } else {
+ ++_selectionRect.bottom;
+ if (_selectionRect.bottom > kScreen.bottom - 1) {
+ bottom = _viewport.top + (_selectionRect.bottom - kScreen.top) * _viewport.height() / kScreen.height();
+ if (bottom > kPhotoHeight - 1) {
+ bottom = kPhotoHeight - 1;
+ --_selectionRect.bottom;
+ if (stop) {
+ break;
+ }
+ alternate = true;
+ stop = true;
+ }
+ }
+ }
+ if (!stop) {
+ alternate = !alternate;
+ }
+ }
+
+ _regionSelected = findRegion(Common::Rect(left, top, right, bottom));
+ if (_regionSelected >= 0) {
+ _regionSelectedAck = true;
+ setStatePhoto(kEsperPhotoStatePhotoZooming);
+ }
+}
+
+int ESPER::viewportXToScreenX(int x) {
+ return kScreen.width() * (x - _viewport.left) / _viewport.width() + kScreen.left;
+}
+
+int ESPER::viewportYToScreenY(int y) {
+ return kScreen.height() * (y - _viewport.top) / _viewport.height() + kScreen.top;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/ui/esper.h b/engines/bladerunner/ui/esper.h
new file mode 100644
index 0000000000..9d0dd6d012
--- /dev/null
+++ b/engines/bladerunner/ui/esper.h
@@ -0,0 +1,285 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BLADERUNNER_ESPER_H
+#define BLADERUNNER_ESPER_H
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+class Font;
+class Shape;
+class VQAPlayer;
+class UIImagePicker;
+class ESPERScript;
+
+// CD-changing logic has been removed
+
+enum EsperMainStates {
+ kEsperMainStateOpening = 0,
+ kEsperMainStateList = 1,
+ kEsperMainStatePhotoOpening = 2,
+ kEsperMainStateClear = 3,
+ kEsperMainStatePhoto = 5
+};
+
+enum EsperPhotoStates {
+ kEsperPhotoStateShow = 0,
+ kEsperPhotoStateOpening = 1,
+ kEsperPhotoStateScrolling = 2,
+ kEsperPhotoStateSelectionZooming = 3,
+ kEsperPhotoStateSelectionBlinking = 4,
+ kEsperPhotoStatePhotoZooming = 5,
+ kEsperPhotoStatePhotoSharpening = 6,
+ kEsperPhotoStatePhotoZoomOut = 7,
+ kEsperPhotoStateVideoZooming = 8,
+ kEsperPhotoStateVideoShow = 9,
+ kEsperPhotoStateVideoZoomOut = 10
+};
+
+class ESPER {
+ static const int kPhotoCount = 12;
+ static const int kRegionCount = 6;
+ static const int kPhotoWidth = 1280;
+ static const int kPhotoHeight = 960;
+ static const int kSelectionZoomSteps = 6;
+
+ static const Common::Rect kScreen;
+
+ struct Photo {
+ bool isPresent;
+ char name[13];
+ int photoId;
+ int shapeId;
+ };
+
+ struct Region {
+ bool isPresent;
+ int regionId;
+ Common::Rect rectInner;
+ Common::Rect rectOuter;
+ Common::Rect rectSelection;
+ char name[13];
+ };
+
+ BladeRunnerEngine *_vm;
+ ESPERScript *_script;
+
+ bool _isWaiting;
+ bool _isOpen;
+
+ UIImagePicker *_buttons;
+
+ Graphics::Surface *_photoData;
+ Graphics::Surface *_viewportData;
+
+ VQAPlayer *_vqaMainPlayer;
+ VQAPlayer *_vqaPhotoPlayer;
+ int _vqaLastFrame;
+
+ Shape *_shapeButton;
+ Common::Array<Shape *> _shapesPhotos;
+ Shape *_shapeThumbnail;
+
+ Photo _photos[kPhotoCount];
+ int _photoIdSelected;
+
+ Region _regions[kRegionCount];
+ int _regionSelected;
+ bool _regionSelectedAck;
+
+ EsperMainStates _stateMain;
+ EsperPhotoStates _statePhoto;
+
+ bool _isDrawingSelection;
+ bool _isMouseDown;
+ int _mouseOverScroll;
+
+ float _zoomHorizontal;
+ float _zoomVertical;
+ float _zoom;
+ float _zoomMin;
+ float _zoomTarget;
+ float _zoomDelta;
+ float _blur;
+ int _zoomSteps;
+ int _zoomStep;
+ int _timeZoomNext;
+
+ bool _isZoomingOut;
+ int _timeZoomOutNext;
+
+ Common::Rect _viewport;
+ Common::Rect _viewportNext;
+ int _viewportPositionX;
+ int _viewportPositionY;
+ float _viewportPositionXCurrent;
+ float _viewportPositionYCurrent;
+ int _viewportPositionXTarget;
+ int _viewportPositionYTarget;
+ float _viewportPositionXDelta;
+ float _viewportPositionYDelta;
+ int _viewportWidth;
+ int _viewportHeight;
+
+ int _screenHalfWidth;
+ int _screenHalfHeight;
+
+ int _flash;
+
+ Common::Rect _selectionRect;
+ Common::Rect _selectionRectTarget;
+ Common::Rect _selectionRectDelta;
+ int _selectionCrosshairX;
+ int _selectionCrosshairY;
+
+ int _selectionBlinkingCounter;
+ int _selectionBlinkingStyle;
+ int _timeSelectionBlinkingNext;
+
+ int _selectionZoomStep;
+ int _timeSelectionZoomNext;
+
+ int _photoOpeningWidth;
+ int _photoOpeningHeight;
+ int _timePhotoOpeningNext;
+
+ bool _isScrolling;
+ int _scrollingDirection;
+ int _timeScrollNext;
+
+ int _soundId1;
+ int _volume1;
+ int _soundId2;
+ int _volume2;
+ int _soundId3;
+ int _volume3;
+ int _ambientVolume;
+
+public:
+ ESPER(BladeRunnerEngine *vm);
+ ~ESPER();
+
+ void open(Graphics::Surface *surface);
+ void close();
+ bool isOpen();
+
+ void handleMouseUp(int x, int y, bool buttonLeft);
+ void handleMouseDown(int x, int y, bool buttonLeft);
+
+ void tick();
+
+ void resume();
+
+ void addPhoto(const char *name, int photoId, int shapeId);
+ void defineRegion(int regionId, Common::Rect inner, Common::Rect outer, Common::Rect selection, const char *name);
+
+private:
+ static void mouseDownCallback(int, void *);
+ static void mouseUpCallback(int, void *);
+
+ void reset();
+ void resetData();
+ void resetPhotos();
+ void resetRegions();
+ void resetViewport();
+ void resetSelectionRect();
+ void resetSelectionBlinking();
+ void resetPhotoZooming();
+ void resetPhotoOpening();
+
+ void updateViewport();
+
+ void activate(bool withOpening);
+ void setStateMain(EsperMainStates state);
+ void setStatePhoto(EsperPhotoStates state);
+
+ void wait(int timeout);
+ void playSound(int soundId, int volume);
+
+ void draw(Graphics::Surface &surface);
+
+ void drawPhotoOpening(Graphics::Surface &surface);
+ bool drawSelectionZooming(Graphics::Surface &surface);
+ bool drawSelectionBlinking(Graphics::Surface &surface);
+ void drawPhotoZooming(Graphics::Surface &surface);
+ void drawPhotoSharpening(Graphics::Surface &surface);
+ void drawPhotoZoomOut(Graphics::Surface &surface);
+ void drawVideoZooming(Graphics::Surface &surface);
+ void drawVideoZoomOut(Graphics::Surface &surface);
+
+ void drawPhoto(Graphics::Surface &surface);
+ void drawGrid(Graphics::Surface &surface);
+ void drawPhotoWithGrid(Graphics::Surface &surface);
+ void drawSelection(Graphics::Surface &surface, bool crosshair, int style);
+ void drawVideoFrame(Graphics::Surface &surface);
+ void drawTextCoords(Graphics::Surface &surface);
+
+ void flashViewport();
+
+ void copyImageScale(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect);
+ void copyImageBlur(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect, float u);
+ void copyImageBlit(Graphics::Surface *src, Common::Rect srcRect, Graphics::Surface *dst, Common::Rect dstRect);
+
+ void tickSound();
+ void tickMouse(Graphics::Surface &surface);
+ void tickScroll();
+
+ int findEmptyPhoto();
+ void selectPhoto(int photoId);
+ void unloadPhotos();
+
+ int findEmptyRegion();
+ int findRegion(Common::Rect where);
+
+ void zoomingStart();
+ void zoomOutStart();
+ void zoomOutStop();
+
+ void scrollingStart(int direction);
+ void scrollingStop();
+ void scrollUpdate();
+ void scrollLeft();
+ void scrollUp();
+ void scrollRight();
+ void scrollDown();
+
+ void goBack();
+
+ void prepareZoom();
+ void updateSelection();
+
+ int viewportXToScreenX(int x);
+ int viewportYToScreenY(int y);
+
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index 95a66b6332..7563816f0f 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -59,20 +59,6 @@ namespace BladeRunner {
const char *KIA::kPogo = "POGO";
-enum KIASections {
- kKIASectionNone = 0,
- kKIASectionCrimes = 1,
- kKIASectionSuspects = 2,
- kKIASectionClues = 3,
- kKIASectionSettings = 4,
- kKIASectionHelp = 5,
- kKIASectionSave = 6,
- kKIASectionLoad = 7,
- kKIASectionQuit = 8,
- kKIASectionDiagnostic = 9,
- kKIASectionPogo = 10
-};
-
KIA::KIA(BladeRunnerEngine *vm) {
_vm = vm;
@@ -137,12 +123,81 @@ void KIA::openLastOpened() {
open(_lastSectionIdKIA);
}
-void KIA::openOptions() {
- open(kKIASectionSettings);
+void KIA::open(KIASections sectionId) {
+ if (_currentSectionId == sectionId) {
+ return;
+ }
+
+ if (!sectionId) {
+ unload();
+ return;
+ }
+
+ if (!isOpen()) {
+ init();
+ }
+
+ switch (_currentSectionId) {
+ case kKIASectionCrimes:
+ _crimesSection->saveToLog();
+ break;
+ case kKIASectionSuspects:
+ _suspectsSection->saveToLog();
+ break;
+ case kKIASectionClues:
+ _cluesSection->saveToLog();
+ break;
+ default:
+ break;
+ }
+
+ if (sectionId != kKIASectionCrimes && sectionId != kKIASectionSuspects && sectionId != kKIASectionClues) {
+ playerReset();
+ }
+
+ _transitionId = getTransitionId(_currentSectionId, sectionId);
+ const char *name = getSectionVqaName(sectionId);
+ if (getSectionVqaName(_currentSectionId) != name) {
+ if (_mainVqaPlayer) {
+ _mainVqaPlayer->close();
+ delete _mainVqaPlayer;
+ _mainVqaPlayer = nullptr;
+ }
+
+ _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
+ _mainVqaPlayer->open(name);
+ }
+
+ if (_transitionId) {
+ playTransitionSound(_transitionId);
+ _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr);
+ _mainVqaPlayer->setLoop(getVqaLoopMain(sectionId), -1, kLoopSetModeEnqueue, loopEnded, this);
+ } else {
+ int loopId = getVqaLoopMain(sectionId);
+ _mainVqaPlayer->setLoop(loopId, -1, kLoopSetModeImmediate, nullptr, nullptr);
+ _mainVqaPlayer->setLoop(loopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr);
+ }
+
+ _buttons->resetImages();
+ createButtons(sectionId);
+ switchSection(sectionId);
+ _currentSectionId = sectionId;
+
+ if (sectionId == kKIASectionCrimes || sectionId == kKIASectionSuspects || sectionId == kKIASectionClues) {
+ _lastSectionIdKIA = _currentSectionId;
+ }
+
+ if (sectionId == kKIASectionSettings || sectionId == kKIASectionHelp || sectionId == kKIASectionSave || sectionId == kKIASectionLoad) {
+ _lastSectionIdOptions = _currentSectionId;
+ }
+}
+
+bool KIA::isOpen() {
+ return _currentSectionId != kKIASectionNone;
}
void KIA::tick() {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
@@ -235,8 +290,9 @@ void KIA::tick() {
_shapes->get(39)->draw(_vm->_surfaceFront, 583, 342);
}
}
+ //TODO: implement frame loading after seek, then advanceFrame can be removed
_playerVqaPlayer->seekToFrame(_playerVqaFrame);
- _playerVqaPlayer->update(true); //_vm->_surfaceFront, 3
+ _playerVqaPlayer->update(true, true);
_playerSliceModelAngle += (float)(timeDiff) * 1.0f/400.0f;
while (_playerSliceModelAngle >= 2 * M_PI) {
@@ -296,11 +352,24 @@ void KIA::tick() {
_vm->_mouse->draw(_vm->_surfaceFront, mouse.x, mouse.y);
_vm->blitToScreen(_vm->_surfaceFront);
+ _vm->_system->delayMillis(10);
+
_timeLast = timeNow;
}
+void KIA::resume() {
+ // vqaPlayer::clear(this->_vqaPlayerMain);
+ if (_transitionId) {
+ _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr);
+ _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId), -1, kLoopSetModeEnqueue, loopEnded, this);
+ } else {
+ _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId), -1, kLoopSetModeImmediate, nullptr, nullptr);
+ _mainVqaPlayer->setLoop(getVqaLoopMain(_currentSectionId) + 1, -1, kLoopSetModeJustStart, nullptr, nullptr);
+ }
+}
+
void KIA::handleMouseDown(int mouseX, int mouseY, bool mainButton) {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
if (mainButton) {
@@ -312,7 +381,7 @@ void KIA::handleMouseDown(int mouseX, int mouseY, bool mainButton) {
}
void KIA::handleMouseUp(int mouseX, int mouseY, bool mainButton) {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
if (mainButton) {
@@ -339,7 +408,7 @@ void KIA::handleMouseUp(int mouseX, int mouseY, bool mainButton) {
}
void KIA::handleKeyUp(const Common::KeyState &kbd) {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
@@ -370,7 +439,7 @@ void KIA::handleKeyUp(const Common::KeyState &kbd) {
}
void KIA::handleKeyDown(const Common::KeyState &kbd) {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
switch (kbd.keycode) {
@@ -473,7 +542,7 @@ void KIA::playPhotograph(int photographId) {
}
_playerPhotographId = photographId;
_playerPhotograph = new Shape(_vm);
- _playerPhotograph->readFromContainer("photos.shp", photographId);
+ _playerPhotograph->open("photos.shp", photographId);
}
void KIA::mouseDownCallback(int buttonId, void *callbackData) {
@@ -544,73 +613,6 @@ void KIA::loopEnded(void *callbackData, int frame, int loopId) {
self->_transitionId = 0;
}
-void KIA::open(int sectionId) {
- if (_currentSectionId == sectionId) {
- return;
- }
-
- if (!sectionId) {
- unload();
- return;
- }
-
- if (!_currentSectionId) {
- init();
- }
-
- switch (_currentSectionId) {
- case kKIASectionCrimes:
- _crimesSection->saveToLog();
- break;
- case kKIASectionSuspects:
- _suspectsSection->saveToLog();
- break;
- case kKIASectionClues:
- _cluesSection->saveToLog();
- break;
- }
-
- if (sectionId != kKIASectionCrimes && sectionId != kKIASectionSuspects && sectionId != kKIASectionClues) {
- playerReset();
- }
-
- _transitionId = getTransitionId(_currentSectionId, sectionId);
- const char *name = getSectionVqaName(sectionId);
- if (getSectionVqaName(_currentSectionId) != name) {
- if (_mainVqaPlayer) {
- _mainVqaPlayer->close();
- delete _mainVqaPlayer;
- _mainVqaPlayer = nullptr;
- }
-
- _mainVqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack);
- _mainVqaPlayer->open(name);
- }
-
- if (_transitionId) {
- playTransitionSound(_transitionId);
- _mainVqaPlayer->setLoop(getVqaLoopTransition(_transitionId), -1, kLoopSetModeImmediate, nullptr, nullptr);
- _mainVqaPlayer->setLoop(getVqaLoopMain(sectionId), -1, kLoopSetModeEnqueue, loopEnded, this);
- } else {
- int loopId = getVqaLoopMain(sectionId);
- _mainVqaPlayer->setLoop(loopId, -1, kLoopSetModeImmediate, nullptr, nullptr);
- _mainVqaPlayer->setLoop(loopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr);
- }
-
- _buttons->resetImages();
- createButtons(sectionId);
- switchSection(sectionId);
- _currentSectionId = sectionId;
-
- if (sectionId == kKIASectionCrimes || sectionId == kKIASectionSuspects || sectionId == kKIASectionClues) {
- _lastSectionIdKIA = _currentSectionId;
- }
-
- if (sectionId == kKIASectionSettings || sectionId == kKIASectionHelp || sectionId == kKIASectionSave || sectionId == kKIASectionLoad) {
- _lastSectionIdOptions = _currentSectionId;
- }
-}
-
void KIA::init() {
if (!_vm->openArchive("MODE.MIX")) {
return;
@@ -641,7 +643,7 @@ void KIA::init() {
}
void KIA::unload() {
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
@@ -671,7 +673,7 @@ void KIA::unload() {
_vm->closeArchive("MODE.MIX");
- _currentSectionId = 0;
+ _currentSectionId = kKIASectionNone;
// TODO: Unfreeze game time
@@ -846,7 +848,7 @@ void KIA::createButtons(int sectionId) {
void KIA::buttonClicked(int buttonId) {
int soundId = 0;
- if (!_currentSectionId) {
+ if (!isOpen()) {
return;
}
switch (buttonId) {
diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h
index 0452230382..0ac7a22600 100644
--- a/engines/bladerunner/ui/kia.h
+++ b/engines/bladerunner/ui/kia.h
@@ -54,6 +54,21 @@ class Shape;
class UIImagePicker;
class VQAPlayer;
+enum KIASections {
+ kKIASectionNone = 0,
+ kKIASectionCrimes = 1,
+ kKIASectionSuspects = 2,
+ kKIASectionClues = 3,
+ kKIASectionSettings = 4,
+ kKIASectionHelp = 5,
+ kKIASectionSave = 6,
+ kKIASectionLoad = 7,
+ kKIASectionQuit = 8,
+ kKIASectionDiagnostic = 9,
+ kKIASectionPogo = 10
+};
+
+
class KIA {
static const char *kPogo;
static const int kPlayerActorDialogueQueueCapacity = 31;
@@ -68,9 +83,6 @@ class KIA {
int _forceOpen;
int _transitionId;
- int _lastSectionIdKIA;
- int _lastSectionIdOptions;
-
int _playerVqaTimeLast;
VQAPlayer *_playerVqaPlayer;
int _playerVqaFrame;
@@ -86,7 +98,11 @@ class KIA {
int _playerActorDialogueQueueSize;
int _playerActorDialogueState;
+ KIASections _currentSectionId;
+ KIASections _lastSectionIdKIA;
+ KIASections _lastSectionIdOptions;
KIASectionBase *_currentSection;
+
KIASectionClues *_cluesSection;
KIASectionCrimes *_crimesSection;
KIASectionDiagnostic *_diagnosticSection;
@@ -104,20 +120,22 @@ class KIA {
int _pogoPos;
public:
- int _currentSectionId;
- KIALog *_log;
- KIAScript *_script;
- KIAShapes *_shapes;
+ KIALog *_log;
+ KIAScript *_script;
+ KIAShapes *_shapes;
public:
KIA(BladeRunnerEngine *vm);
~KIA();
void openLastOpened();
- void openOptions();
+ void open(KIASections sectionId);
+ bool isOpen();
void tick();
+ void resume();
+
void handleMouseDown(int mouseX, int mouseY, bool mainButton);
void handleMouseUp(int mouseX, int mouseY, bool mainButton);
void handleKeyUp(const Common::KeyState &kbd);
@@ -133,7 +151,6 @@ private:
static void mouseUpCallback(int buttonId, void *callbackData);
static void loopEnded(void *callbackData, int frame, int loopId);
- void open(int sectionId);
void init();
void unload();
void switchSection(int sectionId);
diff --git a/engines/bladerunner/ui/kia_section_crimes.cpp b/engines/bladerunner/ui/kia_section_crimes.cpp
index b0b3d7c234..a5ff221250 100644
--- a/engines/bladerunner/ui/kia_section_crimes.cpp
+++ b/engines/bladerunner/ui/kia_section_crimes.cpp
@@ -409,7 +409,7 @@ void KIASectionCrimes::updateSuspectPhoto() {
if (_suspectPhotoShapeId != -1) {
_suspectPhotoShape = new Shape(_vm);
- _suspectPhotoShape->readFromContainer("photos.shp", _suspectPhotoShapeId);
+ _suspectPhotoShape->open("photos.shp", _suspectPhotoShapeId);
}
}
diff --git a/engines/bladerunner/ui/kia_section_suspects.cpp b/engines/bladerunner/ui/kia_section_suspects.cpp
index ce13243f76..053705c9da 100644
--- a/engines/bladerunner/ui/kia_section_suspects.cpp
+++ b/engines/bladerunner/ui/kia_section_suspects.cpp
@@ -498,7 +498,7 @@ void KIASectionSuspects::updateSuspectPhoto() {
if (_suspectPhotoShapeId != -1) {
_suspectPhotoShape = new Shape(_vm);
- _suspectPhotoShape->readFromContainer("photos.shp", _suspectPhotoShapeId);
+ _suspectPhotoShape->open("photos.shp", _suspectPhotoShapeId);
}
}
diff --git a/engines/bladerunner/ui/kia_shapes.cpp b/engines/bladerunner/ui/kia_shapes.cpp
index d992ec7045..ce5c8a7e5e 100644
--- a/engines/bladerunner/ui/kia_shapes.cpp
+++ b/engines/bladerunner/ui/kia_shapes.cpp
@@ -45,7 +45,7 @@ void KIAShapes::load() {
for (uint i = 0; i < kShapeCount; ++i) {
Shape *shape = new Shape(_vm);
- shape->readFromContainer("kiaopt.shp", i);
+ shape->open("kiaopt.shp", i);
_shapes[i] = shape;
}
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 72c8b19070..2df9390afe 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -23,6 +23,7 @@
#include "bladerunner/ui/spinner.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/game_constants.h"
#include "bladerunner/mouse.h"
#include "bladerunner/scene.h"
#include "bladerunner/shape.h"
@@ -123,7 +124,7 @@ int Spinner::chooseDestination(int loopId, bool immediately) {
for (int j = 0; j != shapeCount; ++j) {
_shapes.push_back(new Shape(_vm));
- _shapes[j]->readFromContainer("SPINNER.SHP", firstShapeId + j);
+ _shapes[j]->open("SPINNER.SHP", firstShapeId + j);
}
_imagePicker->resetImages();
diff --git a/engines/bladerunner/ui/ui_image_picker.cpp b/engines/bladerunner/ui/ui_image_picker.cpp
index 6c46880e02..ca61a4d989 100644
--- a/engines/bladerunner/ui/ui_image_picker.cpp
+++ b/engines/bladerunner/ui/ui_image_picker.cpp
@@ -24,6 +24,7 @@
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
+#include "bladerunner/game_constants.h"
#include "bladerunner/mouse.h"
#include "bladerunner/shape.h"
@@ -207,6 +208,7 @@ void UIImagePicker::draw(Graphics::Surface &surface) {
}
#if BLADERUNNER_DEBUG_RENDERING
surface.frameRect(img.rect, 0x7fff);
+ _vm->_mainFont->drawColor(Common::String::format("%d", i), surface, (img.rect.left + img.rect.right) / 2, (img.rect.top + img.rect.bottom) / 2, 0x7fff);
#endif
}
}
@@ -252,11 +254,11 @@ void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) {
_vm->_mainFont->drawColor(tooltip, surface, rect.left + 2, rect.top, 0x7FFF);
}
-void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) {
+bool UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) {
if (!_isVisible || ignore) {
- return;
+ return false;
}
-
+ bool actionHandled = false;
int hoveredImageIndex = -1;
for (int i = 0; i != _imageCount; ++i) {
if (_images[i].rect.contains(x, y)) {
@@ -286,9 +288,10 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign
if (down && !_isButtonDown) {
_isButtonDown = true;
_pressedImageIndex = _hoveredImageIndex;
- if (_hoveredImageIndex != 1) {
+ if (_hoveredImageIndex != -1) {
if (_mouseDownCallback) {
_mouseDownCallback(_hoveredImageIndex, _callbackData);
+ actionHandled = true;
}
}
}
@@ -297,13 +300,17 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign
if (up) {
if (_isButtonDown) {
if (_hoveredImageIndex == _pressedImageIndex && _pressedImageIndex != -1) {
- if (_mouseUpCallback)
+ if (_mouseUpCallback) {
_mouseUpCallback(_hoveredImageIndex, _callbackData);
+ actionHandled = true;
+ }
}
}
_isButtonDown = false;
_pressedImageIndex = -1;
}
+
+ return actionHandled;
}
void UIImagePicker::resetImage(int i) {
diff --git a/engines/bladerunner/ui/ui_image_picker.h b/engines/bladerunner/ui/ui_image_picker.h
index 00f2f3dfd9..d5b18b2f0a 100644
--- a/engines/bladerunner/ui/ui_image_picker.h
+++ b/engines/bladerunner/ui/ui_image_picker.h
@@ -91,7 +91,7 @@ public:
void draw(Graphics::Surface &surface);
void drawTooltip(Graphics::Surface &surface, int x, int y);
- void handleMouseAction(int x, int y, bool down, bool up, bool ignore = false);
+ bool handleMouseAction(int x, int y, bool down, bool up, bool ignore = false);
void resetImage(int i);
bool hasHoveredImage();
diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp
index aec4d18e5d..7dc7616fe9 100644
--- a/engines/bladerunner/vqa_decoder.cpp
+++ b/engines/bladerunner/vqa_decoder.cpp
@@ -25,6 +25,7 @@
#include "bladerunner/bladerunner.h"
#include "bladerunner/decompress_lcw.h"
#include "bladerunner/decompress_lzo.h"
+#include "bladerunner/game_info.h"
#include "bladerunner/lights.h"
#include "bladerunner/screen_effects.h"
#include "bladerunner/view.h"
@@ -116,9 +117,8 @@ const char *strTag(uint32 tag) {
return s;
}
-VQADecoder::VQADecoder(Graphics::Surface *surface) :
+VQADecoder::VQADecoder() :
_s(nullptr),
- _surface(surface),
_frameInfo(nullptr),
_videoTrack(nullptr),
_audioTrack(nullptr),
@@ -179,7 +179,7 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
}
} while (chd.id != kFINF);
- _videoTrack = new VQAVideoTrack(this, _surface);
+ _videoTrack = new VQAVideoTrack(this);
_audioTrack = new VQAAudioTrack(this);
#if BLADERUNNER_DEBUG_CONSOLE
@@ -194,9 +194,9 @@ bool VQADecoder::loadStream(Common::SeekableReadStream *s) {
return true;
}
-void VQADecoder::decodeVideoFrame(int frame, bool forceDraw) {
+void VQADecoder::decodeVideoFrame(Graphics::Surface *surface, int frame, bool forceDraw) {
_decodingFrame = frame;
- _videoTrack->decodeVideoFrame(forceDraw);
+ _videoTrack->decodeVideoFrame(surface, forceDraw);
}
void VQADecoder::decodeZBuffer(ZBuffer *zbuffer) {
@@ -573,9 +573,8 @@ bool VQADecoder::readMFCI(Common::SeekableReadStream *s, uint32 size) {
return true;
}
-VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface) {
+VQADecoder::VQAVideoTrack::VQAVideoTrack(VQADecoder *vqaDecoder) {
_vqaDecoder = vqaDecoder;
- _surface = surface;
_hasNewFrame = false;
VQADecoder::Header *header = &vqaDecoder->_header;
@@ -635,9 +634,10 @@ Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const {
return _frameRate;
}
-void VQADecoder::VQAVideoTrack::decodeVideoFrame(bool forceDraw) {
+void VQADecoder::VQAVideoTrack::decodeVideoFrame(Graphics::Surface *surface, bool forceDraw) {
if (_hasNewFrame || forceDraw) {
- decodeFrame((uint16 *)_surface->getPixels());
+ assert(surface);
+ decodeFrame(surface);
_hasNewFrame = false;
}
}
@@ -807,11 +807,12 @@ bool VQADecoder::VQAVideoTrack::readVPTR(Common::SeekableReadStream *s, uint32 s
return true;
}
-void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha) {
- uint16 frame_width = _width;
- uint32 frame_stride = 640;
- uint16 block_width = _blockW;
- uint16 block_height = _blockH;
+void VQADecoder::VQAVideoTrack::VPTRWriteBlock(Graphics::Surface *surface, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha) {
+ uint16 *frame = (uint16 *)surface->getPixels();
+ uint16 frame_width = _width;
+ uint32 frame_stride = surface->w;
+ uint16 block_width = _blockW;
+ uint16 block_height = _blockH;
const uint8 *const block_src =
&_codebook[2 * srcBlock * block_width * block_height];
@@ -845,7 +846,7 @@ void VQADecoder::VQAVideoTrack::VPTRWriteBlock(uint16 *frame, unsigned int dstBl
} while (--count);
}
-bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) {
+bool VQADecoder::VQAVideoTrack::decodeFrame(Graphics::Surface *surface) {
CodebookInfo &codebookInfo = _vqaDecoder->codebookInfoForFrame(_vqaDecoder->_decodingFrame);
if (!codebookInfo.data) {
@@ -876,19 +877,19 @@ bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) {
count = 2 * (((command >> 8) & 0x1f) + 1);
srcBlock = command & 0x00ff;
- VPTRWriteBlock(frame, dstBlock, srcBlock, count);
+ VPTRWriteBlock(surface, dstBlock, srcBlock, count);
dstBlock += count;
break;
case 2:
count = 2 * (((command >> 8) & 0x1f) + 1);
srcBlock = command & 0x00ff;
- VPTRWriteBlock(frame, dstBlock, srcBlock, 1);
+ VPTRWriteBlock(surface, dstBlock, srcBlock, 1);
++dstBlock;
for (int i = 0; i < count; ++i) {
srcBlock = *src++;
- VPTRWriteBlock(frame, dstBlock, srcBlock, 1);
+ VPTRWriteBlock(surface, dstBlock, srcBlock, 1);
++dstBlock;
}
break;
@@ -897,7 +898,7 @@ bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) {
count = 1;
srcBlock = command & 0x1fff;
- VPTRWriteBlock(frame, dstBlock, srcBlock, count, prefix == 4);
+ VPTRWriteBlock(surface, dstBlock, srcBlock, count, prefix == 4);
++dstBlock;
break;
case 5:
@@ -905,7 +906,7 @@ bool VQADecoder::VQAVideoTrack::decodeFrame(uint16 *frame) {
count = *src++;
srcBlock = command & 0x1fff;
- VPTRWriteBlock(frame, dstBlock, srcBlock, count, prefix == 6);
+ VPTRWriteBlock(surface, dstBlock, srcBlock, count, prefix == 6);
dstBlock += count;
break;
default:
diff --git a/engines/bladerunner/vqa_decoder.h b/engines/bladerunner/vqa_decoder.h
index 8ab6be5c7e..fdc5b53d19 100644
--- a/engines/bladerunner/vqa_decoder.h
+++ b/engines/bladerunner/vqa_decoder.h
@@ -54,14 +54,14 @@ enum VQADecoderSkipFlags {
class VQADecoder {
public:
- VQADecoder(Graphics::Surface *surface);
+ VQADecoder();
~VQADecoder();
bool loadStream(Common::SeekableReadStream *s);
void readFrame(int frame, uint readFlags = kVQAReadAll);
- void decodeVideoFrame(int frame, bool forceDraw = false);
+ void decodeVideoFrame(Graphics::Surface *surface, int frame, bool forceDraw = false);
void decodeZBuffer(ZBuffer *zbuffer);
Audio::SeekableAudioStream *decodeAudioFrame();
void decodeView(View *view);
@@ -139,7 +139,7 @@ private:
class VQAAudioTrack;
Common::SeekableReadStream *_s;
- Graphics::Surface *_surface;
+ // Graphics::Surface *_surface;
Header _header;
int _readingFrame;
@@ -172,7 +172,7 @@ private:
class VQAVideoTrack {
public:
- VQAVideoTrack(VQADecoder *vqaDecoder, Graphics::Surface *surface);
+ VQAVideoTrack(VQADecoder *vqaDecoder);
~VQAVideoTrack();
uint16 getWidth() const;
@@ -180,7 +180,7 @@ private:
int getFrameCount() const;
- void decodeVideoFrame(bool forceDraw);
+ void decodeVideoFrame(Graphics::Surface *surface, bool forceDraw);
void decodeZBuffer(ZBuffer *zbuffer);
void decodeView(View *view);
void decodeScreenEffects(ScreenEffects *aesc);
@@ -202,7 +202,7 @@ private:
private:
VQADecoder *_vqaDecoder;
- Graphics::Surface *_surface;
+
bool _hasNewFrame;
uint16 _numFrames;
@@ -233,8 +233,8 @@ private:
uint8 *_screenEffectsData;
uint32 _screenEffectsDataSize;
- void VPTRWriteBlock(uint16 *frame, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false);
- bool decodeFrame(uint16 *frame);
+ void VPTRWriteBlock(Graphics::Surface *surface, unsigned int dstBlock, unsigned int srcBlock, int count, bool alpha = false);
+ bool decodeFrame(Graphics::Surface *surface);
};
class VQAAudioTrack {
diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp
index 1616459d36..7e41913042 100644
--- a/engines/bladerunner/vqa_player.cpp
+++ b/engines/bladerunner/vqa_player.cpp
@@ -23,6 +23,7 @@
#include "bladerunner/vqa_player.h"
#include "bladerunner/bladerunner.h"
+#include "bladerunner/game_constants.h"
#include "audio/decoders/raw.h"
@@ -71,7 +72,7 @@ void VQAPlayer::close() {
_s = nullptr;
}
-int VQAPlayer::update(bool forceDraw) {
+int VQAPlayer::update(bool forceDraw, bool advanceFrame, Graphics::Surface *customSurface) {
uint32 now = 60 * _vm->_system->getMillis();
int result = -1;
@@ -104,14 +105,14 @@ int VQAPlayer::update(bool forceDraw) {
}
result = -1;
- } else if (_frameNext > _frameEnd) {
+ } else if (_frameNext > _frameEnd) {
result = -3;
} else if (now < _frameNextTime) {
result = -1;
- } else {
+ } else if (advanceFrame) {
_frame = _frameNext;
_decoder.readFrame(_frameNext, kVQAReadVideo);
- _decoder.decodeVideoFrame(_frameNext);
+ _decoder.decodeVideoFrame(customSurface != nullptr ? customSurface : _surface, _frameNext);
int audioPreloadFrames = 14;
@@ -140,8 +141,9 @@ int VQAPlayer::update(bool forceDraw) {
_frameNext++;
result = _frame;
}
+
if (result < 0 && forceDraw && _frame != -1) {
- _decoder.decodeVideoFrame(_frame, true);
+ _decoder.decodeVideoFrame(customSurface != nullptr ? customSurface : _surface, _frame, true);
result = _frame;
}
return result;
@@ -240,6 +242,10 @@ int VQAPlayer::getLoopEndFrame(int loop) {
return end;
}
+int VQAPlayer::getFrameCount() {
+ return _decoder.numFrames();
+}
+
void VQAPlayer::queueAudioFrame(Audio::AudioStream *audioStream) {
int n = _audioStream->numQueuedStreams();
if (n == 0)
diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h
index d276b9111c..d95f8b36a2 100644
--- a/engines/bladerunner/vqa_player.h
+++ b/engines/bladerunner/vqa_player.h
@@ -50,6 +50,7 @@ class VQAPlayer {
Common::SeekableReadStream *_s;
VQADecoder _decoder;
Audio::QueuingAudioStream *_audioStream;
+ Graphics::Surface *_surface;
int _frame;
int _frameNext;
@@ -77,7 +78,8 @@ public:
VQAPlayer(BladeRunnerEngine *vm, Graphics::Surface *surface)
: _vm(vm),
_s(nullptr),
- _decoder(surface),
+ _surface(surface),
+ _decoder(),
_audioStream(nullptr),
_frame(-1),
_frameNext(-1),
@@ -102,7 +104,7 @@ public:
bool open(const Common::String &name);
void close();
- int update(bool forceDraw = false);
+ int update(bool forceDraw = false, bool advanceFrame = true, Graphics::Surface *customSurface = nullptr);
void updateZBuffer(ZBuffer *zbuffer);
void updateView(View *view);
void updateScreenEffects(ScreenEffects *screenEffects);
@@ -116,6 +118,8 @@ public:
int getLoopBeginFrame(int loop);
int getLoopEndFrame(int loop);
+ int getFrameCount();
+
private:
void queueAudioFrame(Audio::AudioStream *audioStream);
};
diff --git a/engines/bladerunner/waypoints.h b/engines/bladerunner/waypoints.h
index 5a2a1fce6a..183c0895b1 100644
--- a/engines/bladerunner/waypoints.h
+++ b/engines/bladerunner/waypoints.h
@@ -31,9 +31,7 @@
namespace BladeRunner {
class Waypoints {
-#if BLADERUNNER_DEBUG_RENDERING
friend class BladeRunnerEngine;
-#endif
struct Waypoint {
int setId;