Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,321 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/elevator.h"
#include "bladerunner/actor.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/game_info.h"
#include "bladerunner/mouse.h"
#include "bladerunner/shape.h"
#include "bladerunner/script/script.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
#include "bladerunner/subtitles.h"
#include "common/rect.h"
#include "common/str.h"
#include "common/system.h"
namespace BladeRunner {
Elevator::Elevator(BladeRunnerEngine *vm) {
_vm = vm;
reset();
_imagePicker = new UIImagePicker(vm, 8);
_shapes = new Shapes(vm);
}
Elevator::~Elevator() {
delete _shapes;
_shapes = nullptr;
delete _imagePicker;
_imagePicker = nullptr;
}
int Elevator::activate(int elevatorId) {
const char *vqaName;
if (elevatorId == kElevatorMA) {
_buttonClicked = 3;
vqaName = "MA06ELEV.VQA";
} else if (elevatorId == kElevatorPS) {
_buttonClicked = 1;
vqaName = "PS02ELEV.VQA";
} else {
error("Invalid elevator id");
}
if (!_vm->openArchive("MODE.MIX")) {
return 0;
}
_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, vqaName);
if (!_vqaPlayer->open()) {
return 0;
}
_vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr);
_vm->_mouse->setCursor(0);
_shapes->load("ELEVATOR.SHP");
_imagePicker->resetImages();
if (elevatorId == kElevatorMA) {
_imagePicker->defineImage(
0,
Common::Rect(220, 298, 308, 392),
nullptr,
_shapes->get(11),
_shapes->get(14),
nullptr);
_imagePicker->defineImage(
1,
Common::Rect(259, 259, 302, 292),
nullptr,
_shapes->get(10),
_shapes->get(13),
nullptr);
_imagePicker->defineImage(
2,
Common::Rect(227, 398, 301, 434),
nullptr,
_shapes->get(12),
_shapes->get(15),
nullptr);
} else { // kElevatorPS
_imagePicker->defineImage(
4,
Common::Rect(395, 131, 448, 164),
nullptr,
_shapes->get(0),
_shapes->get(5),
nullptr
);
_imagePicker->defineImage(
3,
Common::Rect(395, 165, 448, 198),
nullptr,
_shapes->get(1),
_shapes->get(6),
nullptr
);
_imagePicker->defineImage(
5,
Common::Rect(395, 199, 448, 232),
nullptr,
_shapes->get(2),
_shapes->get(7),
nullptr
);
_imagePicker->defineImage(
6,
Common::Rect(395, 233, 448, 264),
nullptr,
_shapes->get(3),
_shapes->get(8),
nullptr
);
_imagePicker->defineImage(
7,
Common::Rect(395, 265, 448, 295),
nullptr,
_shapes->get(4),
_shapes->get(9),
nullptr
);
}
_imagePicker->activate(
mouseInCallback,
mouseOutCallback,
mouseDownCallback,
mouseUpCallback,
this
);
open();
_vm->_time->pause();
_buttonClicked = -1;
do {
_vm->gameTick();
} while (_vm->_gameIsRunning && _buttonClicked == -1);
_imagePicker->deactivate();
delete _vqaPlayer;
_vqaPlayer = nullptr;
_shapes->unload();
_vm->closeArchive("MODE.MIX");
_isOpen = false;
_vm->_time->resume();
return _buttonClicked;
}
void Elevator::open() {
resetDescription();
_isOpen = true;
}
bool Elevator::isOpen() const {
return _isOpen;
}
int Elevator::handleMouseUp(int x, int y) {
_imagePicker->handleMouseAction(x, y, false, true, false);
return false;
}
int Elevator::handleMouseDown(int x, int y) {
_imagePicker->handleMouseAction(x, y, true, false, false);
return false;
}
void Elevator::tick() {
if (!_vm->_windowIsActive) {
return;
}
int frame = _vqaPlayer->update();
assert(frame >= -1);
// vqaPlayer renders to _surfaceBack
blit(_vm->_surfaceBack, _vm->_surfaceFront);
Common::Point p = _vm->getMousePos();
// TODO(madmoose): BLADE.EXE has hasHoveredImage before handleMouseAction?
_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
if (_imagePicker->hasHoveredImage()) {
_vm->_mouse->setCursor(1);
} else {
_vm->_mouse->setCursor(0);
}
_imagePicker->draw(_vm->_surfaceFront);
_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
_vm->_subtitles->tick(_vm->_surfaceFront);
_vm->blitToScreen(_vm->_surfaceFront);
tickDescription();
}
void Elevator::buttonClick(int buttonId) {
_buttonClicked = buttonId;
}
void Elevator::reset() {
_isOpen = false;
_vqaPlayer = nullptr;
_imagePicker = nullptr;
_actorId = -1;
_sentenceId = -1;
_timeSpeakDescriptionStart = 0u;
_buttonClicked = false;
}
void Elevator::buttonFocus(int buttonId) {
switch (buttonId) {
case 7:
setupDescription(kActorAnsweringMachine, 140);
break;
case 6:
setupDescription(kActorAnsweringMachine, 130);
break;
case 5:
setupDescription(kActorAnsweringMachine, 120);
break;
case 4:
setupDescription(kActorAnsweringMachine, 100);
break;
case 3:
setupDescription(kActorAnsweringMachine, 110);
break;
case 2:
setupDescription(kActorAnsweringMachine, 130);
break;
case 1:
setupDescription(kActorAnsweringMachine, 100);
break;
case 0:
setupDescription(kActorAnsweringMachine, 150);
break;
default:
resetDescription();
break;
}
}
void Elevator::setupDescription(int actorId, int sentenceId) {
_actorId = actorId;
_sentenceId = sentenceId;
_timeSpeakDescriptionStart = _vm->_time->current();
}
void Elevator::resetDescription() {
_actorId = -1;
_sentenceId = -1;
_timeSpeakDescriptionStart = 0u;
}
void Elevator::tickDescription() {
uint32 now = _vm->_time->current();
// unsigned difference is intentional
if (_actorId <= 0 || (now - _timeSpeakDescriptionStart < 600u)) {
return;
}
_vm->_actors[_actorId]->speechPlay(_sentenceId, false);
_actorId = -1;
_sentenceId = -1;
}
void Elevator::mouseInCallback(int buttonId, void *self) {
((Elevator *)self)->buttonFocus(buttonId);
}
void Elevator::mouseOutCallback(int, void *self) {
((Elevator *)self)->buttonFocus(-1);
}
void Elevator::mouseDownCallback(int, void *self) {
Elevator *elevator = ((Elevator *)self);
elevator->_vm->_audioPlayer->playAud(elevator->_vm->_gameInfo->getSfxTrack(kSfxELEBUTN1), 100, 0, 0, 50, 0);
}
void Elevator::mouseUpCallback(int buttonId, void *self) {
((Elevator *)self)->buttonClick(buttonId);
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,71 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_ELEVATOR_H
#define BLADERUNNER_ELEVATOR_H
#include "common/array.h"
namespace BladeRunner {
class BladeRunnerEngine;
class Shapes;
class VQAPlayer;
class UIImagePicker;
class Elevator {
BladeRunnerEngine *_vm;
bool _isOpen;
VQAPlayer *_vqaPlayer;
int _buttonClicked;
Shapes *_shapes;
UIImagePicker *_imagePicker;
int _actorId;
int _sentenceId;
uint32 _timeSpeakDescriptionStart;
public:
Elevator(BladeRunnerEngine *vm);
~Elevator();
int activate(int elevatorId);
void open();
bool isOpen() const;
int handleMouseUp(int x, int y);
int handleMouseDown(int x, int y);
void tick();
void buttonClick(int buttonId);
void reset();
void buttonFocus(int buttonId);
void setupDescription(int actorId, int sentenceId);
void resetDescription();
void tickDescription();
private:
static void mouseInCallback(int, void *);
static void mouseOutCallback(int, void *);
static void mouseDownCallback(int, void *);
static void mouseUpCallback(int, void *);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,319 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "common/rect.h"
#include "audio/mixer.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/audio_speech.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/mouse.h"
#include "bladerunner/music.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/end_credits.h"
namespace BladeRunner {
EndCredits::EndCredits(BladeRunnerEngine *vm) {
_vm = vm;
}
EndCredits::~EndCredits() {
}
// Aux method with hardcoded fixes for the credits
// in the official localizations
// ENG (not needed)
// ITA, FRA, ESP, DEU
void EndCredits::creditsCheckAndFix(int &textResourceId, Common::String &textStr) {
switch (_vm->_language) {
case Common::IT_ITA:
switch (textResourceId) {
case 71: // Grafici Ideatori
textStr = "Ideatori Grafici";
break;
case 211:
textStr.trim();
break;
default:
break;
}
break;
case Common::DE_DEU:
switch (textResourceId) {
case 312:
textStr.trim();
break;
default:
break;
}
break;
case Common::FR_FRA:
switch (textResourceId) {
case 97:
// fall through
case 265:
// fall through
case 266:
textStr.trim();
break;
default:
break;
}
break;
default:
break;
}
if (_vm->_language == Common::ES_ESP && _vm->_spanishCreditsCorrection) {
// Corrections in credited names according to
// https://www.doblajevideojuegos.es/fichajuego/blade-runner
// Move 280-283 two lines above to accommodate new addition (Early Q actor)
switch (textResourceId) {
case 278:
textStr = "Voces"; // moved two lines above
break;
case 280:
textStr = "McCoy"; // moved two lines above
break;
case 281:
textStr = "^Carlos Salamanca"; // from "^Luis Casanovas" (also moved two lines above)
break;
case 282:
textStr = "Early Q"; // originally uncredited
break;
case 283:
textStr = Common::U32String("^Tino Mart\xedn", Common::kISO8859_1).encode(Common::kDos850); // originally uncredited
break;
case 300:
textStr = Common::U32String("Piernas Locas Larry", Common::kISO8859_1).encode(Common::kDos850); // from "Crazylegs" (use translated name as elsewhere)
break;
case 303:
textStr = "^Antonio Cobos"; // from "^Antonio Fernández" for Chew
break;
case 304:
textStr = "Crystal"; // from "Cristal"
break;
case 311:
textStr = Common::U32String("^Carmen Gamb\xedn", Common::kISO8859_1).encode(Common::kDos850); // from "^María Palacios" for Lucy
break;
case 312:
textStr = "Bob Bala"; // from "Bollet Bob" (use proper translated name)
break;
case 313:
textStr = Common::U32String("^Enrique Jord\xe1", Common::kISO8859_1).encode(Common::kDos850); // from "^Enrique Jorda" for Bullet Bob (accent change)
break;
case 314:
textStr = "Peruana"; // from "Peru Lady"
break;
case 317:
textStr = Common::U32String("^Beatriz Su\xe1rez Cerrato", Common::kISO8859_1).encode(Common::kDos850); // from "^Beatriz Suarez" for Isabella
break;
case 318:
textStr = "Presentadora"; // from "Newscaster" (use translated name as elsewhere)
break;
case 319:
textStr = "^Montse Herranz"; // from "^Montse Pastor" for Presentadora (Newscaster)
break;
case 321:
textStr = Common::U32String("^Beatriz Su\xe1rez Cerrato", Common::kISO8859_1).encode(Common::kDos850); // from "^Beatriz Cerrato" for Contestador (Answering Machine)
break;
default:
break;
}
}
}
void EndCredits::show() {
_vm->_mouse->disable();
_vm->_mixer->stopAll();
_vm->_ambientSounds->removeAllNonLoopingSounds(true);
_vm->_ambientSounds->removeAllLoopingSounds(4u);
_vm->_audioSpeech->stopSpeech();
_vm->_music->play(_vm->_gameInfo->getMusicTrack(kMusicCredits), 100, 0, 2, -1, kMusicLoopPlayOnce, 3);
Font *fontBig = Font::load(_vm, "TAHOMA24.FON", 1, true);
Font *fontSmall = Font::load(_vm, "TAHOMA18.FON", 1, true);
TextResource *textResource = new TextResource(_vm);
textResource->open("ENDCRED");
int textCount = textResource->getCount();
int *textYPositions = new int[textCount]();
int *textXPositions = new int[textCount]();
int y = 480 - fontBig->getFontHeight();
bool small = false;
int textStrWidth = 0;
const int bigToSmallTextYPosDiff = ((fontBig->getFontHeight() - fontSmall->getFontHeight()) / 2);
const int smallestMarginXToCreditName = 6; // px
for (int i = 0; i < textCount; ++i) {
Common::String s = textResource->getText(i);
creditsCheckAndFix(i, s);
if (s.hasPrefix("^")) {
if (!small) {
y += fontBig->getFontHeight();
}
small = false;
textYPositions[i] = y;
textXPositions[i] = 280;
} else {
if (small) {
y += fontSmall->getFontHeight();
} else {
y += fontBig->getFontHeight();
}
small = true;
textYPositions[i] = y + bigToSmallTextYPosDiff;
if (_vm->_language == Common::ES_ESP
&& _vm->_spanishCreditsCorrection
&& i == 277) {
y += 2 * fontSmall->getFontHeight();
}
textStrWidth = fontSmall->getStringWidth(s);
textXPositions[i] = 270 - textStrWidth;
//
// Check here if horizontal alignment of this credit "title"
// may cause clipping off the right edge of the screen.
// Note, that we don't do the same check for a credit "name"
// clipping off the left edge of the screen, as this does not happen
// with the text resources for the credits in the official releases.
// For fan made credits, the new text resources can be designed
// with custom line wrapping and line spacing
// so as to avoid any clipping, so this is no issue there.
if (textXPositions[i] < 0) {
textXPositions[i] = 0;
if (textStrWidth > 280 - smallestMarginXToCreditName
&& (i + 1 < textResource->getCount())) {
Common::String sNext = textResource->getText(i + 1);
if (sNext.hasPrefix("^")) {
// If, for this case, the next string is a credit "name", ie.
// aligned starting from the center (or near the center anyway),
// then insert an extra line to avoid overlap with the title
y += fontSmall->getFontHeight();
}
}
}
}
}
_vm->_vqaIsPlaying = true;
_vm->_vqaStopIsRequested = false;
double position = 0.0;
uint32 timeLast = _vm->_time->currentSystem();
Font *font;
int height;
while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
if (position >= textYPositions[textCount - 1]) {
break;
}
//soundSystem::tick(SoundSystem);
_vm->handleEvents();
if (!_vm->_windowIsActive) {
timeLast = _vm->_time->currentSystem();
continue;
}
uint32 timeNow = _vm->_time->currentSystem();
position += (double)(timeNow - timeLast) * 0.05f; // unsigned difference is intentional
timeLast = timeNow;
_vm->_surfaceFront.fillRect(Common::Rect(BladeRunnerEngine::kOriginalGameWidth, BladeRunnerEngine::kOriginalGameHeight), 0);
for (int i = 0; i < textCount; ++i) {
Common::String s = textResource->getText(i);
creditsCheckAndFix(i, s);
if (s.hasPrefix("^")) {
font = fontBig;
height = fontBig->getFontHeight();
s.deleteChar(0);
} else {
font = fontSmall;
height = fontSmall->getFontHeight();
}
y = textYPositions[i] - (int)position;
if (y < 452 && y + height > fontBig->getFontHeight()) {
font->drawString(&_vm->_surfaceFront, s, textXPositions[i], y, _vm->_surfaceFront.w, 0);
}
}
_vm->_surfaceFront.fillRect(Common::Rect(0, 0, BladeRunnerEngine::kOriginalGameWidth, fontBig->getFontHeight()), 0);
_vm->_surfaceFront.fillRect(Common::Rect(0, BladeRunnerEngine::kOriginalGameHeight - fontBig->getFontHeight(), BladeRunnerEngine::kOriginalGameWidth, BladeRunnerEngine::kOriginalGameHeight), 0);
_vm->blitToScreen(_vm->_surfaceFront);
}
_vm->_vqaIsPlaying = false;
_vm->_vqaStopIsRequested = false;
delete[] textYPositions;
delete[] textXPositions;
delete textResource;
delete fontSmall;
delete fontBig;
_vm->_music->stop(0u);
_vm->_mouse->enable();
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,44 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_END_CREDITS_H
#define BLADERUNNER_UI_END_CREDITS_H
namespace BladeRunner {
class BladeRunnerEngine;
class EndCredits {
BladeRunnerEngine *_vm;
private:
void creditsCheckAndFix(int &textResourceId, Common::String &textStr);
public:
EndCredits(BladeRunnerEngine *vm);
~EndCredits();
void show();
};
} // End of namespace BladeRunner
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_ESPER_H
#define BLADERUNNER_ESPER_H
#include "common/array.h"
#include "common/rect.h"
#include "graphics/surface.h"
namespace BladeRunner {
class BladeRunnerEngine;
class Font;
class Shape;
class Shapes;
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;
int photoId;
int shapeId;
Common::String name;
};
struct Region {
bool isPresent;
int regionId;
Common::Rect rectInner;
Common::Rect rectOuter;
Common::Rect rectSelection;
Common::String name;
};
BladeRunnerEngine *_vm;
ESPERScript *_script;
bool _isWaiting;
bool _isOpen;
UIImagePicker *_buttons;
Graphics::Surface _surfacePhoto;
Graphics::Surface _surfaceViewport;
VQAPlayer *_vqaPlayerMain;
VQAPlayer *_vqaPlayerPhoto;
int _vqaLastFrame;
Shapes *_shapesButtons;
Shapes *_shapesPhotos;
const 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;
uint32 _timeZoomNextDiff;
uint32 _timeZoomNextStart;
bool _isZoomingOut;
uint32 _timeZoomOutNextStart;
Common::Rect _screen;
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 _selection;
Common::Rect _selectionTarget;
Common::Rect _selectionDelta;
int _selectionCrosshairX;
int _selectionCrosshairY;
int _selectionBlinkingCounter;
int _selectionBlinkingStyle;
uint32 _timeSelectionBlinkingNextStart;
int _selectionZoomStep;
uint32 _timeSelectionZoomNextStart;
int _photoOpeningWidth;
int _photoOpeningHeight;
uint32 _timePhotoOpeningNextDiff;
uint32 _timePhotoOpeningNextStart;
bool _isScrolling;
int _scrollingDirection;
uint32 _timeScrollNextStart;
int _soundId1;
int _volume1; // should be in [0, 100]
int _soundId2;
int _volume2; // should be in [0, 100]
int _soundId3;
int _volume3; // should be in [0, 100]
int _ambientVolumeFactorOutsideEsper;
public:
ESPER(BladeRunnerEngine *vm);
~ESPER();
void open(Graphics::Surface *surface);
void close();
bool isOpen() const;
void handleMouseUp(int x, int y, bool buttonLeft);
void handleMouseDown(int x, int y, bool buttonLeft);
void tick();
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(uint32 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 drawMouse(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 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_H
#define BLADERUNNER_KIA_H
#include "common/str.h"
#include "graphics/surface.h"
namespace Common {
struct KeyState;
struct Event;
}
namespace BladeRunner {
class BladeRunnerEngine;
class KIALog;
class KIAScript;
class KIASectionBase;
class KIASectionClues;
class KIASectionDiagnostic;
class KIASectionCrimes;
class KIASectionHelp;
class KIASectionLoad;
class KIASectionSettings;
class KIASectionPogo;
class KIASectionSave;
class KIASectionSuspects;
class Shape;
class Shapes;
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;
struct ActorDialogueQueueEntry {
int actorId;
int sentenceId;
};
BladeRunnerEngine *_vm;
int _transitionId;
uint32 _playerVqaTimeLast;
VQAPlayer *_playerVqaPlayer;
uint32 _playerVqaFrame;
uint32 _playerVisualizerState;
int _playerPhotographId;
Shapes *_playerPhotographs;
int _playerSliceModelId;
float _playerSliceModelAngle;
Graphics::Surface _playerImage;
uint32 _timeLast;
ActorDialogueQueueEntry _playerActorDialogueQueue[kPlayerActorDialogueQueueCapacity];
int _playerActorDialogueQueuePosition;
int _playerActorDialogueQueueSize;
int _playerActorDialogueState;
KIASections _currentSectionId;
KIASections _lastSectionIdKIA;
KIASections _lastSectionIdOptions;
KIASectionBase *_currentSection;
KIASectionClues *_cluesSection;
KIASectionCrimes *_crimesSection;
KIASectionDiagnostic *_diagnosticSection;
KIASectionHelp *_helpSection;
KIASectionLoad *_loadSection;
KIASectionSettings *_settingsSection;
KIASectionPogo *_pogoSection;
KIASectionSave *_saveSection;
KIASectionSuspects *_suspectsSection;
UIImagePicker *_buttons;
VQAPlayer *_mainVqaPlayer;
int _pogoPos;
public:
// Indicates when KIA opens after player has died
// or the game just launched and there are existing saved games to load.
// In forced open mode, certain KIA tabs are not available,
// such as the Save Game tab and the Crime Scene, Suspect, Clue database tabs.
bool _forceOpen;
KIALog *_log;
KIAScript *_script;
Shapes *_shapes;
Graphics::Surface _thumbnail;
public:
KIA(BladeRunnerEngine *vm);
~KIA();
void reset();
void openLastOpened();
void open(KIASections sectionId);
bool isOpen() const;
void tick();
void resume();
void handleMouseDown(int mouseX, int mouseY, bool mainButton);
void handleMouseUp(int mouseX, int mouseY, bool mainButton);
void handleMouseScroll(int mouseX, int mouseY, int direction); // Added by ScummVM team
void handleKeyUp(const Common::KeyState &kbd);
void handleKeyDown(const Common::KeyState &kbd);
void handleCustomEventStop(const Common::Event &evt);
void handleCustomEventStart(const Common::Event &evt);
void playerReset();
void playActorDialogue(int actorId, int sentenceId);
void playSliceModel(int sliceModelId);
void playPhotograph(int photographId);
void playImage(const Graphics::Surface &image);
const char *scrambleSuspectsName(const char *name);
private:
static void mouseDownCallback(int buttonId, void *callbackData);
static void mouseUpCallback(int buttonId, void *callbackData);
static void loopEnded(void *callbackData, int frame, int loopId);
void init();
void unload();
void switchSection(int sectionId);
void createButtons(int sectionId);
void buttonClicked(int buttonId);
const char *getSectionVqaName(int sectionId);
int getVqaLoopMain(int sectionId);
int getVqaLoopTransition(int transitionId);
int getTransitionId(int oldSectionId, int newSectionId);
void playTransitionSound(int transitionId);
void playPrivateAddon();
void playObjectDescription(); // for restored content mode
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,127 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_log.h"
#include "bladerunner/bladerunner.h"
namespace BladeRunner {
KIALog::KIALog(BladeRunnerEngine *vm) {
_vm = vm;
_firstIndex = 0;
_lastIndex = 0;
_currentIndex = 0;
for (int i = 0; i < kSize; ++i) {
_entries[i].dataSize = 0;
_entries[i].data = nullptr;
}
}
KIALog::~KIALog() {
clear();
}
void KIALog::add(int type, int dataSize, const void *data) {
if (_currentIndex == _lastIndex) {
_lastIndex = (_lastIndex + 1) % kSize;
}
if (_entries[_currentIndex].data) {
delete[] _entries[_currentIndex].data;
}
_entries[_currentIndex].type = type;
_entries[_currentIndex].dataSize = dataSize;
if (dataSize > 0) {
unsigned char *dataCopy = new unsigned char[dataSize];
memcpy(dataCopy, data, dataSize);
_entries[_currentIndex].data = dataCopy;
} else {
_entries[_currentIndex].data = nullptr;
}
}
void KIALog::clear() {
_firstIndex = 0;
_lastIndex = 0;
_currentIndex = 0;
for (int i = 0; i < kSize; ++i) {
if (_entries[i].data) {
delete[] _entries[i].data;
}
_entries[i].dataSize = 0;
_entries[i].data = nullptr;
}
}
void KIALog::prev() {
if (_currentIndex != _firstIndex) {
_currentIndex = (_currentIndex - 1) % kSize;
}
}
void KIALog::next() {
if (_currentIndex != _lastIndex) {
_currentIndex = (_currentIndex + 1) % kSize;
}
}
void KIALog::clearFuture() {
_lastIndex = _currentIndex;
int currentIndex = _currentIndex;
while (currentIndex != _firstIndex) {
if (_entries[currentIndex].data) {
delete[] _entries[currentIndex].data;
_entries[currentIndex].data = nullptr;
_entries[currentIndex].dataSize = 0;
}
currentIndex = (currentIndex + 1) % kSize;
}
}
bool KIALog::hasPrev() const {
return _currentIndex != _firstIndex;
}
bool KIALog::hasNext() const {
if (_currentIndex == _lastIndex) {
return false;
}
return (((_currentIndex + 1) % kSize) != _lastIndex);
}
int KIALog::getPrevType() const {
return _entries[(_currentIndex - 1) % kSize].type;
}
int KIALog::getNextType() const {
return _entries[(_currentIndex + 1) % kSize].type;
}
const void *KIALog::getCurrentData() const {
return _entries[_currentIndex].data;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,64 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_LOG_H
#define BLADERUNNER_KIA_LOG_H
namespace BladeRunner {
class BladeRunnerEngine;
class KIALog {
static const int kSize = 16;
struct Entry {
int type;
int dataSize;
const unsigned char *data;
};
BladeRunnerEngine *_vm;
Entry _entries[kSize];
int _firstIndex;
int _lastIndex;
int _currentIndex;
public:
KIALog(BladeRunnerEngine *vm);
~KIALog();
void add(int type, int dataSize, const void *data);
void clear();
void prev();
void next();
void clearFuture();
bool hasPrev() const;
bool hasNext() const;
int getPrevType() const;
int getNextType() const;
const void *getCurrentData() const;
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,34 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_base.h"
namespace BladeRunner {
KIASectionBase::KIASectionBase(BladeRunnerEngine *vm) {
_vm = vm;
_scheduledSwitch = false;
}
KIASectionBase::~KIASectionBase() {
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,73 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_BASE_H
#define BLADERUNNER_KIA_SECTION_BASE_H
#include "common/scummsys.h" // for "override" keyword
namespace Common {
struct KeyState;
struct Event;
}
namespace Graphics {
struct Surface;
}
namespace BladeRunner {
class BladeRunnerEngine;
class KIASectionBase {
protected:
BladeRunnerEngine *_vm;
public:
bool _scheduledSwitch;
KIASectionBase(BladeRunnerEngine *vm);
virtual ~KIASectionBase();
virtual void open() {}
virtual void close() {}
virtual void draw(Graphics::Surface &surface) {}
virtual void handleKeyUp(const Common::KeyState &kbd) {}
virtual void handleKeyDown(const Common::KeyState &kbd) {}
virtual void handleCustomEventStart(const Common::Event &evt) {}
virtual void handleCustomEventStop(const Common::Event &evt) {}
virtual void handleMouseMove(int mouseX, int mouseY) {}
virtual void handleMouseDown(bool mainButton) {}
virtual void handleMouseUp(bool mainButton) {}
virtual void handleMouseScroll(int direction) {} // Added by ScummVM team
protected:
virtual void onButtonPressed(int buttonId) {}
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,445 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_clues.h"
#include "bladerunner/actor_clues.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/crimes_database.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
#include "bladerunner/font.h"
#include "bladerunner/shape.h"
#include "bladerunner/script/kia_script.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/kia_log.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "common/rect.h"
namespace BladeRunner {
KIASectionClues::KIASectionClues(BladeRunnerEngine *vm, ActorClues *clues) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
_isOpen = false;
_debugIntangible = false;
_debugNop = 0;
_clues = clues;
_mouseX = 0;
_mouseY = 0;
_buttons = new UIImagePicker(_vm, 2);
_cluesScrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, kClueCount, 1, false, Common::Rect(312, 172, 500, 376), Common::Rect(506, 160, 506, 394));
_uiContainer->add(_cluesScrollBox);
_filterScrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 128, 1, false, Common::Rect(142, 162, 291, 376), Common::Rect(120, 160, 120, 370));
_uiContainer->add(_filterScrollBox);
_assetTypeFilterCount = 4 + 1; // we have 4 asset types
_crimeFilterCount = _vm->_gameInfo->getCrimeCount() + 1;
_filterCount = _assetTypeFilterCount + _crimeFilterCount;
_filters.resize(_filterCount);
for (int i = 0; i < _filterCount; ++i) {
_filters[i] = true;
}
}
KIASectionClues::~KIASectionClues() {
_uiContainer->clear();
delete _filterScrollBox;
delete _cluesScrollBox;
delete _buttons;
delete _uiContainer;
}
void KIASectionClues::reset() {
_debugIntangible = false;
_debugNop = 0;
_mouseX = 0;
_mouseY = 0;
for (int i = 0; i < _filterCount; ++i) {
_filters[i] = true;
}
}
void KIASectionClues::open() {
_isOpen = true;
_buttons->resetImages();
_buttons->defineImage(0, Common::Rect(142, 380, 191, 395), _vm->_kia->_shapes->get(79), _vm->_kia->_shapes->get(80), _vm->_kia->_shapes->get(81), _vm->_textKIA->getText(30));
_buttons->defineImage(1, Common::Rect(193, 380, 242, 395), _vm->_kia->_shapes->get(76), _vm->_kia->_shapes->get(77), _vm->_kia->_shapes->get(78), _vm->_textKIA->getText(31));
_buttons->activate(nullptr, nullptr, nullptr, mouseUpCallback, this);
_cluesScrollBox->show();
_filterScrollBox->show();
populateFilters();
populateClues();
}
void KIASectionClues::close() {
if (_isOpen) {
_isOpen = false;
_buttons->deactivate();
_cluesScrollBox->hide();
_filterScrollBox->hide();
}
}
void KIASectionClues::draw(Graphics::Surface &surface) {
_uiContainer->draw(surface);
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(0), 300, 162, surface.w, surface.format.RGBToColor(232, 240, 255));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(2), 440, 426, surface.w, surface.format.RGBToColor(80, 96, 136));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(1), 440, 442, surface.w, surface.format.RGBToColor(80, 96, 136));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(4), 440, 458, surface.w, surface.format.RGBToColor(80, 96, 136));
int clueId = _cluesScrollBox->getSelectedLineData();
if (clueId != -1) {
Common::String text;
int actorId = _clues->getFromActorId(clueId);
if (actorId != -1) {
text = _vm->_textActorNames->getText(actorId);
} else {
text.clear();
}
_vm->_mainFont->drawString(&surface, text, 490, 426, surface.w, surface.format.RGBToColor(136, 168, 255));
int crimeId = _vm->_crimesDatabase->getCrime(clueId);
if (crimeId != -1) {
text = _vm->_textCrimes->getText(crimeId);
} else {
text.clear();
}
_vm->_mainFont->drawString(&surface, text, 490, 442, surface.w, surface.format.RGBToColor(136, 168, 255));
int assetType = _vm->_crimesDatabase->getAssetType(clueId);
if (assetType != kClueTypeIntangible) {
text = _vm->_textClueTypes->getText(assetType);
} else {
text.clear();
}
_vm->_mainFont->drawString(&surface, text, 490, 458, surface.w, surface.format.RGBToColor(136, 168, 255));
}
_buttons->draw(surface);
_buttons->drawTooltip(surface, _mouseX, _mouseY);
if (_debugNop) {
_vm->_mainFont->drawString(&surface, Common::String::format("Debug display: %s", _vm->_textActorNames->getText(_debugNop)), 120, 132, surface.w, surface.format.RGBToColor(255, 255, 0));
}
if (_debugIntangible) {
_vm->_mainFont->drawString(&surface, "Debug Mode: Showing intangible clues.", 220, 105, surface.w, surface.format.RGBToColor(255, 255, 0));
}
}
void KIASectionClues::handleMouseMove(int mouseX, int mouseY) {
_mouseX = mouseX;
_mouseY = mouseY;
_buttons->handleMouseAction(mouseX, mouseY, false, false, false);
_uiContainer->handleMouseMove(mouseX, mouseY);
}
void KIASectionClues::handleMouseDown(bool mainButton) {
_uiContainer->handleMouseDown(!mainButton);
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
}
void KIASectionClues::handleMouseUp(bool mainButton) {
_uiContainer->handleMouseUp(!mainButton);
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
}
void KIASectionClues::handleMouseScroll(int direction) {
_uiContainer->handleMouseScroll(direction);
}
void KIASectionClues::saveToLog() {
_vm->_kia->_log->add(0, sizeof(bool) * _filterCount, _filters.data());
}
void KIASectionClues::loadFromLog() {
memcpy(_filters.data(), _vm->_kia->_log->getCurrentData(), sizeof(bool) * _filterCount);
populateFilters();
populateClues();
}
void KIASectionClues::scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionClues *self = (KIASectionClues *)callbackData;
if (source == self->_filterScrollBox && lineData >= 0) {
self->_filters[lineData] = !self->_filters[lineData];
self->_filterScrollBox->toggleCheckBox(lineData);
self->populateClues();
} else if (source == self->_cluesScrollBox && lineData >= 0) {
if (mouseButton) {
if (self->_vm->_gameFlags->query(kFlagKIAPrivacyAddon)) {
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxBEEP15), 70, 0, 0, 50, 0);
if (self->_clues->isPrivate(lineData)) {
self->_clues->setPrivate(lineData, false);
self->_cluesScrollBox->resetFlags(lineData, 0x08);
} else {
self->_clues->setPrivate(lineData, true);
self->_cluesScrollBox->setFlags(lineData, 0x08);
}
}
} else {
self->_clues->setViewed(lineData, true);
self->_cluesScrollBox->resetHighlight(lineData);
self->_vm->_kia->_script->playClueAssetScript(0, lineData);
}
}
}
void KIASectionClues::mouseUpCallback(int buttonId, void *callbackData) {
KIASectionClues *self = (KIASectionClues *)callbackData;
if (buttonId <= 1) {
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxBEEP10A), 100, 0, 0, 50, 0);
}
self->onButtonPressed(buttonId);
}
void KIASectionClues::onButtonPressed(int buttonId) {
if (buttonId == 1) {
disableAllFilters();
}
if (buttonId == 0) {
enableAllFilters();
}
}
void KIASectionClues::enableAllFilters() {
for (int i = 0; i < _filterCount; ++i) {
if (_filterScrollBox->hasLine(i)) {
_filters[i] = true;
}
}
_filterScrollBox->checkAll();
populateClues();
}
void KIASectionClues::disableAllFilters() {
for (int i = 0; i < _filterCount; ++i) {
if (_filterScrollBox->hasLine(i)) {
_filters[i] = false;
}
}
_filterScrollBox->uncheckAll();
populateClues();
}
void KIASectionClues::populateFilters() {
_filterScrollBox->clearLines();
Common::Array<bool> availableFilters(_filterCount);
for (int i = 0; i < _filterCount; ++i) {
availableFilters[i] = false;
}
Common::String assetTypeNames[] = {
_vm->_textKIA->getText(6),
_vm->_textKIA->getText(7),
_vm->_textKIA->getText(8),
_vm->_textKIA->getText(9)
};
for (int i = 0; i < kClueCount; ++i) {
int clueId = i;
if (_clues->isAcquired(clueId)) {
int assetType = _vm->_crimesDatabase->getAssetType(clueId);
int crimeId = _vm->_crimesDatabase->getCrime(clueId);
if (_debugIntangible || assetType != kClueTypeIntangible) {
availableFilters[getLineIdForAssetType(assetType)] = true;
availableFilters[getLineIdForCrimeId(crimeId)] = true;
}
}
}
int assetTypeFiltersAvailable = 0;
for (int i = 0; i < _assetTypeFilterCount; ++i) {
if (availableFilters[i]) {
++assetTypeFiltersAvailable;
}
}
int crimeFiltersAvailable = 0;
for (int i = _assetTypeFilterCount; i < _filterCount; ++i) {
if (availableFilters[i]) {
++crimeFiltersAvailable;
}
}
if (assetTypeFiltersAvailable > 1) {
_filterScrollBox->addLine(_vm->_textKIA->getText(11), -1, 0x04);
for (int i = 0; i < _assetTypeFilterCount; ++i) {
if (availableFilters[i]) {
int flags = 0x01;
if (_filters[i]) {
flags |= 0x02;
}
Common::String text;
int typeTextId = getClueFilterTypeTextId(i);
if (typeTextId == -1) {
text = _vm->_textKIA->getText(10);
} else {
text = assetTypeNames[typeTextId];
}
_filterScrollBox->addLine(text, i, flags);
}
}
}
if (crimeFiltersAvailable > 1) {
if (assetTypeFiltersAvailable > 1) {
_filterScrollBox->addLine(" ", -1, 0);
}
_filterScrollBox->addLine(_vm->_textKIA->getText(12), -1, 0x04);
Common::Array<Line> crimeLines;
crimeLines.reserve(crimeFiltersAvailable);
for (int i = _assetTypeFilterCount; i < _filterCount; ++i) {
if (availableFilters[i]) {
Line line;
line.lineData = i;
line.flags = 0x01;
if (_filters[i]) {
line.flags |= 0x02;
}
int crimeId = getClueFilterCrimeId(i);
if (crimeId == -1) {
line.crimeName = _vm->_textKIA->getText(5);
} else {
line.crimeName = _vm->_textCrimes->getText(crimeId);
}
crimeLines.push_back(line);
}
}
for (int i = 0; i < crimeFiltersAvailable - 1; ++i) {
for (int j = i + 1; j < crimeFiltersAvailable; ++j) {
if (crimeLines[i].lineData != _assetTypeFilterCount) {
if (crimeLines[i].crimeName.compareToIgnoreCase(crimeLines[j].crimeName) <= 0) {
continue;
}
}
SWAP(crimeLines[i], crimeLines[j]);
}
}
for (uint i = 0; i < crimeLines.size(); ++i) {
_filterScrollBox->addLine(crimeLines[i].crimeName, crimeLines[i].lineData, crimeLines[i].flags);
}
}
}
void KIASectionClues::populateClues() {
_cluesScrollBox->clearLines();
for (int i = 0; i < kClueCount; ++i) {
int clueId = i;
if (_clues->isAcquired(clueId)) {
int assetType = _vm->_crimesDatabase->getAssetType(clueId);
int crimeId = _vm->_crimesDatabase->getCrime(clueId);
if (assetType != kClueTypeIntangible || _debugIntangible) {
if (_filters[getLineIdForAssetType(assetType)] && _filters[getLineIdForCrimeId(crimeId)]) {
int flags = 0x30;
#if BLADERUNNER_ORIGINAL_BUGS
if (_clues->isPrivate(clueId)) {
flags = 0x08;
} else if (_clues->isViewed(clueId)) {
flags = 0x10;
}
#else
if (_clues->isPrivate(clueId)) {
flags |= 0x08;
}
if (_clues->isViewed(clueId)) {
flags &= ~0x20;
}
if (_vm->_cutContent && _clues->isSharedWithMainframe(clueId)) {
flags |= 0x40;
}
#endif // BLADERUNNER_ORIGINAL_BUGS
_cluesScrollBox->addLine(_vm->_crimesDatabase->getClueText(clueId), clueId, flags);
}
}
}
}
_cluesScrollBox->sortLines();
}
int KIASectionClues::getClueFilterTypeTextId(int filterId) {
if (filterId) {
return filterId - 1;
}
return -1;
}
int KIASectionClues::getClueFilterCrimeId(int filterId) {
if (filterId != _assetTypeFilterCount) {
return filterId - (_assetTypeFilterCount + 1);
}
return -1;
}
int KIASectionClues::getLineIdForAssetType(int assetType) {
if (assetType == kClueTypeIntangible) {
return 0;
}
return assetType + 1;
}
int KIASectionClues::getLineIdForCrimeId(int crimeId) {
if (crimeId == -1) {
return _assetTypeFilterCount;
}
return _assetTypeFilterCount + crimeId + 1;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,103 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_CLUES_H
#define BLADERUNNER_KIA_SECTION_CLUES_H
#include "bladerunner/ui/kia_section_base.h"
#include "common/array.h"
#include "common/str.h"
namespace BladeRunner {
class ActorClues;
class UIContainer;
class UIImagePicker;
class UIScrollBox;
class KIASectionClues : public KIASectionBase {
static const int kClueCount = 288;
struct Line {
Common::String crimeName;
int lineData;
int flags;
};
UIContainer *_uiContainer;
UIImagePicker *_buttons;
UIScrollBox *_cluesScrollBox;
UIScrollBox *_filterScrollBox;
bool _isOpen;
bool _debugIntangible;
int _debugNop;
ActorClues *_clues;
int _assetTypeFilterCount;
int _crimeFilterCount;
int _filterCount;
Common::Array<bool> _filters;
int _mouseX;
int _mouseY;
public:
KIASectionClues(BladeRunnerEngine *vm, ActorClues *clues);
~KIASectionClues() override;
void reset();
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void saveToLog();
void loadFromLog();
private:
static void scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void mouseUpCallback(int buttonId, void *callbackData);
void onButtonPressed(int buttonId) override;
void enableAllFilters();
void disableAllFilters();
void populateFilters();
void populateClues();
int getClueFilterTypeTextId(int);
int getClueFilterCrimeId(int);
int getLineIdForAssetType(int assetType);
int getLineIdForCrimeId(int crimeId);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,502 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_crimes.h"
#include "bladerunner/actor_clues.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/crimes_database.h"
#include "bladerunner/font.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/script/kia_script.h"
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/kia_log.h"
#include "bladerunner/ui/kia_section_suspects.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "graphics/surface.h"
namespace BladeRunner {
KIASectionCrimes::KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
_isOpen = false;
_clues = clues;
_mouseX = 0;
_mouseY = 0;
_buttons = new UIImagePicker(_vm, 5);
_cluesScrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 50, 1, false, Common::Rect(312, 172, 500, 376), Common::Rect(506, 160, 506, 394));
_uiContainer->add(_cluesScrollBox);
_acquiredClueCount = 0;
for (int i = 0; i < kClueCount; ++i) {
_acquiredClues[i].clueId = -1;
_acquiredClues[i].actorId = -1;
}
_crimeSelected = -1;
_crimesFoundCount = 0;
_crimesFound.resize(_vm->_gameInfo->getCrimeCount());
_suspectSelected = -1;
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
_suspectPhotoShapes = new Shapes(vm);
_suspectsFoundCount = 0;
_suspectsFound.resize(_vm->_gameInfo->getSuspectCount());
_suspectsWithIdentity.resize(_vm->_gameInfo->getSuspectCount());
}
KIASectionCrimes::~KIASectionCrimes() {
delete _suspectPhotoShapes;
_uiContainer->clear();
delete _cluesScrollBox;
delete _buttons;
delete _uiContainer;
}
void KIASectionCrimes::reset() {
_acquiredClueCount = 0;
_crimesFoundCount = 0;
_suspectsFoundCount = 0;
_mouseX = 0;
_mouseY = 0;
_suspectSelected = -1;
_crimeSelected = -1;
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
}
void KIASectionCrimes::open() {
_scheduledSwitch = false;
_suspectPhotoShapes->load("photos.shp");
_buttons->resetImages();
_buttons->defineImage(0, Common::Rect(136, 326, 185, 342), nullptr, _vm->_kia->_shapes->get(32), _vm->_kia->_shapes->get(36), _vm->_textKIA->getText(32));
_buttons->defineImage(1, Common::Rect(218, 326, 269, 342), nullptr, _vm->_kia->_shapes->get(33), _vm->_kia->_shapes->get(37), _vm->_textKIA->getText(33));
_buttons->defineImage(2, Common::Rect(354, 128, 404, 144), nullptr, _vm->_kia->_shapes->get(30), _vm->_kia->_shapes->get(34), _vm->_textKIA->getText(34));
_buttons->defineImage(3, Common::Rect(425, 128, 474, 144), nullptr, _vm->_kia->_shapes->get(31), _vm->_kia->_shapes->get(35), _vm->_textKIA->getText(35));
_buttons->defineImage(4, Common::Rect(142, 150, 260, 297), nullptr, nullptr, nullptr, _vm->_textKIA->getText(36));
_buttons->activate(nullptr, nullptr, nullptr, mouseUpCallback, this);
_cluesScrollBox->show();
populateAcquiredClues();
populateCrimes();
populateSuspects();
populateVisibleClues();
updateSuspectPhoto();
_isOpen = true;
}
void KIASectionCrimes::close() {
if (!_isOpen) {
return;
}
_isOpen = false;
_buttons->deactivate();
_cluesScrollBox->hide();
_suspectPhotoShapes->unload();
}
void KIASectionCrimes::draw(Graphics::Surface &surface) {
const char *text = nullptr;
if (_suspectPhotoShapeId != -1) {
const Shape *shape = _suspectPhotoShapes->get(_suspectPhotoShapeId);
shape->draw(surface, 201 - shape->getWidth() / 2, 223 - shape->getHeight() / 2);
}
if (_suspectPhotoShapeId == 14 || _suspectPhotoShapeId == 13) {
text = _vm->_textKIA->getText(49);
_vm->_mainFont->drawString(&surface, text, 201 - _vm->_mainFont->getStringWidth(text) / 2, 218, surface.w, surface.format.RGBToColor(255, 255, 255));
}
surface.fillRect(Common::Rect(120, 134, 250, 145), 0);
surface.hLine(120, 133, 250, surface.format.RGBToColor(48, 40, 40));
surface.hLine(120, 146, 250, surface.format.RGBToColor(88, 80, 96));
surface.vLine(119, 134, 145, surface.format.RGBToColor(48, 40, 40));
surface.vLine(251, 134, 145, surface.format.RGBToColor(88, 80, 96));
surface.hLine(251, 146, 251, surface.format.RGBToColor(72, 64, 72));
if (_crimeSelected == -1) {
text = _vm->_textKIA->getText(49);
} else {
text = _vm->_textCrimes->getText(_crimeSelected);
}
_vm->_mainFont->drawString(&surface, text, 185 - _vm->_mainFont->getStringWidth(text) / 2, 136, surface.w, surface.format.RGBToColor(136, 168, 255));
surface.fillRect(Common::Rect(136, 304, 266, 315), 0);
surface.hLine(136, 303, 266, surface.format.RGBToColor(48, 40, 40));
surface.hLine(136, 316, 266, surface.format.RGBToColor(88, 80, 96));
surface.vLine(135, 304, 315, surface.format.RGBToColor(48, 40, 40));
surface.vLine(267, 304, 315, surface.format.RGBToColor(88, 80, 96));
surface.hLine(267, 316, 267, surface.format.RGBToColor(72, 64, 72));
Common::String generatedText;
if (_suspectSelected == -1) {
text = _vm->_textKIA->getText(22);
} else {
const char *suspectName = _vm->_suspectsDatabase->get(_suspectSelected)->getName();
if (_suspectsWithIdentity[_suspectSelected]) {
text = suspectName;
} else if (_vm->_suspectsDatabase->get(_suspectSelected)->getSex()) {
generatedText = Common::String::format("%s %s", _vm->_textKIA->getText(20), _vm->_kia->scrambleSuspectsName(suspectName));
text = generatedText.c_str();
} else {
generatedText = Common::String::format("%s %s", _vm->_textKIA->getText(21), _vm->_kia->scrambleSuspectsName(suspectName));
text = generatedText.c_str();
}
}
_vm->_mainFont->drawString(&surface, text, 201 - _vm->_mainFont->getStringWidth(text) / 2, 306, surface.w, surface.format.RGBToColor(136, 168, 255));
_uiContainer->draw(surface);
_buttons->draw(surface);
_buttons->drawTooltip(surface, _mouseX, _mouseY);
}
void KIASectionCrimes::handleMouseMove(int mouseX, int mouseY) {
_mouseX = mouseX;
_mouseY = mouseY;
_buttons->handleMouseAction(mouseX, mouseY, false, false, false);
_uiContainer->handleMouseMove(mouseX, mouseY);
}
void KIASectionCrimes::handleMouseDown(bool mainButton) {
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
_uiContainer->handleMouseDown(!mainButton);
}
void KIASectionCrimes::handleMouseUp(bool mainButton) {
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
_uiContainer->handleMouseUp(!mainButton);
}
void KIASectionCrimes::handleMouseScroll(int direction) {
_uiContainer->handleMouseScroll(direction);
}
void KIASectionCrimes::saveToLog() {
int data[] = { _crimeSelected, _suspectSelected };
_vm->_kia->_log->add(2, sizeof(data), &data);
}
void KIASectionCrimes::loadFromLog() {
const int *data = (const int*)_vm->_kia->_log->getCurrentData();
_crimeSelected = data[0];
_suspectSelected = data[1];
populateSuspects();
populateVisibleClues();
}
void KIASectionCrimes::selectCrime(int crimeId) {
_crimeSelected = crimeId;
populateSuspects();
populateVisibleClues();
updateSuspectPhoto();
}
void KIASectionCrimes::scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionCrimes *self = (KIASectionCrimes *)callbackData;
if (source == self->_cluesScrollBox && lineData >= 0) {
if (mouseButton) {
if (self->_vm->_gameFlags->query(kFlagKIAPrivacyAddon)) {
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxBEEP15), 70, 0, 0, 50, 0);
if (self->_clues->isPrivate(lineData)) {
self->_clues->setPrivate(lineData, false);
self->_cluesScrollBox->resetFlags(lineData, 0x08);
} else {
self->_clues->setPrivate(lineData, true);
self->_cluesScrollBox->setFlags(lineData, 0x08);
}
}
} else {
self->_clues->setViewed(lineData, true);
self->_cluesScrollBox->resetHighlight(lineData);
self->_vm->_kia->_script->playClueAssetScript(0, lineData);
}
}
}
void KIASectionCrimes::mouseUpCallback(int buttonId, void *callbackData) {
((KIASectionCrimes *)callbackData)->onButtonPressed(buttonId);
}
void KIASectionCrimes::onButtonPressed(int buttonId) {
switch (buttonId) {
case 0:
prevSuspect();
break;
case 1:
nextSuspect();
break;
case 2:
prevCrime();
break;
case 3:
nextCrime();
break;
case 4:
if (_suspectSelected != -1) {
_scheduledSwitch = true;
}
break;
}
}
void KIASectionCrimes::populateAcquiredClues() {
_acquiredClueCount = 0;
for (int i = 0; i < kClueCount; ++i) {
int clueId = i;
if (_clues->isAcquired(clueId)) {
_acquiredClues[_acquiredClueCount].clueId = clueId;
_acquiredClues[_acquiredClueCount].actorId = _clues->getFromActorId(clueId);
++_acquiredClueCount;
}
}
// sort clues by name, is it necessary?
}
void KIASectionCrimes::populateCrimes() {
int firstCrime = -1;
int crimeCount = _vm->_gameInfo->getCrimeCount();
for (int i = 0; i < crimeCount; ++i) {
_crimesFound[i] = false;
}
_crimesFoundCount = 0;
if (!_acquiredClueCount) {
return;
}
for (int i = 0; i < crimeCount; ++i) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_crimesDatabase->getCrime(_acquiredClues[j].clueId) == i) {
if (firstCrime == -1) {
firstCrime = i;
}
_crimesFound[i] = true;
++_crimesFoundCount;
}
}
}
if (_crimesFoundCount > 0 && _crimeSelected == -1) {
_crimeSelected = firstCrime;
}
}
void KIASectionCrimes::populateSuspects() {
int firstSuspect = -1;
int suspectCount = _vm->_gameInfo->getSuspectCount();
for (int i = 0; i < suspectCount; ++i) {
_suspectsFound[i] = false;
_suspectsWithIdentity[i] = false;
}
_suspectsFoundCount = 0;
if (!_acquiredClueCount || _crimeSelected == -1) {
return;
}
for (int i = 0; i < suspectCount; ++i) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_crimesDatabase->getCrime(_acquiredClues[j].clueId) == _crimeSelected
&& _vm->_suspectsDatabase->get(i)->hasClue(_acquiredClues[j].clueId)
) {
if (firstSuspect == -1) {
firstSuspect = i;
}
_suspectsFound[i] = true;
++_suspectsFoundCount;
}
}
if (_suspectsFound[i]) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_suspectsDatabase->get(i)->hasIdentityClue(_acquiredClues[j].clueId)) {
_suspectsWithIdentity[i] = true;
}
}
}
}
if (_suspectsFoundCount) {
if (_suspectSelected == -1 || !_suspectsFound[_suspectSelected]) {
_suspectSelected = firstSuspect;
}
} else {
_suspectSelected = -1;
}
}
void KIASectionCrimes::populateVisibleClues() {
_cluesScrollBox->clearLines();
if (_crimeSelected != -1) {
for (int i = 0; i < kClueCount; ++i) {
int clueId = i;
if (_vm->_crimesDatabase->getAssetType(clueId) != kClueTypeIntangible
&& _vm->_crimesDatabase->getCrime(clueId) == _crimeSelected
&& _clues->isAcquired(clueId)
) {
int flags = 0x30;
#if BLADERUNNER_ORIGINAL_BUGS
if (_clues->isPrivate(clueId)) {
flags = 0x08;
} else if (_clues->isViewed(clueId)) {
flags = 0x10;
}
#else
if (_clues->isPrivate(clueId)) {
flags |= 0x08;
}
if (_clues->isViewed(clueId)) {
flags &= ~0x20;
}
if (_vm->_cutContent && _clues->isSharedWithMainframe(clueId)) {
flags |= 0x40;
}
#endif // BLADERUNNER_ORIGINAL_BUGS
_cluesScrollBox->addLine(_vm->_crimesDatabase->getClueText(clueId), clueId, flags);
}
}
_cluesScrollBox->sortLines();
}
}
void KIASectionCrimes::updateSuspectPhoto() {
if (_suspectSelected == -1) {
_suspectPhotoShapeId = -1;
return;
}
SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected);
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
int photoCluesCount = suspect->getPhotoCount();
if (photoCluesCount > 0) {
for (int i = 0 ; i < photoCluesCount; ++i) {
//TODO: weird stuff going on here... original game is using internal clue index instead id
if (_clues->isAcquired(suspect->getPhotoClueId(i))) {
_suspectPhotoShapeId = suspect->getPhotoShapeId(i);
_suspectPhotoNotUsed = suspect->getPhotoNotUsed(i);
break;
}
}
}
if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) {
if (suspect->getSex()) {
_suspectPhotoShapeId = 14;
} else {
_suspectPhotoShapeId = 13;
}
}
}
void KIASectionCrimes::nextCrime() {
if (_crimesFoundCount >= 2) {
while (true) {
++_crimeSelected;
if (_crimeSelected >= (int)_vm->_gameInfo->getCrimeCount()) {
_crimeSelected = 0;
}
if (_crimesFound[_crimeSelected]) {
selectCrime(_crimeSelected);
break;
}
}
}
}
void KIASectionCrimes::prevCrime() {
if (_crimesFoundCount >= 2) {
while (true) {
--_crimeSelected;
if (_crimeSelected < 0) {
_crimeSelected = _vm->_gameInfo->getCrimeCount() - 1;
}
if (_crimesFound[_crimeSelected]) {
selectCrime(_crimeSelected);
break;
}
}
}
}
void KIASectionCrimes::nextSuspect() {
if (_suspectsFoundCount >= 2) {
while (true) {
++_suspectSelected;
if (_suspectSelected >= (int)_vm->_gameInfo->getSuspectCount()) {
_suspectSelected = 0;
}
if (_suspectsFound[_suspectSelected]) {
updateSuspectPhoto();
break;
}
}
}
}
void KIASectionCrimes::prevSuspect() {
if (_suspectsFoundCount >= 2) {
while (true) {
--_suspectSelected;
if (_suspectSelected < 0) {
_suspectSelected = _vm->_gameInfo->getSuspectCount() - 1;
}
if (_suspectsFound[_suspectSelected]) {
updateSuspectPhoto();
break;
}
}
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,119 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_CRIME_H
#define BLADERUNNER_KIA_SECTION_CRIME_H
#include "bladerunner/ui/kia_section_base.h"
#include "common/array.h"
namespace BladeRunner {
class ActorClues;
class BladeRunnerEngine;
class Shapes;
class UIContainer;
class UIImagePicker;
class UIScrollBox;
class KIASectionCrimes : public KIASectionBase {
// _vm->_gameInfo->getClueCount()
static const int kClueCount = 288;
struct AcquiredClue {
int clueId;
int actorId;
};
bool _isOpen;
UIContainer *_uiContainer;
UIImagePicker *_buttons;
UIScrollBox *_cluesScrollBox;
ActorClues *_clues;
int _acquiredClueCount;
AcquiredClue _acquiredClues[kClueCount];
int _crimeSelected;
int _crimesFoundCount;
Common::Array<bool> _crimesFound;
int _suspectsFoundCount;
Common::Array<bool> _suspectsFound;
Common::Array<bool> _suspectsWithIdentity;
int _mouseX;
int _mouseY;
int _suspectPhotoShapeId;
int _suspectPhotoNotUsed;
Shapes *_suspectPhotoShapes;
public:
int _suspectSelected;
public:
KIASectionCrimes(BladeRunnerEngine *vm, ActorClues *clues);
~KIASectionCrimes() override;
void reset();
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void saveToLog();
void loadFromLog();
void selectCrime(int crimeId);
private:
static void scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void mouseUpCallback(int buttonId, void *callbackData);
void onButtonPressed(int buttonId) override;
void populateAcquiredClues();
void populateCrimes();
void populateSuspects();
void populateVisibleClues();
void updateSuspectPhoto();
void nextCrime();
void prevCrime();
void nextSuspect();
void prevSuspect();
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,103 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_diagnostic.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/kia.h"
namespace BladeRunner {
const Color256 KIASectionDiagnostic::kTextColors[] = {
{ 0, 0, 0 },
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 },
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 },
{ 184, 144, 88 },
{ 200, 160, 96 },
{ 216, 184, 112 },
{ 232, 200, 128 },
{ 240, 224, 144 }
};
KIASectionDiagnostic::KIASectionDiagnostic(BladeRunnerEngine *vm) : KIASectionBase(vm) {
_text = nullptr;
_offset = 0;
_timeLast = 0;
}
void KIASectionDiagnostic::open() {
_text = new TextResource(_vm);
if (!_text->open("KIACRED")) {
return;
}
_vm->_kia->playActorDialogue(kActorRunciter, 140);
_offset = 0;
_timeLast = _vm->_time->currentSystem();
}
void KIASectionDiagnostic::close() {
delete _text;
}
void KIASectionDiagnostic::draw(Graphics::Surface &surface) {
uint32 timeNow = _vm->_time->currentSystem();
for (int i = 0; i < _text->getCount(); ++i) {
int y = kLineHeight * i + 366 - _offset;
if (y >= 150 && y < 366) {
int colorIndex = 15;
if (y < 182) {
colorIndex = (y - 150) / 2;
} else if (y >= 334) {
colorIndex = (365 - y) / 2;
}
const char *text = _text->getText(i);
if (text) {
_vm->_mainFont->drawString(&surface, text, 320 - _vm->_mainFont->getStringWidth(text) / 2, y, surface.w, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
}
}
}
// Timing fixed for 60Hz by ScummVM team
// unsigned difference is intentional
if (timeNow - _timeLast > (1000u / 60u)) {
++_offset;
if (_offset > kLineHeight * _text->getCount() + 366) {
_offset = 0;
}
_timeLast = timeNow;
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,51 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_DIAGNOSTIC_H
#define BLADERUNNER_KIA_SECTION_DIAGNOSTIC_H
#include "bladerunner/color.h"
#include "bladerunner/ui/kia_section_base.h"
namespace BladeRunner {
class TextResource;
class KIASectionDiagnostic : public KIASectionBase {
static const Color256 kTextColors[];
static const int kLineHeight = 18;
TextResource *_text;
int _offset;
uint32 _timeLast;
public:
KIASectionDiagnostic(BladeRunnerEngine *vm);
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,96 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_help.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/shape.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_scroll_box.h"
namespace BladeRunner {
KIASectionHelp::KIASectionHelp(BladeRunnerEngine *vm) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
#if BLADERUNNER_ORIGINAL_BUGS
_scrollBox = new UIScrollBox(_vm, nullptr, this, 1024, 0, false, Common::Rect(135, 145, 461, 385), Common::Rect(506, 160, 506, 350));
#else
// Increase width of scollable area, to eliminate the (significant) area to the right,
// before the scroll bar, where scrolling would not work.
_scrollBox = new UIScrollBox(_vm, nullptr, this, 1024, 0, false, Common::Rect(135, 145, 502, 385), Common::Rect(506, 160, 506, 350));
#endif
_uiContainer->add(_scrollBox);
}
KIASectionHelp::~KIASectionHelp() {
_uiContainer->clear();
delete _scrollBox;
delete _uiContainer;
}
void KIASectionHelp::open() {
TextResource textResource(_vm);
if (!textResource.open("HELP")) {
return;
}
_scrollBox->clearLines();
for (int i = 0; i < textResource.getCount(); ++i) {
Common::String textLine = textResource.getText(i);
int flags = 0x04;
if (textLine.firstChar() == ' ') {
flags = 0x00;
}
_scrollBox->addLine(textLine, -1, flags);
}
_scrollBox->show();
}
void KIASectionHelp::close() {
_scrollBox->hide();
}
void KIASectionHelp::draw(Graphics::Surface &surface) {
_vm->_kia->_shapes->get(69)->draw(surface, 501, 123);
_uiContainer->draw(surface);
}
void KIASectionHelp::handleMouseMove(int mouseX, int mouseY) {
_uiContainer->handleMouseMove(mouseX, mouseY);
}
void KIASectionHelp::handleMouseDown(bool mainButton) {
_uiContainer->handleMouseDown(!mainButton);
}
void KIASectionHelp::handleMouseUp(bool mainButton) {
_uiContainer->handleMouseUp(!mainButton);
}
void KIASectionHelp::handleMouseScroll(int direction) {
_uiContainer->handleMouseScroll(direction);
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,53 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_HELP_H
#define BLADERUNNER_KIA_SECTION_HELP_H
#include "bladerunner/ui/kia_section_base.h"
namespace BladeRunner {
class BladeRunnerEngine;
class UIContainer;
class UIScrollBox;
class KIASectionHelp : public KIASectionBase {
UIContainer *_uiContainer;
UIScrollBox *_scrollBox;
public:
KIASectionHelp(BladeRunnerEngine *vm);
~KIASectionHelp() override;
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,183 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_load.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/game_info.h"
#include "bladerunner/savefile.h"
#include "bladerunner/shape.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "common/error.h"
#include "common/system.h"
namespace BladeRunner {
KIASectionLoad::KIASectionLoad(BladeRunnerEngine *vm) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
// There is a small area to the right of the save games list, before the scroll bar,
// where scrolling does not work.
// However, unlike kia_section_help, if we increase the width of the scrollable area here,
// we would noticeably mess with the centering of the title label and the saved game names in the list.
_scrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 1025, 0, true, Common::Rect(155, 158, 461, 346), Common::Rect(506, 160, 506, 350));
_uiContainer->add(_scrollBox);
_timeLast = 0u;
_timeLeft = 0u;
_hoveredLineId = -1;
_displayingLineId = -1;
_newGameEasyLineId = -1;
_newGameMediumLineId = -1;
_newGameHardLineId = -1;
}
KIASectionLoad::~KIASectionLoad() {
_uiContainer->clear();
delete _scrollBox;
delete _uiContainer;
}
void KIASectionLoad::open() {
_scheduledSwitch = false;
_scrollBox->show();
_scrollBox->clearLines();
_saveList = SaveFileManager::list(_vm->getMetaEngine(), _vm->getTargetName());
if (!_saveList.empty()) {
_scrollBox->addLine(_vm->_textOptions->getText(36), -1, 4); // Load game:
for (uint i = 0; i < _saveList.size(); ++i) {
_scrollBox->addLine(_saveList[i].getDescription().encode(Common::kDos850), i, 0);
}
_scrollBox->addLine("", -1, 4);
}
_newGameEasyLineId = _saveList.size();
_newGameMediumLineId = _saveList.size() + 1;
_newGameHardLineId = _saveList.size() + 2;
_scrollBox->addLine(_vm->_textOptions->getText(37), -1, 4); // New game:
_scrollBox->addLine(_vm->_textOptions->getText(20), _newGameEasyLineId, 0); // Easy
_scrollBox->addLine(_vm->_textOptions->getText(28), _newGameMediumLineId, 0); // Medium
_scrollBox->addLine(_vm->_textOptions->getText(29), _newGameHardLineId, 0); // Hard
_hoveredLineId = -1;
_timeLast = _vm->_time->currentSystem();
_timeLeft = 800u;
}
void KIASectionLoad::close() {
_scrollBox->hide();
_vm->_kia->playerReset();
_saveList.clear();
}
void KIASectionLoad::draw(Graphics::Surface &surface) {
_vm->_kia->_shapes->get(69)->draw(surface, 501, 123);
_uiContainer->draw(surface);
int selectedLineId = _scrollBox->getSelectedLineData();
if (_hoveredLineId != selectedLineId) {
if (selectedLineId >= 0 && selectedLineId < (int)_saveList.size() && _displayingLineId != selectedLineId) {
if (_timeLeft == 0u) {
SaveStateDescriptor desc = SaveFileManager::queryMetaInfos(_vm->getMetaEngine(), _vm->getTargetName(), selectedLineId);
const Graphics::Surface *thumbnail = desc.getThumbnail();
if (thumbnail != nullptr) {
_vm->_kia->playImage(*thumbnail);
_displayingLineId = selectedLineId;
}
}
} else {
_vm->_kia->playerReset();
_timeLeft = 800u;
_displayingLineId = -1;
}
_hoveredLineId = selectedLineId;
}
uint32 now = _vm->_time->currentSystem();
if (selectedLineId >= 0 && selectedLineId < (int)_saveList.size() && _displayingLineId != selectedLineId) {
if (_timeLeft) {
uint32 timeDiff = now - _timeLast; // unsigned difference is intentional
if (timeDiff >= _timeLeft) {
SaveStateDescriptor desc = SaveFileManager::queryMetaInfos(_vm->getMetaEngine(), _vm->getTargetName(), _saveList[selectedLineId].getSaveSlot());
const Graphics::Surface *thumbnail = desc.getThumbnail();
if (thumbnail != nullptr) {
_vm->_kia->playImage(*thumbnail);
_displayingLineId = selectedLineId;
}
} else {
_timeLeft = (_timeLeft < timeDiff) ? 0u : (_timeLeft - timeDiff);
}
}
}
_timeLast = now;
}
void KIASectionLoad::handleMouseMove(int mouseX, int mouseY) {
_uiContainer->handleMouseMove(mouseX, mouseY);
}
void KIASectionLoad::handleMouseDown(bool mainButton) {
_uiContainer->handleMouseDown(!mainButton);
}
void KIASectionLoad::handleMouseUp(bool mainButton) {
_uiContainer->handleMouseUp(!mainButton);
}
void KIASectionLoad::handleMouseScroll(int direction) {
_uiContainer->handleMouseScroll(direction);
}
void KIASectionLoad::scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionLoad *self = (KIASectionLoad *)callbackData;
if (mouseButton == 0 && source == self->_scrollBox && lineData >= 0) {
if (lineData == self->_newGameEasyLineId) {
self->_vm->newGame(kGameDifficultyEasy);
} else if (lineData == self->_newGameMediumLineId) {
self->_vm->newGame(kGameDifficultyMedium);
} else if (lineData == self->_newGameHardLineId) {
self->_vm->newGame(kGameDifficultyHard);
} else {
self->_vm->loadGameState(self->_saveList[lineData].getSaveSlot());
}
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
self->_vm->_kia->resume();
self->_scheduledSwitch = true;
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,76 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_LOAD_H
#define BLADERUNNER_KIA_SECTION_LOAD_H
#include "bladerunner/ui/kia_section_base.h"
#include "common/scummsys.h"
#include "common/str.h"
#include "engines/savestate.h"
namespace Graphics {
struct Surface;
}
namespace BladeRunner {
class UIContainer;
class UIScrollBox;
class KIASectionLoad : public KIASectionBase {
UIContainer *_uiContainer;
UIScrollBox *_scrollBox;
uint32 _timeLast;
uint32 _timeLeft;
SaveStateList _saveList;
int _hoveredLineId;
int _displayingLineId;
int _newGameEasyLineId;
int _newGameMediumLineId;
int _newGameHardLineId;
public:
KIASectionLoad(BladeRunnerEngine *vm);
~KIASectionLoad() override;
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
private:
static void scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,292 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_pogo.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/game_constants.h"
namespace BladeRunner {
const Color256 KIASectionPogo::kTextColors[] = {
{ 0, 0, 0 },
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 },
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 },
{ 184, 144, 88 },
{ 200, 160, 96 },
{ 216, 184, 112 },
{ 232, 200, 128 },
{ 240, 224, 144 }
};
const char *KIASectionPogo::kStrings[] = {
"Air Conditioning",
"Amy Shoopman",
"Andy B. and the Milk Carton Kids",
"Area 51",
"Aspirin",
"Babylon 5",
"Bandit",
"Bauer Inline Skates",
"Bill Randolph",
"Bill (Mr. Motorola) and Sarah",
"Boo Berry and Frankenberry",
"Brett W. Sperry",
"Brianhead Ski Resort",
"\"Bubba\"",
"Bubbles",
"Building 2 Parking",
"The Buke",
"Chan \"The Emporer\" Lee",
"Cheezy Poofs",
"Chuck \"Walter\" Karras",
"Cinco De Mayo",
"Club Med",
"Code Complete",
"Coffee Pub, Las Vegas",
"Coke",
"Coin Magic",
"Count Chocula",
"Dad",
"David Arkenstone",
"Digital Camera",
"Direct X Team",
"Denis and Joanne Dyack",
"Blue Bayou, Disneyland",
"Dongle-Boy",
"Doves and Sparrows",
"Dovey",
"Draracles",
"Dry Air",
"Ed Del Castillo",
"Eric \"Kick Ass\" Cartman",
"FHM",
"Fog City Diner",
"Fog Studios",
"Gatorade",
"Gandhi Cuisine of India",
"Giant Lava Lamp",
"Eric and Nancy Gooch",
"Grayford Family",
"Comet Hale-Bopp",
"Joseph B. Hewitt IV",
"Hercules",
"Hillbilly Jeopardy",
"Home Cookin'",
"Hooey Stick",
"The Hypnotist",
"Insects on the Move",
"Intel",
"James Hong",
"Jasmine",
"The Mysterious Cockatiel",
"Joe's Frog",
"\"Jed\"",
"Jeeps",
"\"Jeeter\"",
"Jeff Brown",
"JoeB",
"\"Joe-Bob McClintock\"",
"Joseph Turkel",
"Jose Cuervo",
"Juggling Balls",
"Keith Parkinson",
"Khan",
"King of the Hill",
"Kurt O. and the Toothbrush Squad",
"Leonard and Shirley Legg",
"\"Leroy\"",
"Brion James",
"Louis and his \"friend\"",
"M.C. Crammer and Janie",
"Men's Room Magna-Doodle",
"Mark and Deepti Rowland",
"Metro Pizza, Las Vegas",
"Matt Vella",
"Maui",
"1 Million Candlepower Spotlight",
"Mom",
"Movie-makers",
"Mr. Nonsense",
"\"Needles\"",
"Nerf Weaponry",
"Nimbus",
"Norm Vordahl",
"KNPR",
"Olive Garden",
"Onkyo",
"Orangey",
"Osbur, the Human Resource Manager",
"Our Cheery Friend Leary",
"Ousted Gnome King",
"Pepsi",
"Peta Wilson",
"Pogo the Mockingbird",
"Poker Nights",
"Pirates",
"Playmate Lingerie Calendar",
"Pop-Ice",
"Powerhouse Gym",
"Rade McDowell",
"Red Rock Canyon",
"Refrigeration",
"Rhoda",
"Richard and Kimberly Weier",
"Ridley Scott",
"Ruud the Dude",
"Our old pal Rick Parks",
"Ruby's Diner",
"Savatage",
"Scully and Mulder",
"Sean Young",
"Seinfeld",
"The Shadow",
"\"Shakes\"",
"Shorts",
"Silly Putty",
"The Simpsons",
"Thomas Christensen",
"We love you Steve Wetherill!!!",
"\"Skank\"",
"\"Slice\"",
"SSG",
"Steve and Anne Tall",
"South Park",
"Snap 'n Pops",
"Sneaker",
"Star Wars Trilogy",
"Nonstop Summer Pool Parties",
"Sunsets",
"T-Bone and Angie",
"T-shirts",
"Julio Schembari, Tango Pools",
"The Thermostat Key",
"The Wizard",
"Tomb Raider",
"Tom Elmer II",
"Tujia Linden",
"Turbo",
"Tweeter",
"Twonky",
"Ty and Judy Coon",
"The Courtyard",
"U.F.C.",
"Uli Boehnke",
"\"Virgil\"",
"Virtual Boy",
"Westwood Offroad Excursion Team",
"William Sanderson",
"Xena",
"Zion National Park"
};
KIASectionPogo::KIASectionPogo(BladeRunnerEngine *vm) : KIASectionBase(vm) {
_stringIndex = 0;
_timeLast = 0;
for (int i = 0; i < kStringCount; ++i) {
_strings[i] = nullptr;
}
for (int i = 0; i < kLineCount; ++i) {
_lineTexts[i] = nullptr;
_lineTimeouts[i] = 0;
_lineOffsets[i] = 0;
}
}
void KIASectionPogo::open() {
_stringIndex = 0;
for (int i = 0; i < kStringCount; ++i) {
_strings[i] = kStrings[i];
}
for (int i = 0; i < kStringCount; ++i) {
int j = _vm->_rnd.getRandomNumberRng(i, kStringCount - 1);
SWAP<const char *>(_strings[i], kStrings[j]);
}
for (int i = 0; i < kLineCount; ++i) {
_lineTexts[i] = nullptr;
_lineTimeouts[i] = _vm->_rnd.getRandomNumberRng(0, 63);
_lineOffsets[i] = 0;
}
_timeLast = _vm->_time->currentSystem();
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxAUDLAFF1), 100, 0, 0, 50, 0);
}
void KIASectionPogo::draw(Graphics::Surface &surface) {
// Timing fixed for 60Hz by ScummVM team
uint32 timeNow = _vm->_time->currentSystem();
bool updateTimeout = false;
// unsigned difference is intentional
if (timeNow - _timeLast > (1000u / 60u)) {
updateTimeout = true;
_timeLast = timeNow;
}
const char *title = "We 3 coders give special thanks to:";
_vm->_mainFont->drawString(&surface, title, 313 - _vm->_mainFont->getStringWidth(title) / 2, 143, surface.w, surface.format.RGBToColor(240, 232, 192));
int y = 158;
int lineTextWidth;
for (int i = 0; i < kLineCount; ++i) {
if (updateTimeout) {
if (_lineTimeouts[i] > 0) {
--_lineTimeouts[i];
} else {
_lineTexts[i] = _strings[_stringIndex];
_lineTimeouts[i] = 63;
lineTextWidth = _vm->_mainFont->getStringWidth(_lineTexts[i]);
_lineOffsets[i] = _vm->_rnd.getRandomNumberRng(0, (306 - lineTextWidth) > 0 ? (306 - lineTextWidth) : 0) + 155;
_stringIndex = (_stringIndex + 1) % kStringCount;
}
}
if (_lineTexts[i]) {
int colorIndex = _lineTimeouts[i];
if (colorIndex >= 32) {
colorIndex = 63 - colorIndex;
}
colorIndex /= 2;
_vm->_mainFont->drawString(&surface, _lineTexts[i], _lineOffsets[i], y, surface.w, surface.format.RGBToColor(kTextColors[colorIndex].r, kTextColors[colorIndex].g, kTextColors[colorIndex].b));
}
y += 10;
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,55 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_POGO_H
#define BLADERUNNER_KIA_SECTION_POGO_H
#include "bladerunner/color.h"
#include "bladerunner/ui/kia_section_base.h"
namespace BladeRunner {
class KIASectionPogo : public KIASectionBase {
static const int kStringCount = 158;
static const int kLineCount = 22;
static const char *kStrings[];
static const Color256 kTextColors[];
const char *_strings[kStringCount];
int _stringIndex;
const char *_lineTexts[kLineCount];
int _lineTimeouts[kLineCount];
int _lineOffsets[kLineCount];
uint32 _timeLast;
public:
KIASectionPogo(BladeRunnerEngine *vm);
void open() override;
void draw(Graphics::Surface &surface) override;
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,456 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_save.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/savefile.h"
#include "bladerunner/shape.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_input_box.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "common/error.h"
#include "common/keyboard.h"
#include "common/system.h"
namespace BladeRunner {
KIASectionSave::KIASectionSave(BladeRunnerEngine *vm) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
// There is a small area to the right of the save games list, before the scroll bar,
// where scrolling does not work.
// However, unlike kia_section_help, if we increase the width of the scrollable area here,
// we would noticeably mess with the centering of the title label and the saved game names in the list.
_scrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 1024, 0, true, Common::Rect(155, 158, 461, 346), Common::Rect(506, 160, 506, 350));
_uiContainer->add(_scrollBox);
_inputBox = new UIInputBox(_vm, inputBoxCallback, this, Common::Rect(155, 367, 461, 376), SaveFileManager::kNameLength, ""); // original game has limit 41 characters
_uiContainer->add(_inputBox);
_inputBox->hide();
_buttons = new UIImagePicker(_vm, 3);
_timeLast = 0u;
_timeLeft = 0u;
_state = kStateNormal;
_mouseX = 0;
_mouseY = 0;
_hoveredLineId = -1;
_displayingLineId = -1;
_selectedLineId = -1;
_newSaveLineId = -1;
}
KIASectionSave::~KIASectionSave() {
delete _buttons;
_uiContainer->clear();
delete _inputBox;
delete _scrollBox;
delete _uiContainer;
}
void KIASectionSave::open() {
_scheduledSwitch = false;
_state = kStateNormal;
_buttons->resetImages();
_buttons->defineImage(
0,
Common::Rect(460, 366, 497, 402),
_vm->_kia->_shapes->get(82),
_vm->_kia->_shapes->get(83),
_vm->_kia->_shapes->get(84),
_vm->_textOptions->getText(22) // Save
);
_scrollBox->show();
_saveList = SaveFileManager::list(_vm->getMetaEngine(), _vm->getTargetName());
bool ableToSaveGame = true;
_newSaveLineId = _saveList.size();
if (!_saveList.empty() || ableToSaveGame) {
_buttons->activate(onButtonHovered, nullptr, nullptr, onKSSButtonPressed, this);
_inputBox->show();
_scrollBox->clearLines();
if (ableToSaveGame) {
_scrollBox->addLine(_vm->_textOptions->getText(23), _newSaveLineId, 0);
}
for (uint i = 0; i < _saveList.size(); ++i) {
_scrollBox->addLine(_saveList[i].getDescription().encode(Common::kDos850), i, 0);
}
if (ableToSaveGame) {
// New save
_selectedLineId = _newSaveLineId;
_inputBox->setText("");
} else {
// Overwrite first save
_selectedLineId = 0;
_inputBox->setText(_saveList[_selectedLineId].getDescription());
}
_scrollBox->setFlags(_selectedLineId, 8);
}
_hoveredLineId = -1;
_timeLast = _vm->_time->currentSystem();
_timeLeft = 800u;
}
void KIASectionSave::close() {
_inputBox->hide();
_scrollBox->hide();
_buttons->deactivate();
_vm->_kia->playerReset();
_saveList.clear();
}
void KIASectionSave::draw(Graphics::Surface &surface) {
_vm->_kia->_shapes->get(69)->draw(surface, 501, 123);
_buttons->draw(surface);
if (_state == kStateNormal) {
const char *textChooseSlot = _vm->_textOptions->getText(24); // Choose a slot ...
int textChooseSlotWidth = _vm->_mainFont->getStringWidth(textChooseSlot);
_vm->_mainFont->drawString(&surface, textChooseSlot, 308 - textChooseSlotWidth / 2, 143, surface.w, surface.format.RGBToColor(240, 232, 192));
// Original game shows warnings/error here, but we don't have any
const char *textTypeName = _vm->_textOptions->getText(25); // Type a name ...
int textTypeNameWidth = _vm->_mainFont->getStringWidth(textTypeName);
_vm->_mainFont->drawString(&surface, textTypeName, 308 - textTypeNameWidth / 2, 352, surface.w, surface.format.RGBToColor(240, 232, 192));
_uiContainer->draw(surface);
} else if (_state == kStateOverwrite) {
surface.fillRect(Common::Rect(155, 230, 462, 239), surface.format.RGBToColor(80, 56, 32));
const Common::String &saveName = _saveList[_selectedLineId].getDescription();
int saveNameWidth = _vm->_mainFont->getStringWidth(saveName);
_vm->_mainFont->drawString(&surface, saveName, 308 - saveNameWidth / 2, 230, surface.w, surface.format.RGBToColor(232, 208, 136));
const char *textOverwrite = _vm->_textOptions->getText(35); // Overwrite previously saved game?
int textOverwriteWidth = _vm->_mainFont->getStringWidth(textOverwrite);
_vm->_mainFont->drawString(&surface, textOverwrite, 308 - textOverwriteWidth / 2, 240, surface.w, surface.format.RGBToColor(240, 232, 192));
} else if (_state == kStateDelete) {
surface.fillRect(Common::Rect(155, 230, 462, 239), surface.format.RGBToColor(80, 56, 32));
const Common::String &saveName = _saveList[_selectedLineId].getDescription();
int saveNameWidth = _vm->_mainFont->getStringWidth(saveName); // Delete this game?
_vm->_mainFont->drawString(&surface, saveName, 308 - saveNameWidth / 2, 230, surface.w, surface.format.RGBToColor(232, 208, 136));
const char *textDelete = _vm->_textOptions->getText(40);
int textDeleteWidth = _vm->_mainFont->getStringWidth(textDelete);
_vm->_mainFont->drawString(&surface, textDelete, 308 - textDeleteWidth / 2, 240, surface.w, surface.format.RGBToColor(240, 232, 192));
}
int selectedLineId = _scrollBox->getSelectedLineData();
if (selectedLineId != _hoveredLineId) {
if (selectedLineId >= 0 && selectedLineId < (int)_saveList.size() && _displayingLineId != selectedLineId) {
if (_timeLeft == 0u) {
SaveStateDescriptor desc = SaveFileManager::queryMetaInfos(_vm->getMetaEngine(), _vm->getTargetName(), selectedLineId);
const Graphics::Surface *thumbnail = desc.getThumbnail();
if (thumbnail != nullptr) {
_vm->_kia->playImage(*thumbnail);
_displayingLineId = selectedLineId;
}
}
} else {
_vm->_kia->playerReset();
_timeLeft = 800u;
_displayingLineId = -1;
}
_hoveredLineId = selectedLineId;
}
uint32 now = _vm->_time->currentSystem();
if (selectedLineId >= 0 && selectedLineId < (int)_saveList.size() && _displayingLineId != selectedLineId) {
if (_timeLeft) {
uint32 timeDiff = now - _timeLast; // unsigned difference is intentional
if (timeDiff >= _timeLeft) {
SaveStateDescriptor desc = SaveFileManager::queryMetaInfos(_vm->getMetaEngine(), _vm->getTargetName(), _saveList[selectedLineId].getSaveSlot());
const Graphics::Surface *thumbnail = desc.getThumbnail();
if (thumbnail != nullptr) {
_vm->_kia->playImage(*thumbnail);
_displayingLineId = selectedLineId;
}
} else {
_timeLeft = (_timeLeft < timeDiff) ? 0u : (_timeLeft - timeDiff);
}
}
}
_timeLast = now;
_buttons->drawTooltip(surface, _mouseX, _mouseY);
}
void KIASectionSave::handleKeyUp(const Common::KeyState &kbd) {
if (_state == kStateNormal) {
_uiContainer->handleKeyUp(kbd);
}
}
void KIASectionSave::handleKeyDown(const Common::KeyState &kbd) {
if (_state == kStateNormal) {
_uiContainer->handleKeyDown(kbd);
}
}
void KIASectionSave::handleCustomEventStop(const Common::Event &evt) {
if (_state == kStateNormal) {
_uiContainer->handleCustomEventStop(evt);
}
}
void KIASectionSave::handleCustomEventStart(const Common::Event &evt) {
if (_state == kStateNormal) {
// Delete a saved game entry either with Delete key or numpad's (keypad's) Del key (when Num Lock Off)
if (_selectedLineId != _newSaveLineId
&& evt.customType == BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpDeleteSelectedSvdGame) {
changeState(kStateDelete);
}
_uiContainer->handleCustomEventStart(evt);
} else if (_state == kStateOverwrite) {
if (evt.customType == BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpConfirmDlg) {
save();
changeState(kStateNormal);
}
} else if (_state == kStateDelete) {
if (evt.customType == BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpConfirmDlg) {
deleteSave();
changeState(kStateNormal);
}
}
}
void KIASectionSave::handleMouseMove(int mouseX, int mouseY) {
_mouseX = mouseX;
_mouseY = mouseY;
_buttons->handleMouseAction(_mouseX, _mouseY, false, false, false);
if (_state == kStateNormal) {
_uiContainer->handleMouseMove(_mouseX, _mouseY);
}
}
void KIASectionSave::handleMouseDown(bool mainButton) {
if (mainButton) {
if (_state == kStateNormal) {
_uiContainer->handleMouseDown(false);
}
_buttons->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
}
void KIASectionSave::handleMouseUp(bool mainButton) {
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, false, true, false);
if (_state == kStateNormal) {
_uiContainer->handleMouseUp(false);
}
}
}
void KIASectionSave::handleMouseScroll(int direction) {
if (_state == kStateNormal) {
_uiContainer->handleMouseScroll(direction);
}
}
void KIASectionSave::scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionSave *self = (KIASectionSave *)callbackData;
if (mouseButton == 0 && source == self->_scrollBox && lineData >= 0 && lineData <= (int)self->_saveList.size()) {
self->_scrollBox->resetFlags(self->_selectedLineId, 8);
self->_selectedLineId = lineData;
self->_scrollBox->setFlags(self->_selectedLineId, 8);
if (self->_selectedLineId == self->_newSaveLineId) {
self->_inputBox->setText("");
} else {
self->_inputBox->setText(self->_saveList[self->_selectedLineId].getDescription().encode(Common::kDos850));
}
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP3), 40, 0, 0, 50, 0);
self->_vm->_kia->resume();
}
}
void KIASectionSave::inputBoxCallback(void *callbackData, void *source) {
KIASectionSave *self = (KIASectionSave *)callbackData;
if (source == self->_inputBox) {
if (self->_selectedLineId == self->_newSaveLineId) {
self->save();
} else {
self->changeState(kStateOverwrite);
}
}
}
void KIASectionSave::onButtonHovered(int buttonId, void *callbackData) {
KIASectionSave *self = (KIASectionSave *)callbackData;
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
void KIASectionSave::onKSSButtonPressed(int buttonId, void *callbackData) {
KIASectionSave *self = (KIASectionSave *)callbackData;
if (buttonId == 0) {
if (self->_selectedLineId == self->_newSaveLineId) {
self->save();
} else {
self->changeState(kStateOverwrite);
}
} else if (buttonId == 1) {
self->changeState(kStateNormal);
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP6), 90, -50, -50, 50, 0);
} else if (buttonId == 2) {
if (self->_state == kStateOverwrite) {
self->save();
self->changeState(kStateNormal);
} else if (self->_state == kStateDelete) {
self->deleteSave();
}
}
}
void KIASectionSave::changeState(State state) {
if (_state == state)
return;
_state = state;
if (state == kStateNormal) {
_buttons->resetImages();
_buttons->defineImage(
0,
Common::Rect(460, 366, 497, 402),
_vm->_kia->_shapes->get(82),
_vm->_kia->_shapes->get(83),
_vm->_kia->_shapes->get(84),
_vm->_textOptions->getText(22) // Save
);
} else {
_buttons->resetImages();
_buttons->defineImage(
1,
Common::Rect(318, 260, 357, 299),
_vm->_kia->_shapes->get(126),
_vm->_kia->_shapes->get(127),
_vm->_kia->_shapes->get(128),
_vm->_textOptions->getText(38) // No
);
_buttons->defineImage(
2,
Common::Rect(258, 260, 297, 299),
_vm->_kia->_shapes->get(129),
_vm->_kia->_shapes->get(130),
_vm->_kia->_shapes->get(131),
_vm->_textOptions->getText(39) // Yes
);
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP7), 90, 0, 0, 50, 0);
}
}
void KIASectionSave::save() {
int slot = -1;
// Do not allow empty string for saved game name
// Note: We do allow a series of blank spaces for name, though
// (this is the behavior in the original game).
Common::String inpBoxTextStr = _inputBox->getText();
if (inpBoxTextStr.empty()) {
return;
}
if (_selectedLineId < (int)_saveList.size()) {
slot = _saveList[_selectedLineId].getSaveSlot();
} else {
// Find first available save slot
int maxSlot = -1;
for (int i = 0; i < (int)_saveList.size(); ++i) {
maxSlot = MAX(maxSlot, _saveList[i].getSaveSlot());
if (_saveList[i].getSaveSlot() != i) {
slot = i;
break;
}
}
if (slot == -1) {
slot = maxSlot + 1;
}
}
Common::OutSaveFile *saveFile = BladeRunner::SaveFileManager::openForSaving(_vm->getTargetName(), slot);
if (saveFile == nullptr || saveFile->err()) {
delete saveFile;
warning("KIASectionSave::save(): Can not open savegame file for writing");
return;
}
BladeRunner::SaveFileHeader header;
header._name = Common::U32String(inpBoxTextStr).encode(Common::kUtf8);
header._playTime = _vm->getTotalPlayTime();
BladeRunner::SaveFileManager::writeHeader(*saveFile, header);
_vm->saveGame(*saveFile, &_vm->_kia->_thumbnail);
saveFile->finalize();
delete saveFile;
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
_scheduledSwitch = true;
}
void KIASectionSave::deleteSave() {
BladeRunner::SaveFileManager::remove(_vm->getTargetName(), _saveList[_selectedLineId].getSaveSlot());
close();
open();
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,104 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_SAVE_H
#define BLADERUNNER_KIA_SECTION_SAVE_H
#include "bladerunner/ui/kia_section_base.h"
#include "common/scummsys.h"
#include "common/str.h"
#include "engines/savestate.h"
namespace Graphics {
struct Surface;
}
namespace BladeRunner {
class UIContainer;
class UIScrollBox;
class UIInputBox;
class UIImagePicker;
class KIASectionSave : public KIASectionBase {
enum State {
kStateNormal = 0,
kStateOverwrite = 1,
kStateDelete = 2
};
UIContainer *_uiContainer;
UIScrollBox *_scrollBox;
UIInputBox *_inputBox;
UIImagePicker *_buttons;
uint32 _timeLast;
uint32 _timeLeft;
SaveStateList _saveList;
State _state;
int _mouseX;
int _mouseY;
int _hoveredLineId;
int _displayingLineId;
int _selectedLineId;
int _newSaveLineId;
public:
KIASectionSave(BladeRunnerEngine *vm);
~KIASectionSave() override;
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleKeyUp(const Common::KeyState &kbd) override;
void handleKeyDown(const Common::KeyState &kbd) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void handleCustomEventStart(const Common::Event &evt) override;
void handleCustomEventStop(const Common::Event &evt) override;
private:
static void scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void inputBoxCallback(void *callbackData, void *source);
static void onButtonHovered(int buttonId, void *callbackData);
// NOTE: Renamed the method from onButtonPressed() to onKSSButtonPressed(),
// since this static method hides the virtual method of KIASectionBase (which is not static and has different signature)
static void onKSSButtonPressed(int buttonId, void *callbackData);
void changeState(State state);
void save();
void deleteSave();
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,584 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_settings.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/audio_speech.h"
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
#include "bladerunner/music.h"
#include "bladerunner/settings.h"
#include "bladerunner/shape.h"
#include "bladerunner/subtitles.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_check_box.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_slider.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "bladerunner/ui/ui_dropdown.h"
#include "audio/mixer.h"
#include "common/keyboard.h"
#include "common/debug.h"
namespace BladeRunner {
const char *KIASectionSettings::kLeary = "LEARY";
const Color256 KIASectionSettings::kColors[] = {
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 } // Pressed (framing rectange)
};
KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
: KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
#if BLADERUNNER_ORIGINAL_SETTINGS
_musicVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 160, 460, 170), 101, 0);
_soundEffectVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 185, 460, 195), 101, 0);
_ambientSoundVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 210, 460, 220), 101, 0);
_speechVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 235, 460, 245), 101, 0);
_gammaCorrection = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 260, 460, 270), 101, 0);
#else
_musicVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 160, 460, 170), _vm->_mixer->kMaxMixerVolume, 0);
_soundEffectVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 185, 460, 195), _vm->_mixer->kMaxMixerVolume, 0);
_ambientSoundVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 210, 460, 220), _vm->_mixer->kMaxMixerVolume, 0);
_speechVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 235, 460, 245), _vm->_mixer->kMaxMixerVolume, 0);
#endif
_subtitlesEnable = nullptr;
if (_vm->_language == Common::RU_RUS) {
// expanded click-bounding box x-axis
_directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 436, 374), 0, false);
if (_vm->_subtitles->isSystemActive()) {
// moved to new line
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(276, 376, 345, 386), 0, false);
}
} else {
_directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 270, 374), 0, false);
if (_vm->_subtitles->isSystemActive()) {
// moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(311, 364, 380, 374), 0, false);
}
}
_selectedTextLanguageStr = "";
_selectedTextLanguageId = -1;
_textLanguageDropdown = nullptr;
//
// Section commented out for 2.2.0 release since it's not ready for production
//
//if (_vm->_subtitles->isSystemActive()) {
// // TODO initialize with default values or ConfMan() in open()?
// _selectedTextLanguageStr = "";
// _selectedTextLanguageId = -1;
// // Put at height on top of Music setting
// // This avoids interference and handling the case of when BLADERUNNER_ORIGINAL_SETTINGS is set
// // (in which case the middle part of the KIA screen is filled with controls)
// _textLanguageDropdown = new UIDropDown(_vm,
// dropdownSelectedCallback,
// dropdownCancelledCallback,
// dropdownClickedTopFrameCallback,
// this,
// "",
// 0,
// 136,
// Subtitles::kMaxLanguageSelectionNum);
//}
_playerAgendaSelector = new UIImagePicker(_vm, 5);
_uiContainer->add(_musicVolume);
_uiContainer->add(_soundEffectVolume);
_uiContainer->add(_ambientSoundVolume);
_uiContainer->add(_speechVolume);
#if BLADERUNNER_ORIGINAL_SETTINGS
_uiContainer->add(_gammaCorrection);
#endif
_uiContainer->add(_directorsCut);
if (_vm->_subtitles->isSystemActive()) {
_uiContainer->add(_subtitlesEnable);
// Note: Keep _textLanguageDropdown last added to _uiContainer
// in order to be able to set it as the only active object
// when the language selection dropdown is shown.
//
// commented out for 2.2.0 release since _textLanguageDropdown is not ready for production
//
//_uiContainer->add(_textLanguageDropdown);
}
_state = kStateNormal;
_mouseX = 0;
_mouseY = 0;
_learyPos = 0;
}
KIASectionSettings::~KIASectionSettings() {
delete _uiContainer;
delete _musicVolume;
delete _soundEffectVolume;
delete _ambientSoundVolume;
delete _speechVolume;
#if BLADERUNNER_ORIGINAL_SETTINGS
delete _gammaCorrection;
#endif
delete _directorsCut;
if (_vm->_subtitles->isSystemActive()) {
//
// commented out for 2.2.0 release since _textLanguageDropdown is not ready for production
//
//delete _textLanguageDropdown;
delete _subtitlesEnable;
}
delete _playerAgendaSelector;
}
void KIASectionSettings::open() {
_state = kStateNormal;
_playerAgendaSelector->resetImages();
_playerAgendaSelector->defineImage(kPlayerAgendaPolite, Common::Rect(180, 290, 227, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(30));
_playerAgendaSelector->defineImage(kPlayerAgendaNormal, Common::Rect(238, 290, 285, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(31));
_playerAgendaSelector->defineImage(kPlayerAgendaSurly, Common::Rect(296, 290, 343, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(32));
_playerAgendaSelector->defineImage(kPlayerAgendaErratic, Common::Rect(354, 290, 401, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(33));
_playerAgendaSelector->defineImage(kPlayerAgendaUserChoice, Common::Rect(412, 290, 459, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(34));
initConversationChoices();
_playerAgendaSelector->activate(mouseInCallback, nullptr, nullptr, mouseUpCallback, this);
_directorsCut->enable();
if (_vm->_subtitles->isSystemActive()) {
_subtitlesEnable->enable();
//
// commented out for 2.2.0 release since _textLanguageDropdown is not ready for production
//
//_textLanguageDropdown->activate();
populateLanguageSelection();
}
}
void KIASectionSettings::close() {
_playerAgendaSelector->deactivate();
//
// commented out for 2.2.0 release since _textLanguageDropdown is not ready for production
//
//if (_vm->_subtitles->isSystemActive()) {
// _textLanguageDropdown->deactivate();
//}
}
void KIASectionSettings::draw(Graphics::Surface &surface) {
#if BLADERUNNER_ORIGINAL_SETTINGS
_musicVolume->setValue(_vm->_music->getVolume());
_soundEffectVolume->setValue(_vm->_audioPlayer->getVolume());
_ambientSoundVolume->setValue(_vm->_ambientSounds->getVolume());
_speechVolume->setValue(_vm->_audioSpeech->getVolume());
_gammaCorrection->setValue(100.0f);
#else
_musicVolume->setValue(_vm->_mixer->getVolumeForSoundType(_vm->_mixer->kMusicSoundType));
_soundEffectVolume->setValue(_vm->_mixer->getVolumeForSoundType(_vm->_mixer->kSFXSoundType));
_ambientSoundVolume->setValue(_vm->_mixer->getVolumeForSoundType(_vm->_mixer->kPlainSoundType));
_speechVolume->setValue(_vm->_mixer->getVolumeForSoundType(_vm->_mixer->kSpeechSoundType));
#endif
_directorsCut->setChecked(_vm->_gameFlags->query(kFlagDirectorsCut));
if (_vm->_subtitles->isSystemActive()) {
_subtitlesEnable->setChecked(_vm->isSubtitlesEnabled());
}
const char *textConversationChoices = _vm->_textOptions->getText(0);
const char *textMusic = _vm->_textOptions->getText(2);
const char *textSoundEffects = _vm->_textOptions->getText(3);
const char *textAmbientSound = _vm->_textOptions->getText(4);
const char *textSpeech = _vm->_textOptions->getText(5);
const char *textSoft = _vm->_textOptions->getText(10);
const char *textLoud = _vm->_textOptions->getText(11);
const char *textDesignersCut = _vm->_textOptions->getText(18);
#if BLADERUNNER_ORIGINAL_SETTINGS
const char *textGammaCorrection = _vm->_textOptions->getText(7);
const char *textDark = _vm->_textOptions->getText(14);
const char *textLight = _vm->_textOptions->getText(15);
#endif
int posConversationChoices = 320 - _vm->_mainFont->getStringWidth(textConversationChoices) / 2;
int posMusic = 320 - _vm->_mainFont->getStringWidth(textMusic) / 2;
int posSoundEffects = 320 - _vm->_mainFont->getStringWidth(textSoundEffects) / 2;
int posAmbientSound = 320 - _vm->_mainFont->getStringWidth(textAmbientSound) / 2;
int posSpeech = 320 - _vm->_mainFont->getStringWidth(textSpeech) / 2;
int posSoft = 178 - _vm->_mainFont->getStringWidth(textSoft);
#if BLADERUNNER_ORIGINAL_SETTINGS
int posGammaCorrection = 320 - _vm->_mainFont->getStringWidth(textGammaCorrection) / 2;
int posDark = 178 - _vm->_mainFont->getStringWidth(textDark);
#endif
_playerAgendaSelector->draw(surface);
_vm->_mainFont->drawString(&surface, textConversationChoices, posConversationChoices, 280, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textMusic, posMusic, 150, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textSoft, posSoft, 161, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textLoud, 462, 161, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textSoundEffects, posSoundEffects, 175, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textSoft, posSoft, 186, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textLoud, 462, 186, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textAmbientSound, posAmbientSound, 200, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textSoft, posSoft, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textLoud, 462, 211, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textSpeech, posSpeech, 225, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textSoft, posSoft, 236, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textLoud, 462, 236, surface.w, surface.format.RGBToColor(216, 184, 112));
#if BLADERUNNER_ORIGINAL_SETTINGS
_vm->_mainFont->drawString(&surface, textGammaCorrection, posGammaCorrection, 250, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, textDark, posDark, 261, surface.w, surface.format.RGBToColor(216, 184, 112));
_vm->_mainFont->drawString(&surface, textLight, 462, 261, surface.w, surface.format.RGBToColor(216, 184, 112));
#endif
_vm->_mainFont->drawString(&surface, textDesignersCut, 192, 365, surface.w, surface.format.RGBToColor(232, 208, 136));
if (_vm->_subtitles->isSystemActive()) {
// Allow this to be loading as an extra text item in the resource for text options
const char *subtitlesTranslation = nullptr;
const char *languageSelectTranslation = nullptr;
switch (_vm->_language) {
case Common::EN_ANY:
default:
subtitlesTranslation = "Subtitles";
languageSelectTranslation = "Text Language:";
break;
case Common::DE_DEU:
subtitlesTranslation = "Untertitel";
languageSelectTranslation = "Text Language:"; // TODO DEUTCH translation
break;
case Common::FR_FRA:
subtitlesTranslation = "Sous-titres";
languageSelectTranslation = "Text Language:"; // TODO FRENCH translation
break;
case Common::IT_ITA:
subtitlesTranslation = "Sottotitoli";
languageSelectTranslation = "Text Language:"; // TODO ITALIAN translation
break;
case Common::ES_ESP:
// the spanish text must have accented í
subtitlesTranslation = "Subt\xa1tulos";
languageSelectTranslation = "Text Language:"; // TODO SPANISH translation
break;
case Common::RU_RUS:
// субтитры
if (_vm->_russianCP1251) {
// Patched translation by Siberian Studio is using Windows-1251 encoding
subtitlesTranslation = "\xf1\xf3\xe1\xf2\xe8\xf2\xf0\xfb";
languageSelectTranslation = "Text Language:"; // TODO RUSSIAN translation + proper characters for encoding
} else {
// Original release uses custom encoding
subtitlesTranslation = "CE,NBNHS";
languageSelectTranslation = "Text Language:"; // TODO RUSSIAN translation + proper characters for this encoding
}
break;
}
// +2 to the max of original index of textOptions which is 41
const char *textSubtitles = strcmp(_vm->_textOptions->getText(42), "") == 0 ? subtitlesTranslation : _vm->_textOptions->getText(42);
const char *textLanguageSelect = strcmp(_vm->_textOptions->getText(43), "") == 0 ? languageSelectTranslation : _vm->_textOptions->getText(43);
debug(9, "TODO: Implement _textLanguageDropdown for %s", textLanguageSelect);
if (_vm->_language == Common::RU_RUS) {
// special case for Russian version, put the option in a new line to avoid overlap
_vm->_mainFont->drawString(&surface, textSubtitles, 288, 376, surface.w, surface.format.RGBToColor(232, 208, 136));
} else {
// moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
_vm->_mainFont->drawString(&surface, textSubtitles, 323, 365, surface.w, surface.format.RGBToColor(232, 208, 136));
}
// Vertical Align with "Soft" label
//
// commented out for 2.2.0 release since _textLanguageDropdown is not ready for production
//
//_textLanguageDropdown->setControlLeft(posSoft);
//_textLanguageDropdown->setLabelStr(textLanguageSelect);
}
// Draw uiContainer contained objects after drawing the text on the section for music, sound, speech etc.
_uiContainer->draw(surface);
_playerAgendaSelector->drawTooltip(surface, _mouseX, _mouseY);
}
void KIASectionSettings::handleKeyDown(const Common::KeyState &kbd) {
if (_state == kStateNormal) {
if (toupper(kbd.ascii) != kLeary[_learyPos]) {
_learyPos = 0;
}
if (toupper(kbd.ascii) == kLeary[_learyPos]) {
++_learyPos;
if (!kLeary[_learyPos]) {
_vm->_settings->setLearyMode(!_vm->_settings->getLearyMode());
_learyPos = 0;
initConversationChoices();
}
}
}
}
void KIASectionSettings::handleMouseMove(int mouseX, int mouseY) {
_uiContainer->handleMouseMove(mouseX, mouseY);
_mouseX = mouseX;
_mouseY = mouseY;
_playerAgendaSelector->handleMouseAction(mouseX, mouseY, false, false, false);
}
void KIASectionSettings::handleMouseDown(bool mainButton) {
if (mainButton) {
_uiContainer->handleMouseDown(false);
_playerAgendaSelector->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
}
void KIASectionSettings::handleMouseUp(bool mainButton) {
if (mainButton) {
_uiContainer->handleMouseUp(false);
_playerAgendaSelector->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
}
void KIASectionSettings::handleMouseScroll(int direction) {
if (_vm->_subtitles->isSystemActive() && _state == kStateLanguageSelect) {
_uiContainer->handleMouseScroll(direction);
}
}
void KIASectionSettings::sliderCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
#if BLADERUNNER_ORIGINAL_SETTINGS
if (source == self->_musicVolume) {
self->_vm->_music->setVolume(self->_musicVolume->_value);
self->_vm->_music->playSample();
} else if (source == self->_soundEffectVolume) {
self->_vm->_audioPlayer->setVolume(self->_soundEffectVolume->_value);
self->_vm->_audioPlayer->playSample();
} else if (source == self->_ambientSoundVolume) {
self->_vm->_ambientSounds->setVolume(self->_ambientSoundVolume->_value);
self->_vm->_ambientSounds->playSample();
} else if (source == self->_speechVolume) {
self->_vm->_audioSpeech->setVolume(self->_speechVolume->_value);
self->_vm->_audioSpeech->playSample();
} else if (source == self->_gammaCorrection) {
// TODO: gamma, should we support it?
// gamma = self->_gammaCorrection._value / 100.0f;
// settings::setGamma(&Settings, gamma);
// colorFormat = DirectDrawSurfaces_get_colorFormat();
// Palette_fill(colorFormat);
// Palette_copy(Palette);
// kia::resume(KIA);
}
#else
if (source == self->_musicVolume) {
ConfMan.setInt("music_volume", self->_musicVolume->_value);
self->_vm->syncSoundSettings();
self->_vm->_music->playSample();
} else if (source == self->_soundEffectVolume) {
ConfMan.setInt("sfx_volume", self->_soundEffectVolume->_value);
self->_vm->syncSoundSettings();
self->_vm->_audioPlayer->playSample();
} else if (source == self->_ambientSoundVolume) {
ConfMan.setInt("ambient_volume", self->_ambientSoundVolume->_value);
self->_vm->syncSoundSettings();
self->_vm->_ambientSounds->playSample();
} else if (source == self->_speechVolume) {
ConfMan.setInt("speech_volume", self->_speechVolume->_value);
self->_vm->syncSoundSettings();
self->_vm->_audioSpeech->playSample();
}
#endif
}
void KIASectionSettings::checkBoxCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_directorsCut) {
if (self->_directorsCut->_isChecked) {
self->_vm->_gameFlags->set(kFlagDirectorsCut);
} else {
self->_vm->_gameFlags->reset(kFlagDirectorsCut);
}
} else if (self->_vm->_subtitles->isSystemActive() && source == self->_subtitlesEnable) {
self->_vm->setSubtitlesEnabled(self->_subtitlesEnable->_isChecked);
}
}
void KIASectionSettings::mouseInCallback(int buttonId, void *callbackData) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
void KIASectionSettings::mouseUpCallback(int buttonId, void *callbackData) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
self->onButtonPressed(buttonId);
}
void KIASectionSettings::onButtonPressed(int buttonId) {
switch (buttonId) {
case kPlayerAgendaPolite:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, -30, -30, 50, 0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaPolite);
initConversationChoices();
break;
case kPlayerAgendaNormal:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, -15, -15, 50, 0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaNormal);
initConversationChoices();
break;
case kPlayerAgendaSurly:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaSurly);
initConversationChoices();
break;
case kPlayerAgendaErratic:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 15, 15, 50, 0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaErratic);
initConversationChoices();
break;
case kPlayerAgendaUserChoice:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 30, 30, 50, 0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaUserChoice);
initConversationChoices();
break;
default:
return;
}
}
void KIASectionSettings::initConversationChoices() {
for (int i = 0; i < 5; ++i) {
const Shape *shape = nullptr;
if (_vm->_settings->getPlayerAgenda() == i) {
if (i == kPlayerAgendaUserChoice) {
shape = _vm->_kia->_shapes->get(122);
} else if (_vm->_settings->getLearyMode()) {
shape = _vm->_kia->_shapes->get(106 + i);
} else {
shape = _vm->_kia->_shapes->get(114 + i);
}
} else {
if (i == kPlayerAgendaUserChoice) {
shape = _vm->_kia->_shapes->get(123);
} else if (_vm->_settings->getLearyMode()) {
shape = _vm->_kia->_shapes->get(110 + i);
} else {
shape = _vm->_kia->_shapes->get(118 + i);
}
}
_playerAgendaSelector->setImageShapeUp(i, shape);
_playerAgendaSelector->setImageShapeHovered(i, shape);
_playerAgendaSelector->setImageShapeDown(i, shape);
}
}
void KIASectionSettings::populateLanguageSelection() {
if (_textLanguageDropdown != nullptr) {
_textLanguageDropdown->clearLines();
_textLanguageDropdown->addLine("English v7 [ENG] (SCUMMVM)", 1);
_textLanguageDropdown->addLine("French v7 [FRA] (Kwama57)", 2);
_textLanguageDropdown->addLine("Spanish v7 [ESP] (Victor G. Fraile & GeekOb)", 3);
_textLanguageDropdown->addLine("Greek v1 [ENG] (Praetorian)", 4);
_textLanguageDropdown->addLine("Hebrew v1 [ENG] (Rzil)", 5);
_textLanguageDropdown->addLine("Chinese v0 [ENG] (*)", 6);
_textLanguageDropdown->addLine("Russian v1 [ENG] (*)", 7);
_textLanguageDropdown->addLine("Italian v0 [ITA] (*)", 8);
_textLanguageDropdown->addLine("Deutsch v0 [DEU] (*)", 9);
}
}
void KIASectionSettings::changeState(State state) {
_state = state;
if (state != kStateNormal) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP7), 90, 0, 0, 50, 0);
}
}
void KIASectionSettings::dropdownSelectedCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown && lineData >= 0) {
self->_selectedTextLanguageStr = self->_textLanguageDropdown->getLineSelectedStr();
self->_selectedTextLanguageId = lineData;
self->showTextSelectionDropdown(false);
}
}
void KIASectionSettings::dropdownCancelledCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown) {
self->showTextSelectionDropdown(false);
}
}
void KIASectionSettings::dropdownClickedTopFrameCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown) {
self->showTextSelectionDropdown(!self->_textLanguageDropdown->isDropDownMenuExpanded());
}
}
void KIASectionSettings::showTextSelectionDropdown(bool showToggle) {
if (showToggle) {
// scrollable area will be expanded
populateLanguageSelection();
_uiContainer->setHandleSpecificNumOfTopLayers(1);
changeState(kStateLanguageSelect);
} else {
// scrollable area will be collapsed
_uiContainer->setHandleSpecificNumOfTopLayers(0);
changeState(kStateNormal);
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,110 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_SETTINGS_H
#define BLADERUNNER_KIA_SECTION_SETTINGS_H
#include "bladerunner/bladerunner.h" // for BLADERUNNER_ORIGINAL_SETTINGS macro
#include "bladerunner/color.h"
#include "bladerunner/ui/kia_section_base.h"
#include "common/config-manager.h"
#include "common/rect.h"
namespace BladeRunner {
class BladeRunnerEngine;
class UIContainer;
class UICheckBox;
class UIImagePicker;
class UISlider;
class UIScrollBox;
class UIDropDown;
class KIASectionSettings : public KIASectionBase {
enum State {
kStateNormal = 0,
kStateLanguageSelect = 1
};
static const char *kLeary;
static const Color256 kColors[];
UIContainer *_uiContainer;
UISlider *_musicVolume;
UISlider *_soundEffectVolume;
UISlider *_speechVolume;
UISlider *_ambientSoundVolume;
#if BLADERUNNER_ORIGINAL_SETTINGS
UISlider *_gammaCorrection;
#endif
UICheckBox *_directorsCut;
UICheckBox *_subtitlesEnable;
Common::String _selectedTextLanguageStr;
int _selectedTextLanguageId;
UIDropDown *_textLanguageDropdown;
UIImagePicker *_playerAgendaSelector;
int _mouseX;
int _mouseY;
int _learyPos;
State _state;
public:
KIASectionSettings(BladeRunnerEngine *vm);
~KIASectionSettings() override;
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleKeyDown(const Common::KeyState &kbd) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void showTextSelectionDropdown(bool showToggle);
private:
static void sliderCallback(void *callbackData, void *source);
static void checkBoxCallback(void *callbackData, void *source);
static void mouseInCallback(int buttonId, void *callbackData);
static void mouseUpCallback(int buttonId, void *callbackData);
static void dropdownSelectedCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void dropdownCancelledCallback(void *callbackData, void *source);
static void dropdownClickedTopFrameCallback(void *callbackData, void *source);
void onButtonPressed(int buttonId) override;
void initConversationChoices();
void populateLanguageSelection();
void changeState(State state);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,551 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/kia_section_suspects.h"
#include "bladerunner/actor_clues.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/crimes_database.h"
#include "bladerunner/font.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/script/kia_script.h"
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/kia_log.h"
#include "bladerunner/ui/ui_check_box.h"
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "graphics/surface.h"
namespace BladeRunner {
KIASectionSuspects::KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues) : KIASectionBase(vm) {
_uiContainer = new UIContainer(_vm);
_isOpen = false;
_clues = clues;
_mouseX = 0;
_mouseY = 0;
_whereaboutsFilter = true;
_MOFilter = true;
_replicantFilter = true;
_nonReplicantFilter = true;
_othersFilter = true;
_buttons = new UIImagePicker(_vm, 4);
_whereaboutsCheckBox = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(142, 318, 275, 328), 1, _whereaboutsFilter);
_MOCheckBox = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(142, 328, 275, 338), 1, _MOFilter);
_replicantCheckBox = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(142, 338, 275, 348), 1, _replicantFilter);
_nonReplicantCheckBox = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(142, 348, 275, 358), 1, _nonReplicantFilter);
_othersCheckBox = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(142, 358, 275, 368), 1, _othersFilter);
_cluesScrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, kClueCount, 1, false, Common::Rect(312, 172, 500, 376), Common::Rect(506, 160, 506, 394));
_crimesScrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 50, 1, false, Common::Rect(154, 258, 291, 298), Common::Rect(120, 249, 120, 297));
_uiContainer->add(_whereaboutsCheckBox);
_uiContainer->add(_MOCheckBox);
_uiContainer->add(_replicantCheckBox);
_uiContainer->add(_nonReplicantCheckBox);
_uiContainer->add(_othersCheckBox);
_uiContainer->add(_cluesScrollBox);
_uiContainer->add(_crimesScrollBox);
_acquiredClueCount = 0;
for (int i = 0; i < kClueCount; ++i) {
_acquiredClues[i].clueId = -1;
_acquiredClues[i].actorId = -1;
}
_crimeSelected = -1;
_suspectSelected = -1;
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
_suspectPhotoShapes = new Shapes(vm);
_suspectsFoundCount = 0;
_suspectsFound.resize(_vm->_gameInfo->getSuspectCount());
_suspectsWithIdentity.resize(_vm->_gameInfo->getSuspectCount());
}
KIASectionSuspects::~KIASectionSuspects() {
delete _suspectPhotoShapes;
_uiContainer->clear();
delete _crimesScrollBox;
delete _cluesScrollBox;
delete _othersCheckBox;
delete _nonReplicantCheckBox;
delete _replicantCheckBox;
delete _MOCheckBox;
delete _whereaboutsCheckBox;
delete _buttons;
delete _uiContainer;
}
void KIASectionSuspects::reset() {
_acquiredClueCount = 0;
_suspectsFoundCount = 0;
_mouseX = 0;
_mouseY = 0;
_suspectSelected = -1;
_crimeSelected = -1;
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
_whereaboutsFilter = true;
_MOFilter = true;
_replicantFilter = true;
_nonReplicantFilter = true;
_othersFilter = true;
}
void KIASectionSuspects::open() {
_scheduledSwitch = false;
_suspectPhotoShapes->load("photos.shp");
_buttons->resetImages();
_buttons->defineImage(0, Common::Rect(142, 380, 191, 395), _vm->_kia->_shapes->get(79), _vm->_kia->_shapes->get(80), _vm->_kia->_shapes->get(81), _vm->_textKIA->getText(30));
_buttons->defineImage(1, Common::Rect(193, 380, 242, 395), _vm->_kia->_shapes->get(76), _vm->_kia->_shapes->get(77), _vm->_kia->_shapes->get(77), _vm->_textKIA->getText(31));
_buttons->defineImage(2, Common::Rect(354, 128, 404, 144), nullptr, _vm->_kia->_shapes->get(30), _vm->_kia->_shapes->get(34), _vm->_textKIA->getText(32));
_buttons->defineImage(3, Common::Rect(424, 128, 474, 144), nullptr, _vm->_kia->_shapes->get(31), _vm->_kia->_shapes->get(35), _vm->_textKIA->getText(33));
_buttons->activate(nullptr, nullptr, nullptr, mouseUpCallback, this);
_cluesScrollBox->show();
_crimesScrollBox->show();
_whereaboutsCheckBox->enable();
_MOCheckBox->enable();
_replicantCheckBox->enable();
_nonReplicantCheckBox->enable();
_othersCheckBox->enable();
_cluesScrollBox->show();
_crimesScrollBox->show();
populateAcquiredClues();
populateSuspects();
populateCrimes();
populateVisibleClues();
updateSuspectPhoto();
_isOpen = true;
}
void KIASectionSuspects::close() {
if (!_isOpen) {
return;
}
_isOpen = false;
_buttons->deactivate();
_cluesScrollBox->hide();
_suspectPhotoShapes->unload();
}
void KIASectionSuspects::draw(Graphics::Surface &surface) {
const char *text = nullptr;
if (_suspectPhotoShapeId != -1) {
_suspectPhotoShapes->get(_suspectPhotoShapeId)->draw(surface, 142, 150);
}
if (_suspectPhotoShapeId == 14 || _suspectPhotoShapeId == 13) {
text = _vm->_textKIA->getText(49);
_vm->_mainFont->drawString(&surface, text, 190 - _vm->_mainFont->getStringWidth(text) / 2, 201, surface.w, surface.format.RGBToColor(255, 255, 255));
}
_whereaboutsCheckBox->setChecked(_whereaboutsFilter);
_MOCheckBox->setChecked(_MOFilter);
_replicantCheckBox->setChecked(_replicantFilter);
_nonReplicantCheckBox->setChecked(_nonReplicantFilter);
_othersCheckBox->setChecked(_othersFilter);
_uiContainer->draw(surface);
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(0), 300, 162, surface.w, surface.format.RGBToColor(232, 240, 248));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(46), 142, 248, surface.w, surface.format.RGBToColor(232, 240, 248));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(47), 142, 308, surface.w, surface.format.RGBToColor(232, 240, 248));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(14), 154, 319, surface.w, surface.format.RGBToColor(72, 104, 152));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(15), 154, 329, surface.w, surface.format.RGBToColor(96, 120, 184));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(16), 154, 339, surface.w, surface.format.RGBToColor(112, 144, 216));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(17), 154, 349, surface.w, surface.format.RGBToColor(96, 120, 184));
_vm->_mainFont->drawString(&surface, _vm->_textKIA->getText(48), 154, 359, surface.w, surface.format.RGBToColor(72, 104, 152));
surface.fillRect(Common::Rect(120, 134, 250, 145), 0);
surface.hLine(120, 133, 250, surface.format.RGBToColor(48, 40, 40));
surface.hLine(120, 146, 250, surface.format.RGBToColor(88, 80, 96));
surface.vLine(119, 134, 145, surface.format.RGBToColor(48, 40, 40));
surface.vLine(251, 134, 145, surface.format.RGBToColor(88, 80, 96));
surface.hLine(251, 146, 251, surface.format.RGBToColor(72, 64, 72));
Common::String generatedText;
if (_suspectSelected == -1) {
text = _vm->_textKIA->getText(22);
} else {
const char *suspectName = _vm->_suspectsDatabase->get(_suspectSelected)->getName();
if (_suspectsWithIdentity[_suspectSelected]) {
text = suspectName;
} else if (_vm->_suspectsDatabase->get(_suspectSelected)->getSex()) {
generatedText = Common::String::format("%s %s", _vm->_textKIA->getText(20), _vm->_kia->scrambleSuspectsName(suspectName));
text = generatedText.c_str();
} else {
generatedText = Common::String::format("%s %s", _vm->_textKIA->getText(21), _vm->_kia->scrambleSuspectsName(suspectName));
text = generatedText.c_str();
}
}
_vm->_mainFont->drawString(&surface, text, 185 - _vm->_mainFont->getStringWidth(text) / 2, 136, surface.w, surface.format.RGBToColor(136, 168, 248));
_buttons->draw(surface);
_buttons->drawTooltip(surface, _mouseX, _mouseY);
}
void KIASectionSuspects::handleMouseMove(int mouseX, int mouseY) {
_mouseX = mouseX;
_mouseY = mouseY;
_buttons->handleMouseAction(mouseX, mouseY, false, false, false);
_uiContainer->handleMouseMove(mouseX, mouseY);
}
void KIASectionSuspects::handleMouseDown(bool mainButton) {
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
_uiContainer->handleMouseDown(!mainButton);
}
void KIASectionSuspects::handleMouseUp(bool mainButton) {
if (mainButton) {
_buttons->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
_uiContainer->handleMouseUp(!mainButton);
}
void KIASectionSuspects::handleMouseScroll(int direction) {
_uiContainer->handleMouseScroll(direction);
}
void KIASectionSuspects::saveToLog() {
int data[] = {
_crimeSelected,
_suspectSelected,
_whereaboutsFilter,
_MOFilter,
_replicantFilter,
_nonReplicantFilter,
_othersFilter
};
_vm->_kia->_log->add(1, sizeof(data), &data);
}
void KIASectionSuspects::loadFromLog() {
const int *data = (const int*)_vm->_kia->_log->getCurrentData();
_crimeSelected = data[0];
_suspectSelected = data[1];
_whereaboutsFilter = data[2];
_MOFilter = data[3];
_replicantFilter = data[4];
_nonReplicantFilter = data[5];
_othersFilter = data[6];
populateCrimes();
populateVisibleClues();
}
void KIASectionSuspects::selectSuspect(int suspectId) {
_suspectSelected = suspectId;
populateCrimes();
populateVisibleClues();
updateSuspectPhoto();
}
void KIASectionSuspects::scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionSuspects *self = (KIASectionSuspects *)callbackData;
if (source == self->_cluesScrollBox && lineData >= 0) {
if (mouseButton) {
if (self->_vm->_gameFlags->query(kFlagKIAPrivacyAddon)) {
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxBEEP15), 70, 0, 0, 50, 0);
if (self->_clues->isPrivate(lineData)) {
self->_clues->setPrivate(lineData, false);
self->_cluesScrollBox->resetFlags(lineData, 0x08);
} else {
self->_clues->setPrivate(lineData, true);
self->_cluesScrollBox->setFlags(lineData, 0x08);
}
}
} else {
self->_clues->setViewed(lineData, true);
self->_cluesScrollBox->resetHighlight(lineData);
self->_vm->_kia->_script->playClueAssetScript(0, lineData);
}
} else if (source == self->_crimesScrollBox && lineData >= 0 && !mouseButton) {
self->_crimeSelected = lineData - 5;
self->_scheduledSwitch = true;
}
}
void KIASectionSuspects::checkBoxCallback(void *callbackData, void *source) {
KIASectionSuspects *self = (KIASectionSuspects *)callbackData;
UICheckBox *checkBox = (UICheckBox *)source;
if (checkBox == self->_whereaboutsCheckBox) {
self->_whereaboutsFilter = checkBox->_isChecked;
} else if (checkBox == self->_MOCheckBox) {
self->_MOFilter = checkBox->_isChecked;
} else if (checkBox == self->_replicantCheckBox) {
self->_replicantFilter = checkBox->_isChecked;
} else if (checkBox == self->_nonReplicantCheckBox) {
self->_nonReplicantFilter = checkBox->_isChecked;
} else if (checkBox == self->_othersCheckBox) {
self->_othersFilter = checkBox->_isChecked;
}
self->populateVisibleClues();
}
void KIASectionSuspects::mouseUpCallback(int buttonId, void *callbackData) {
((KIASectionSuspects *)callbackData)->onButtonPressed(buttonId);
}
void KIASectionSuspects::onButtonPressed(int buttonId) {
switch (buttonId) {
case 0:
enableAllFilters();
break;
case 1:
disableAllFilters();
break;
case 2:
prevSuspect();
break;
case 3:
nextSuspect();
break;
}
}
void KIASectionSuspects::populateAcquiredClues() {
_acquiredClueCount = 0;
for (int i = 0; i < kClueCount; ++i) {
int clueId = i;
if (_clues->isAcquired(clueId)) {
_acquiredClues[_acquiredClueCount].clueId = clueId;
_acquiredClues[_acquiredClueCount].actorId = _clues->getFromActorId(clueId);
++_acquiredClueCount;
}
}
// sort clues by name, is it necessary
}
void KIASectionSuspects::populateSuspects() {
int firstSuspect = -1;
int suspectCount = _vm->_gameInfo->getSuspectCount();
for (int i = 0; i < suspectCount; ++i) {
_suspectsFound[i] = false;
_suspectsWithIdentity[i] = false;
}
_suspectsFoundCount = 0;
if (!_acquiredClueCount) {
return;
}
for (int i = 0; i < suspectCount; ++i) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_crimesDatabase->getCrime(_acquiredClues[j].clueId) != -1
&& _vm->_suspectsDatabase->get(i)->hasClue(_acquiredClues[j].clueId)
) {
if (firstSuspect == -1) {
firstSuspect = i;
}
_suspectsFound[i] = true;
++_suspectsFoundCount;
}
}
if (_suspectsFound[i]) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_suspectsDatabase->get(i)->hasIdentityClue(_acquiredClues[j].clueId)) {
_suspectsWithIdentity[i] = true;
}
}
}
}
if (_suspectsFoundCount && _suspectSelected == -1) {
_suspectSelected = firstSuspect;
}
}
void KIASectionSuspects::populateCrimes() {
_crimesScrollBox->clearLines();
if (_suspectsFoundCount > 0 && _suspectSelected != -1) {
for (int i = 0; i < (int)_vm->_gameInfo->getCrimeCount(); ++i) {
for (int j = 0; j < _acquiredClueCount; ++j) {
if (_vm->_crimesDatabase->getCrime(_acquiredClues[j].clueId) == i
&& _vm->_suspectsDatabase->get(_suspectSelected)->hasClue(_acquiredClues[j].clueId)) {
_crimesScrollBox->addLine(_vm->_textCrimes->getText(i), i + 5, 0);
break;
}
}
}
_crimesScrollBox->sortLines();
}
}
void KIASectionSuspects::populateVisibleClues() {
_cluesScrollBox->clearLines();
if (_suspectsFoundCount > 0 && _suspectSelected != -1) {
for (int i = 0; i < _acquiredClueCount; ++i) {
int clueId = _acquiredClues[i].clueId;
if (_vm->_crimesDatabase->getAssetType(clueId) != kClueTypeIntangible) {
SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected);
bool showClue = false;
if (_whereaboutsFilter && suspect->hasWhereaboutsClue(clueId)) {
showClue = true;
} else if (_MOFilter && suspect->hasMOClue(clueId)) {
showClue = true;
} else if (_replicantFilter && suspect->hasReplicantClue(clueId)) {
showClue = true;
} else if (_nonReplicantFilter && suspect->hasNonReplicantClue(clueId)) {
showClue = true;
} else if (_othersFilter && suspect->hasOtherClue(clueId)) {
showClue = true;
}
if (showClue) {
int flags = 0x30;
#if BLADERUNNER_ORIGINAL_BUGS
if (_clues->isPrivate(clueId)) {
flags = 0x08;
} else if (_clues->isViewed(clueId)) {
flags = 0x10;
}
#else
if (_clues->isPrivate(clueId)) {
flags |= 0x08;
}
if (_clues->isViewed(clueId)) {
flags &= ~0x20;
}
if (_vm->_cutContent && _clues->isSharedWithMainframe(clueId)) {
flags |= 0x40;
}
#endif // BLADERUNNER_ORIGINAL_BUGS
_cluesScrollBox->addLine(_vm->_crimesDatabase->getClueText(clueId), clueId, flags);
}
}
}
_cluesScrollBox->sortLines();
}
}
void KIASectionSuspects::updateSuspectPhoto() {
if (_suspectSelected == -1) {
_suspectPhotoShapeId = -1;
return;
}
SuspectDatabaseEntry *suspect = _vm->_suspectsDatabase->get(_suspectSelected);
_suspectPhotoShapeId = -1;
_suspectPhotoNotUsed = -1;
int photoCluesCount = suspect->getPhotoCount();
if (photoCluesCount > 0) {
for (int i = 0 ; i < photoCluesCount; ++i) {
//TODO: weird stuff going on here... original game is using internal clue index instead id
if (_clues->isAcquired(suspect->getPhotoClueId(i))) {
_suspectPhotoShapeId = suspect->getPhotoShapeId(i);
_suspectPhotoNotUsed = suspect->getPhotoNotUsed(i);
break;
}
}
}
if (_suspectPhotoShapeId == -1 && _suspectPhotoNotUsed == -1) {
if (suspect->getSex()) {
_suspectPhotoShapeId = 14;
} else {
_suspectPhotoShapeId = 13;
}
}
}
void KIASectionSuspects::nextSuspect() {
if (_suspectsFoundCount >= 2) {
while (true) {
++_suspectSelected;
if (_suspectSelected >= (int)_vm->_gameInfo->getSuspectCount()) {
_suspectSelected = 0;
}
if (_suspectsFound[_suspectSelected]) {
selectSuspect(_suspectSelected);
break;
}
}
}
}
void KIASectionSuspects::prevSuspect() {
if (_suspectsFoundCount >= 2) {
while (true) {
--_suspectSelected;
if (_suspectSelected < 0) {
_suspectSelected = _vm->_gameInfo->getSuspectCount() - 1;
}
if (_suspectsFound[_suspectSelected]) {
selectSuspect(_suspectSelected);
break;
}
}
}
}
void KIASectionSuspects::enableAllFilters() {
_whereaboutsFilter = true;
_MOFilter = true;
_replicantFilter = true;
_nonReplicantFilter = true;
_othersFilter = true;
populateVisibleClues();
}
void KIASectionSuspects::disableAllFilters() {
_whereaboutsFilter = false;
_MOFilter = false;
_replicantFilter = false;
_nonReplicantFilter = false;
_othersFilter = false;
populateVisibleClues();
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,129 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_KIA_SECTION_SUSPECTS_H
#define BLADERUNNER_KIA_SECTION_SUSPECTS_H
#include "bladerunner/ui/kia_section_base.h"
#include "common/array.h"
namespace BladeRunner {
class ActorClues;
class BladeRunnerEngine;
class Shapes;
class UICheckBox;
class UIContainer;
class UIImagePicker;
class UIScrollBox;
class KIASectionSuspects : public KIASectionBase {
// _vm->_gameInfo->getClueCount()
static const int kClueCount = 288;
struct AcquiredClue {
int clueId;
int actorId;
};
bool _isOpen;
UIContainer *_uiContainer;
UIImagePicker *_buttons;
UIScrollBox *_cluesScrollBox;
UIScrollBox *_crimesScrollBox;
UICheckBox *_whereaboutsCheckBox;
UICheckBox *_MOCheckBox;
UICheckBox *_replicantCheckBox;
UICheckBox *_nonReplicantCheckBox;
UICheckBox *_othersCheckBox;
bool _whereaboutsFilter;
bool _MOFilter;
bool _replicantFilter;
bool _nonReplicantFilter;
bool _othersFilter;
ActorClues *_clues;
int _acquiredClueCount;
AcquiredClue _acquiredClues[kClueCount];
int _suspectSelected;
int _suspectsFoundCount;
Common::Array<bool> _suspectsFound;
Common::Array<bool> _suspectsWithIdentity;
int _mouseX;
int _mouseY;
int _suspectPhotoShapeId;
int _suspectPhotoNotUsed;
Shapes *_suspectPhotoShapes;
public:
int _crimeSelected;
public:
KIASectionSuspects(BladeRunnerEngine *vm, ActorClues *clues);
~KIASectionSuspects() override;
void reset();
void open() override;
void close() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void saveToLog();
void loadFromLog();
void selectSuspect(int suspectId);
private:
static void scrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void checkBoxCallback(void *callbackData, void *source);
static void mouseUpCallback(int buttonId, void *callbackData);
void onButtonPressed(int buttonId) override;
void populateAcquiredClues();
void populateSuspects();
void populateCrimes();
void populateVisibleClues();
void updateSuspectPhoto();
void nextSuspect();
void prevSuspect();
void enableAllFilters();
void disableAllFilters();
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,220 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/scores.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/savefile.h"
#include "bladerunner/scene.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/vqa_player.h"
#include "common/keyboard.h"
namespace BladeRunner {
Scores::Scores(BladeRunnerEngine *vm) {
_vm = vm;
_font = nullptr;
_txtScorers = nullptr;
reset();
}
Scores::~Scores() {
}
void Scores::open() {
if (!_vm->openArchive("MODE.MIX")) {
return;
}
_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SCORE.VQA");
if (!_vqaPlayer->open()) {
return;
}
_vqaPlayer->setLoop(1, -1, 0, nullptr, nullptr);
_vm->_time->pause();
_txtScorers = new TextResource(_vm);
_txtScorers->open("SCORERS");
_font = Font::load(_vm, "TAHOMA24.FON", 1, true);
fill();
_isOpen = true;
_isLoaded = false;
}
bool Scores::isOpen() const {
return _isOpen;
}
void Scores::close() {
_isOpen = false;
delete _font;
_font = nullptr;
delete _txtScorers;
_txtScorers = nullptr;
if (_vqaPlayer) {
_vqaPlayer->close();
delete _vqaPlayer;
_vqaPlayer = nullptr;
}
_vm->closeArchive("MODE.MIX");
_vm->_time->resume();
_vm->_scene->resume();
}
void Scores::set(int index, int value) {
if (value > _scores[index]) {
_scores[index] = value;
}
_lastScoreId = index;
_lastScoreValue = value;
}
void Scores::handleCustomEventStart(const Common::Event &evt) {
close();
}
void Scores::handleKeyDown(const Common::KeyState &kbd) {
close();
}
int Scores::handleMouseUp(int x, int y) {
if (_isLoaded) {
close();
}
_isLoaded = false;
return false;
}
int Scores::handleMouseDown(int x, int y) {
_isLoaded = true;
return false;
}
void Scores::tick() {
if (!_vm->_windowIsActive) {
return;
}
int frame = _vqaPlayer->update();
assert(frame >= -1);
// vqaPlayer renders to _surfaceBack
blit(_vm->_surfaceBack, _vm->_surfaceFront);
_vm->_surfaceFront.hLine(200, 139, 400, _vm->_surfaceFront.format.RGBToColor(0, 248, 0));
_vm->_surfaceFront.hLine(200, 347, 400, _vm->_surfaceFront.format.RGBToColor(0, 0, 248));
_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(7), 200, 114, _vm->_surfaceFront.w, 0);
int y = 140;
for (int i = 0; i < 7; ++i) {
_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(_scorers[i]), 220, y, _vm->_surfaceFront.w, 0);
_font->drawString(&_vm->_surfaceFront, Common::String::format("%d", _scores[_scorers[i]]), 360, y, _vm->_surfaceFront.w, 0);
y += 26;
}
_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(8), 200, 322, _vm->_surfaceFront.w, 0);
_font->drawString(&_vm->_surfaceFront, _txtScorers->getText(_lastScoreId), 220, 348, _vm->_surfaceFront.w, 0);
_font->drawString(&_vm->_surfaceFront, Common::String::format("%d", _lastScoreValue), 360, 348, _vm->_surfaceFront.w, 0);
_vm->blitToScreen(_vm->_surfaceFront);
}
void Scores::fill() {
for (int i = 0; i < 7; ++i) {
_scorers[i] = i;
}
// Network sorting using Bose-Nelson Algorithm
const byte network[32] = { // Bose-Nelson
1,2, 3,4, 5,6,
0,2, 3,5, 4,6,
0,1, 4,5, 2,6,
0,4, 1,5,
0,3, 2,5,
1,3, 2,4,
2,3
};
for (int i = 0; i < 32; i += 2) {
int i1 = network[i], i2 = network[i + 1];
if (_scores[_scorers[i1]] < _scores[_scorers[i2]]) {
SWAP(_scorers[i1], _scorers[i2]);
}
}
}
void Scores::reset() {
_isOpen = false;
_isLoaded = false;
_vqaPlayer = nullptr;
for (int i = 0; i < 7; ++i) {
_scores[i] = -80;
_scorers[i] = 0;
}
_lastScoreId = 0;
_lastScoreValue = 0;
}
void Scores::save(SaveFileWriteStream &f) {
for (int i = 0; i < 7; ++i) {
f.writeInt(_scores[i]);
}
f.writeInt(_lastScoreId);
f.writeInt(_lastScoreValue);
}
void Scores::load(SaveFileReadStream &f) {
for (int i = 0; i < 7; ++i) {
_scores[i] = f.readInt();
}
_lastScoreId = f.readInt();
_lastScoreValue = f.readInt();
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,83 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_SCORES_H
#define BLADERUNNER_SCORES_H
#include "common/array.h"
namespace Common {
struct KeyState;
struct Event;
}
namespace BladeRunner {
class BladeRunnerEngine;
class Font;
class Shape;
class SaveFileReadStream;
class SaveFileWriteStream;
class TextResource;
class VQAPlayer;
class UIImagePicker;
class Scores {
BladeRunnerEngine *_vm;
bool _isOpen;
bool _isLoaded;
VQAPlayer *_vqaPlayer;
int _scores[7];
int _scorers[7];
int _lastScoreId;
int _lastScoreValue;
Font *_font;
TextResource *_txtScorers;
public:
Scores(BladeRunnerEngine *vm);
~Scores();
void open();
bool isOpen() const;
void close();
int query(int index) { return _scores[index]; }
void set(int index, int value);
void handleCustomEventStart(const Common::Event &evt);
void handleKeyDown(const Common::KeyState &kbd);
int handleMouseUp(int x, int y);
int handleMouseDown(int x, int y);
void tick();
void fill();
void reset();
void save(SaveFileWriteStream &f);
void load(SaveFileReadStream &f);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,429 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/spinner.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/actor.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/game_info.h"
#include "bladerunner/subtitles.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/mouse.h"
#include "bladerunner/savefile.h"
#include "bladerunner/scene.h"
#include "bladerunner/shape.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
#include "common/rect.h"
#include "common/system.h"
namespace BladeRunner {
Spinner::Spinner(BladeRunnerEngine *vm) {
_vm = vm;
reset();
_imagePicker = new UIImagePicker(vm, kSpinnerDestinations);
_vqaPlayer = nullptr;
_shapes = new Shapes(vm);
}
Spinner::~Spinner() {
delete _imagePicker;
delete _vqaPlayer;
delete _shapes;
reset();
}
void Spinner::setSelectableDestinationFlag(int destination, bool selectable) {
_isDestinationSelectable[destination] = selectable;
}
bool Spinner::querySelectableDestinationFlag(int destination) const {
return _isDestinationSelectable[destination];
}
int Spinner::chooseDestination(int loopId, bool immediately) {
if (_vm->_cutContent) {
resetDescription();
}
_selectedDestination = 0;
if (!_vm->openArchive("MODE.MIX")) {
return 0;
}
if (loopId < 0) {
// call Spinner:open()
open();
} else {
_vm->playerLosesControl();
_vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately);
while (_vm->_gameIsRunning && !_isOpen) {
_vm->gameTick();
}
_vm->playerGainsControl();
}
_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, "SPINNER.VQA");
if (!_vqaPlayer->open()) {
return 0;
}
_vm->_mouse->setCursor(0);
// Determine which map we need to show to include the active destinations
uint8 mapmask = 0;
uint8 mapmaskv[kSpinnerDestinations] = { 1, 1, 1, 1, 1, 3, 3, 3, 7, 7 };
for (int i = 0; i != kSpinnerDestinations; ++i) {
if (_isDestinationSelectable[i]) {
mapmask |= mapmaskv[i];
}
}
_destinations = nullptr;
int spinnerLoopId = 4;
// mapmask determines which map version will be displayed
// Depending on which destinations are available, mapmaks will have value:
// 1: For the near view (first chapter locations, and animoid row for some reason)
// 3: For medium view locations (includes the near view ones)
// 7: For far view locations (includes all the previous ones)
// This values are determined in mapmaskv table
//
// Since the checks below use bitwise AND, we need to check in order
// from the "far" version to the "near" version
if (mapmask & 4) {
_destinations = getDestinationsFar();
spinnerLoopId = 4;
} else if (mapmask & 2) {
_destinations = getDestinationsMedium();
spinnerLoopId = 2;
} else if (mapmask & 1) {
_destinations = getDestinationsNear();
spinnerLoopId = 0;
} else {
return -1;
}
_vqaPlayer->setLoop(spinnerLoopId, -1, kLoopSetModeImmediate, nullptr, nullptr);
_vqaPlayer->setLoop(spinnerLoopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr);
_shapes->load("SPINNER.SHP");
_imagePicker->resetImages();
for (const Destination *dest = _destinations; dest->id != -1; ++dest) {
if (!_isDestinationSelectable[dest->id]) {
continue;
}
const char *tooltip = _vm->_textSpinnerDestinations->getText(dest->id);
_imagePicker->defineImage(
dest->id,
dest->rect,
_shapes->get(dest->shapeId),
_shapes->get(dest->shapeIdOver),
_shapes->get(dest->shapeIdOver),
tooltip
);
}
if (_vm->_cutContent) {
_imagePicker->activate(
mouseInCallback,
mouseOutCallback,
mouseDownCallback,
mouseUpCallback,
this
);
_vm->_actors[kActorAnsweringMachine]->speechPlay(480, false);
_vm->_ambientSounds->addSound(kSfxSPINAMB2, 5u, 30u, 30, 45, 0, 0, -101, -101, 0, 0);
} else {
_imagePicker->activate(
nullptr,
nullptr,
nullptr,
mouseUpCallback,
this
);
}
_vm->_time->pause();
_selectedDestination = -1;
do {
_vm->gameTick();
} while (_vm->_gameIsRunning && _selectedDestination == -1);
_imagePicker->deactivate();
_shapes->unload();
delete _vqaPlayer;
_vqaPlayer = nullptr;
_vm->closeArchive("MODE.MIX");
_isOpen = false;
_vm->_time->resume();
_vm->_scene->resume();
if (_vm->_cutContent) {
_vm->_ambientSounds->removeNonLoopingSound(kSfxSPINAMB2, true);
}
return _selectedDestination;
}
// cut content
void Spinner::mouseInCallback(int destinationImage, void *self) {
((Spinner *)self)->destinationFocus(destinationImage);
}
// cut content
void Spinner::mouseOutCallback(int, void *self) {
((Spinner *)self)->destinationFocus(-1);
}
// cut content
void Spinner::mouseDownCallback(int, void *self) {
((Spinner *)self)->_vm->_audioPlayer->playAud(((Spinner *)self)->_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP9), 100, 0, 0, 50, 0);
}
void Spinner::mouseUpCallback(int destinationImage, void *self) {
if (destinationImage >= 0 && destinationImage < 10) {
((Spinner *)self)->setSelectedDestination(destinationImage);
}
}
void Spinner::open() {
_isOpen = true;
}
bool Spinner::isOpen() const {
return _isOpen;
}
int Spinner::handleMouseUp(int x, int y) {
_imagePicker->handleMouseAction(x, y, false, true, false);
return false;
}
int Spinner::handleMouseDown(int x, int y) {
_imagePicker->handleMouseAction(x, y, true, false, false);
return false;
}
void Spinner::tick() {
if (!_vm->_windowIsActive) {
return;
}
int frame = _vqaPlayer->update();
assert(frame >= -1);
// vqaPlayer renders to _surfaceBack
blit(_vm->_surfaceBack, _vm->_surfaceFront);
Common::Point p = _vm->getMousePos();
_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
if (_imagePicker->hasHoveredImage()) {
_vm->_mouse->setCursor(1);
} else {
_vm->_mouse->setCursor(0);
}
_imagePicker->draw(_vm->_surfaceFront);
_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
_imagePicker->drawTooltip(_vm->_surfaceFront, p.x, p.y);
if (_vm->_cutContent) {
_vm->_subtitles->tick(_vm->_surfaceFront);
}
_vm->blitToScreen(_vm->_surfaceFront);
if (_vm->_cutContent) {
tickDescription();
}
}
void Spinner::setSelectedDestination(int destination) {
_selectedDestination = destination;
}
void Spinner::reset() {
for (int i = 0; i != kSpinnerDestinations; ++i) {
_isDestinationSelectable[i] = false;
}
_isOpen = false;
_destinations = nullptr;
_selectedDestination = -1;
_imagePicker = nullptr;
_actorId = -1;
_sentenceId = -1;
_timeSpeakDescriptionStart = 0u;
}
void Spinner::resume() {
if (_vqaPlayer == nullptr) {
return;
}
//_vqa->clear();
_vqaPlayer->setLoop(0, -1, kLoopSetModeImmediate, nullptr, nullptr);
tick();
_vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr);
}
void Spinner::save(SaveFileWriteStream &f) {
assert(!_isOpen);
for (int i = 0; i != kSpinnerDestinations; ++i) {
f.writeBool(_isDestinationSelectable[i]);
}
}
void Spinner::load(SaveFileReadStream &f) {
for (int i = 0; i != kSpinnerDestinations; ++i) {
_isDestinationSelectable[i] = f.readBool();
}
}
const Spinner::Destination *Spinner::getDestinationsFar() {
static const Destination destinations[] = {
{ 0, Common::Rect(220, 227, 246, 262), 26 , 36 },
{ 1, Common::Rect(260, 252, 286, 279), 27 , 37 },
{ 2, Common::Rect(286, 178, 302, 196), 28 , 38 },
{ 3, Common::Rect(244, 178, 263, 195), 29 , 39 },
{ 4, Common::Rect(288, 216, 306, 228), 30 , 40 },
{ 5, Common::Rect(249, 77, 353, 124), 31 , 41 },
{ 6, Common::Rect(190, 127, 208, 138), 32 , 42 },
{ 7, Common::Rect(185, 149, 206, 170), 33 , 43 },
{ 8, Common::Rect(398, 249, 419, 268), 34 , 44 },
{ 9, Common::Rect(390, 218, 419, 236), 35 , 45 },
{ -1, Common::Rect(-1, -1, -1, -1), 0, 0 }
};
return destinations;
}
const Spinner::Destination *Spinner::getDestinationsMedium() {
static const Destination destinations[] = {
{ 0, Common::Rect(252, 242, 279, 283), 10, 18 },
{ 1, Common::Rect(301, 273, 328, 304), 11, 19 },
{ 2, Common::Rect(319, 182, 336, 200), 12, 20 },
{ 3, Common::Rect(269, 181, 293, 200), 13, 21 },
{ 4, Common::Rect(325, 227, 345, 240), 14, 22 },
{ 5, Common::Rect(259, 74, 380, 119), 15, 23 },
{ 6, Common::Rect(203, 124, 224, 136), 16, 24 },
{ 7, Common::Rect(200, 147, 222, 170), 17, 25 },
{ -1, Common::Rect(-1,-1,-1,-1), 0, 0 }
};
return destinations;
}
const Spinner::Destination *Spinner::getDestinationsNear() {
static const Destination destinations[] = {
{ 0, Common::Rect(210, 263, 263, 332), 0, 5 },
{ 1, Common::Rect(307, 330, 361, 381), 1, 6 },
{ 2, Common::Rect(338, 137, 362, 169), 2, 7 },
{ 3, Common::Rect(248, 135, 289, 168), 3, 8 },
{ 4, Common::Rect(352, 222, 379, 238), 4, 9 },
{ -1, Common::Rect(-1,-1,-1,-1), 0, 0 }
};
return destinations;
}
void Spinner::destinationFocus(int destinationImage) {
// TODO 590, 600 are "Third Sector", "Fourth Sector" maybe restore those too?
switch (destinationImage) {
case kSpinnerDestinationPoliceStation:
setupDescription(kActorAnsweringMachine, 500);
break;
case kSpinnerDestinationMcCoysApartment:
setupDescription(kActorAnsweringMachine, 510);
break;
case kSpinnerDestinationRuncitersAnimals:
setupDescription(kActorAnsweringMachine, 490);
break;
case kSpinnerDestinationChinatown:
setupDescription(kActorAnsweringMachine, 520);
break;
case kSpinnerDestinationAnimoidRow:
setupDescription(kActorAnsweringMachine, 550);
break;
case kSpinnerDestinationTyrellBuilding:
setupDescription(kActorAnsweringMachine, 560);
break;
case kSpinnerDestinationDNARow:
setupDescription(kActorAnsweringMachine, 530);
break;
case kSpinnerDestinationBradburyBuilding:
setupDescription(kActorAnsweringMachine, 540);
break;
case kSpinnerDestinationNightclubRow:
setupDescription(kActorAnsweringMachine, 570);
break;
case kSpinnerDestinationHysteriaHall:
setupDescription(kActorAnsweringMachine, 580);
break;
default:
resetDescription();
break;
}
}
// copied from elevator.cpp code
void Spinner::setupDescription(int actorId, int sentenceId) {
_actorId = actorId;
_sentenceId = sentenceId;
_timeSpeakDescriptionStart = _vm->_time->current();
}
// copied from elevator.cpp code
void Spinner::resetDescription() {
_actorId = -1;
_sentenceId = -1;
_timeSpeakDescriptionStart = 0u;
}
// copied from elevator.cpp code
void Spinner::tickDescription() {
uint32 now = _vm->_time->current();
// unsigned difference is intentional
if (_actorId <= 0 || (now - _timeSpeakDescriptionStart < 600u)) {
return;
}
if (!_vm->_mouse->isDisabled()) {
// mouse can still move when "disabled", so hover callbacks will work while the cursor is invisible,
// so postpone the speech until mouse is visible again
_vm->_actors[_actorId]->speechPlay(_sentenceId, false);
_actorId = -1;
_sentenceId = -1;
}
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,100 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_SPINNER_H
#define BLADERUNNER_SPINNER_H
#include "common/array.h"
#include "common/rect.h"
namespace BladeRunner {
class BladeRunnerEngine;
class SaveFileReadStream;
class SaveFileWriteStream;
class Shapes;
class UIImagePicker;
class VQAPlayer;
class Spinner {
static const int kSpinnerDestinations = 10;
struct Destination {
int id;
Common::Rect rect;
int shapeId;
int shapeIdOver;
};
BladeRunnerEngine *_vm;
bool _isDestinationSelectable[kSpinnerDestinations];
bool _isOpen;
VQAPlayer *_vqaPlayer;
const Destination *_destinations;
int _selectedDestination;
Shapes *_shapes;
UIImagePicker *_imagePicker;
int _actorId;
int _sentenceId;
uint32 _timeSpeakDescriptionStart;
public:
Spinner(BladeRunnerEngine *vm);
~Spinner();
void setSelectableDestinationFlag(int destination, bool selectable);
bool querySelectableDestinationFlag(int destination) const;
int chooseDestination(int vqaLoopId, bool immediately);
void open();
bool isOpen() const;
int handleMouseUp(int x, int y);
int handleMouseDown(int x, int y);
void tick();
void setSelectedDestination(int destination);
void reset();
void resume();
void destinationFocus(int destination);
void setupDescription(int actorId, int sentenceId);
void resetDescription();
void tickDescription();
void save(SaveFileWriteStream &f);
void load(SaveFileReadStream &f);
private:
static void mouseInCallback(int, void *);
static void mouseOutCallback(int, void *);
static void mouseDownCallback(int, void *);
static void mouseUpCallback(int, void *);
static const Destination *getDestinationsFar();
static const Destination *getDestinationsMedium();
static const Destination *getDestinationsNear();
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,144 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_check_box.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/kia.h"
namespace BladeRunner {
UICheckBox::UICheckBox(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int style, bool isChecked)
: UIComponent(vm) {
_valueChangedCallback = valueChangedCallback;
_callbackData = callbackData;
_isEnabled = true;
_hasFocus = false;
_isPressed = false;
_style = style;
if (isChecked) {
_frame = 5u;
} else {
_frame = 0u;
}
_timeLast = _vm->_time->currentSystem();
_rect = rect;
_isChecked = isChecked;
}
void UICheckBox::draw(Graphics::Surface &surface) {
if (_rect.right > _rect.left && _rect.bottom > _rect.top) {
uint32 shapeId;
uint32 timeNow = _vm->_time->currentSystem();
// unsigned difference is intentional
if (timeNow - _timeLast > 67u) {
// unsigned difference is intentional
uint32 frameDelta = (timeNow - _timeLast) / 67u;
_timeLast = timeNow;
if (_isChecked) {
_frame = MIN<uint32>(_frame + frameDelta, 5u);
} else {
_frame = (_frame < frameDelta) ? 0 : MAX<uint32>(_frame - frameDelta, 0u);
}
}
if (_style) {
if (_frame || (_hasFocus && !_isPressed && _isEnabled)) {
if (_frame != 5u || (_hasFocus && !_isPressed && _isEnabled)) {
shapeId = _frame + 54u;
} else {
shapeId = 53u;
}
} else {
shapeId = 52u;
}
} else {
if (_frame || (_hasFocus && !_isPressed && _isEnabled)) {
if (_frame != 5u || (_hasFocus && !_isPressed && _isEnabled)) {
shapeId = _frame + 62u;
} else {
shapeId = 61u;
}
} else {
shapeId = 60u;
}
}
_vm->_kia->_shapes->get(shapeId)->draw(surface, _rect.left, _rect.top + 1);
}
}
void UICheckBox::enable() {
_isEnabled = true;
_isPressed = false;
_hasFocus = false;
}
void UICheckBox::disable() {
_isEnabled = false;
}
void UICheckBox::setChecked(bool isChecked) {
_isChecked = isChecked;
}
void UICheckBox::handleMouseMove(int mouseX, int mouseY) {
if (_rect.contains(mouseX, mouseY)) {
if (!_hasFocus && _isEnabled && !_isPressed ) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
_hasFocus = true;
} else {
_hasFocus = false;
}
}
void UICheckBox::handleMouseDown(bool alternateButton) {
if (!alternateButton) {
if (_isEnabled && _hasFocus) {
_isChecked = !_isChecked;
if (_valueChangedCallback) {
_valueChangedCallback(_callbackData, this);
}
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxBEEP10), 100, 0, 0, 50, 0);
} else {
_isPressed = true;
}
}
}
void UICheckBox::handleMouseUp(bool alternateButton) {
_isPressed = false;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,61 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_CHECKBOX_H
#define BLADERUNNER_UI_CHECKBOX_H
#include "bladerunner/ui/ui_component.h"
#include "common/rect.h"
namespace BladeRunner {
class UICheckBox : public UIComponent {
UIComponentCallback *_valueChangedCallback;
void *_callbackData;
int _style;
int _isEnabled;
Common::Rect _rect;
uint32 _frame;
int _isPressed;
uint32 _timeLast;
int _hasFocus;
public:
bool _isChecked;
UICheckBox(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int style, bool isChecked);
void draw(Graphics::Surface &surface) override;
void enable();
void disable();
void setChecked(bool isChecked);
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,65 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_COMPONENT_H
#define BLADERUNNER_UI_COMPONENT_H
namespace Common{
struct KeyState;
struct Event;
}
namespace Graphics {
struct Surface;
}
namespace BladeRunner {
class BladeRunnerEngine;
typedef void UIComponentCallback(void *callbackData, void *source);
class UIComponent {
protected:
BladeRunnerEngine *_vm;
public:
UIComponent(BladeRunnerEngine *vm) {
_vm = vm;
}
virtual ~UIComponent() {}
virtual void draw(Graphics::Surface &surface) {}
virtual void handleMouseMove(int mouseX, int mouseY) {}
virtual void handleMouseDown(bool alternateButton) {}
virtual void handleMouseUp(bool alternateButton) {}
virtual void handleMouseScroll(int direction) {} // Added by ScummVM team
virtual void handleKeyUp(const Common::KeyState &kbd) {}
virtual void handleKeyDown(const Common::KeyState &kbd) {}
virtual void handleCustomEventStop(const Common::Event &evt) {}
virtual void handleCustomEventStart(const Common::Event &evt) {}
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,178 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_container.h"
#include "common/keyboard.h"
namespace BladeRunner {
void UIContainer::draw(Graphics::Surface &surface) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->draw(surface);
}
}
void UIContainer::handleMouseMove(int mouseX, int mouseY) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseMove(mouseX, mouseY);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseMove(mouseX, mouseY);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseDown(bool alternateButton) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseDown(alternateButton);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseDown(alternateButton);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseUp(bool alternateButton) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseUp(alternateButton);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseUp(alternateButton);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseScroll(int direction) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseScroll(direction);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseScroll(direction);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleKeyUp(const Common::KeyState &kbd) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyUp(kbd);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleKeyUp(kbd);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleKeyDown(const Common::KeyState &kbd) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyDown(kbd);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleKeyDown(kbd);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleCustomEventStop(const Common::Event &evt) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleCustomEventStop(evt);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleCustomEventStop(evt);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleCustomEventStart(const Common::Event &evt) {
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleCustomEventStart(evt);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleCustomEventStart(evt);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::add(UIComponent *component) {
_components.push_back(component);
}
void UIContainer::clear() {
_components.clear();
_handleSpecificNumOfTopLayers = 0;
}
void UIContainer::setHandleSpecificNumOfTopLayers(int count) {
_handleSpecificNumOfTopLayers = count;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,63 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_CONTAINER_H
#define BLADERUNNER_UI_CONTAINER_H
#include "bladerunner/ui/ui_component.h"
#include "common/array.h"
namespace BladeRunner {
class UIComponent;
class UIContainer : public UIComponent {
Common::Array<UIComponent*> _components;
int _handleSpecificNumOfTopLayers;
public:
UIContainer(BladeRunnerEngine *vm) : UIComponent(vm) {
_handleSpecificNumOfTopLayers = 0;
}
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
void handleMouseScroll(int direction) override; // Added by ScummVM team
void handleKeyUp(const Common::KeyState &kbd) override;
void handleKeyDown(const Common::KeyState &kbd) override;
void handleCustomEventStop(const Common::Event &evt) override;
void handleCustomEventStart(const Common::Event &evt) override;
void add(UIComponent *component);
void clear();
void setHandleSpecificNumOfTopLayers(int count);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,351 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_dropdown.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "bladerunner/subtitles.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/game_constants.h"
#include "common/debug.h"
namespace BladeRunner {
const Color256 UIDropDown::kColors[] = {
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 } // Pressed (framing rectange)
};
UIDropDown::UIDropDown(BladeRunnerEngine *vm,
UIDropDownLineSelectedCallback *ddlLineSelectedCallback,
UIDropDownGenericCallback *ddlCancelledCallback,
UIDropDownGenericCallback *ddlTopFrameClickCallback,
void *callbackData,
Common::String labelStr,
int controlLeftX,
int controlTopY,
int scrollBoxMaxLineCount) : UIComponent(vm) {
_isVisible = false;
_labelStr = labelStr;
_controlLeftX = MAX(controlLeftX, 0);
// TODO The id should be used to eg. grab info about the selected subtitles from an engine's subtitles object
_lineSelectedId = -1;
_lineSelectorFrameRectColor = 0;
_lineSelectorFrameRectHasFocus = false;
// A framing (outlining) rectangle to highlight the selected option field on top of the scrollbox
controlTopY = CLIP(controlTopY, 0, 600);
_lineSelectorFrameRect = Common::Rect(0, controlTopY, 0, controlTopY + kDropDownButtonShapeHeight);
// TODO This eventually should be set to a default probably by the outside caller class(kia_section_settings)
// Current explicit assignment only serves as placeholder / proof of concept
_lineSelectedStr = "English (SCUMMVM) v7 [ENG]";
_lineSelectorScrollBox = new UIScrollBox(_vm, scrollBoxLineSelectCallback, this, scrollBoxMaxLineCount, 2, false, Common::Rect(0, 0, 0, 55 + kFrameRectPaddingPx), Common::Rect(0, 0, 0, 55));
_lineSelectorScrollBoxMaxLineWidth = 0;
_lineDropdownBtn = new UIImagePicker(_vm, 2);
_ddlLineSelectedCallback = ddlLineSelectedCallback;
_ddlCancelledCallback = ddlCancelledCallback;
_ddlTopFrameClickCallback = ddlTopFrameClickCallback;
_callbackData = callbackData;
_mouseX = 0;
_mouseY = 0;
}
UIDropDown::~UIDropDown() {
delete _lineSelectorScrollBox;
delete _lineDropdownBtn;
}
void UIDropDown::activate() {
_lineDropdownBtn->resetImages();
// Actual button shape
// defineImage actually increases internally the bottom and right bounds for the rect to be inclusive (for the contains() method)
_lineDropdownBtn->defineImage(0, Common::Rect(0, _lineSelectorFrameRect.top + 1, kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(73), _vm->_kia->_shapes->get(74), _vm->_kia->_shapes->get(75), nullptr);
// Clickable Selected/Active Line Description area
_lineDropdownBtn->defineImage(1, Common::Rect(0, _lineSelectorFrameRect.top, kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), nullptr, nullptr, nullptr, nullptr);
_lineDropdownBtn->activate(nullptr, nullptr, mouseDownLDBCallback, nullptr, this);
_lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->hide(); // show upon click on field or dropdown button
show();
}
void UIDropDown::deactivate() {
_isVisible = false;
_lineDropdownBtn->deactivate();
_lineSelectorScrollBox->hide();
}
void UIDropDown::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
int posStartOfSelectedLineDesc = _controlLeftX + _vm->_mainFont->getStringWidth(_labelStr) + _vm->_mainFont->getCharWidth(' ');
_vm->_mainFont->drawString(&surface, _labelStr, _controlLeftX, _lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, _lineSelectedStr,
posStartOfSelectedLineDesc,
_lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(240, 232, 192));
// TODO add a clipping for description field here
int posEndOfSelectedLineDesc = posStartOfSelectedLineDesc + _vm->_mainFont->getStringWidth(_lineSelectedStr) + _vm->_mainFont->getCharWidth(' ');
_lineDropdownBtn->setImageLeft(0, posEndOfSelectedLineDesc );
_lineDropdownBtn->setImageLeft(1, posStartOfSelectedLineDesc - kFrameRectPaddingPx);
_lineDropdownBtn->setImageWidth(1, posEndOfSelectedLineDesc + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
_lineDropdownBtn->draw(surface);
// _lineDropdownBtn->drawTooltip(surface, _mouseX, _mouseY);
_lineSelectorFrameRect.moveTo(posStartOfSelectedLineDesc - kFrameRectPaddingPx, _lineSelectorFrameRect.top);
_lineSelectorFrameRect.setWidth(posEndOfSelectedLineDesc + kDropDownButtonShapeWidth + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
_lineSelectorScrollBox->draw(surface);
int lineSelectorFrameRectTargetColor;
if (_lineSelectorScrollBox->isVisible()) {
lineSelectorFrameRectTargetColor = 10;
} else if (_lineSelectorFrameRectHasFocus) {
lineSelectorFrameRectTargetColor = 5;
} else {
lineSelectorFrameRectTargetColor = 0;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_lineSelectorFrameRectColor < lineSelectorFrameRectTargetColor) {
++_lineSelectorFrameRectColor;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_lineSelectorFrameRectColor > lineSelectorFrameRectTargetColor) {
--_lineSelectorFrameRectColor;
}
surface.frameRect(_lineSelectorFrameRect,
surface.format.RGBToColor(kColors[_lineSelectorFrameRectColor].r,
kColors[_lineSelectorFrameRectColor].g,
kColors[_lineSelectorFrameRectColor].b));
}
void UIDropDown::show() {
_isVisible = true;
}
void UIDropDown::hide() {
_isVisible = false;
}
bool UIDropDown::isVisible() {
return _isVisible;
}
bool UIDropDown::isDropDownMenuExpanded() {
return _lineSelectorScrollBox->isVisible();
}
void UIDropDown::clearLines() {
_lineSelectorScrollBox->clearLines();
_lineSelectorScrollBoxMaxLineWidth = 0;
}
void UIDropDown::addLine(const Common::String &text, int lineData) {
_lineSelectorScrollBox->addLine(text, lineData, 0x08);
_lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
}
void UIDropDown::addLine(const char *text, int lineData) {
_lineSelectorScrollBox->addLine(text, lineData, 0x08);
_lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
}
void UIDropDown::sortLines() {
_lineSelectorScrollBox->sortLines();
}
void UIDropDown::handleMouseMove(int mouseX, int mouseY) {
if (!_isVisible) {
return;
}
_mouseX = mouseX;
_mouseY = mouseY;
// contains() does not include right or bottom boundary "line"
if (_lineSelectorFrameRect.contains(mouseX, mouseY)) {
if (!_lineSelectorFrameRectHasFocus && !_lineSelectorScrollBox->isVisible()) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
_lineSelectorFrameRectHasFocus = true;
} else {
_lineSelectorFrameRectHasFocus = false;
}
_lineSelectorScrollBox->handleMouseMove(mouseX, mouseY);
_lineDropdownBtn->handleMouseAction(mouseX, mouseY, false, false, false);
}
void UIDropDown::handleMouseDown(bool alternateButton) {
if (!_isVisible) {
return;
}
if (!alternateButton) {
_lineSelectorScrollBox->handleMouseDown(false);
_lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, true, false, false);
if (!_lineSelectorFrameRectHasFocus
&& _lineSelectorScrollBox->isVisible()
&& !_lineSelectorScrollBox->hasFocus()
) {
_ddlCancelledCallback(_callbackData, this);
showSelectionDropdown(false);
}
}
}
void UIDropDown::handleMouseScroll(int direction) {
if (!_isVisible) {
return;
}
if (_lineSelectorScrollBox->isVisible()) {
_lineSelectorScrollBox->handleMouseScroll(direction);
}
}
void UIDropDown::handleMouseUp(bool alternateButton) {
if (!_isVisible) {
return;
}
if (!alternateButton) {
_lineSelectorScrollBox->handleMouseUp(false);
_lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
}
void UIDropDown::scrollBoxLineSelectCallback(void *callbackData, void *source, int lineData, int mouseButton) {
UIDropDown *self = (UIDropDown *)callbackData;
if (source == self->_lineSelectorScrollBox && lineData >= 0) {
Common::String selectedLangDescStr = self->_lineSelectorScrollBox->getLineText(lineData);
self->_lineSelectedId = lineData;
self->_lineSelectedStr = selectedLangDescStr;
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
self->_ddlLineSelectedCallback(self->_callbackData, self, lineData, mouseButton);
self->showSelectionDropdown(false);
//debug("text selected: %s", selectedLangDescStr.c_str());
}
}
// Callback from _lineDropdownBtn items
void UIDropDown::mouseDownLDBCallback(int buttonId, void *callbackData) {
UIDropDown *self = (UIDropDown *)callbackData;
self->onButtonPressed(buttonId);
}
void UIDropDown::onButtonPressed(int buttonId) {
switch (buttonId) {
case 0:
// Pressed DDL dropdown button (0)
// fall through
case 1:
// if (buttonId == 1) {
// // Pressed DDL clickable area (1)
// debug("Pressed DDL clickable area (1)");
// }
_ddlTopFrameClickCallback(_callbackData, this);
showSelectionDropdown(!_lineSelectorScrollBox->isVisible());
break;
default:
return;
}
}
void UIDropDown::showSelectionDropdown(bool showToggle) {
int prevDropdownBtnLeft = _lineDropdownBtn->getImageLeft(0);
if (showToggle) {
_lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setBoxLeft(_lineDropdownBtn->getImageLeft(1));
// TODO width should be retrieved from the maximum width of a language description in SUBTITLES.MIX (or a max width to clip to)
_lineSelectorScrollBox->setBoxWidth(MAX(_lineDropdownBtn->getImageWidth(1), _lineSelectorScrollBoxMaxLineWidth + _vm->_mainFont->getCharWidth(' ')));
if (_lineDropdownBtn->getImageLeft(0) < kFurthestLeftForScrollBar) {
// CLIP expects the first boundary argument to be the min of the two.
_lineSelectorScrollBox->setScrollbarLeft(CLIP( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), _lineDropdownBtn->getImageLeft(0), kFurthestLeftForScrollBar));
} else {
_lineSelectorScrollBox->setScrollbarLeft(MAX( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), kFurthestLeftForScrollBar));
}
_lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setScrollbarWidth(kDropDownButtonShapeWidth);
_lineSelectorScrollBox->show();
// change dropdown button icon too
_lineDropdownBtn->resetActiveImage(0);
_lineDropdownBtn->defineImage(0, Common::Rect(prevDropdownBtnLeft, _lineSelectorFrameRect.top + 1, prevDropdownBtnLeft + kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(70), _vm->_kia->_shapes->get(71), _vm->_kia->_shapes->get(72), nullptr);
_lineSelectorFrameRectColor = 10;
} else {
// hide scrollable area
_lineSelectorScrollBox->hide();
// change dropdown button icon too
_lineDropdownBtn->resetActiveImage(0);
_lineDropdownBtn->defineImage(0, Common::Rect(prevDropdownBtnLeft, _lineSelectorFrameRect.top + 1, prevDropdownBtnLeft + kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(73), _vm->_kia->_shapes->get(74), _vm->_kia->_shapes->get(75), nullptr);
_lineSelectorFrameRectColor = 0;
}
}
void UIDropDown::setLabelStr(Common::String newLabel) {
_labelStr = newLabel;
}
void UIDropDown::setControlLeft(int controlLeftX) {
_controlLeftX = controlLeftX;
}
Common::String UIDropDown::getLineSelectedStr() {
return _lineSelectedStr;
}
}

View File

@@ -0,0 +1,123 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_DROPDOWN_H
#define BLADERUNNER_UI_DROPDOWN_H
#include "bladerunner/color.h"
#include "bladerunner/ui/ui_component.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
namespace BladeRunner {
class BladeRunnerEngine;
class UIImagePicker;
class UIScrollBox;
typedef void UIDropDownLineSelectedCallback(void *callbackData, void *source, int data, int mouseButton);
typedef void UIDropDownGenericCallback(void *callbackData, void *source);
class UIDropDown : public UIComponent {
static const int kDropDownButtonShapeWidth = 15;
static const int kDropDownButtonShapeHeight = 10;
static const uint8 kFrameRectPaddingPx = 2;
static const int kFurthestLeftForScrollBar = 495;
static const Color256 kColors[];
int _controlLeftX;
Common::String _labelStr;
bool _isVisible;
int _lineSelectedId;
Common::String _lineSelectedStr;
UIScrollBox *_lineSelectorScrollBox;
UIImagePicker *_lineDropdownBtn;
//int _lineDropdownBtnTopY;
//int _lineDropdownBtnHeight;
Common::Rect _lineSelectorFrameRect;
int _lineSelectorFrameRectColor;
bool _lineSelectorFrameRectHasFocus;
int _lineSelectorScrollBoxMaxLineWidth;
UIDropDownLineSelectedCallback *_ddlLineSelectedCallback;
UIDropDownGenericCallback *_ddlCancelledCallback;
UIDropDownGenericCallback *_ddlTopFrameClickCallback;
void *_callbackData;
int _mouseX;
int _mouseY;
public:
UIDropDown(BladeRunnerEngine *vm, UIDropDownLineSelectedCallback *ddlLineSelectedCallback,
UIDropDownGenericCallback *ddlCancelledCallback,
UIDropDownGenericCallback *ddlTopFrameClickCallback,
void *callbackData,
Common::String labelStr,
int controlLeftX,
int controlTopY,
int scrollBoxMaxLineCount);
~UIDropDown() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
void handleMouseScroll(int direction) override;
void show();
void hide();
bool isVisible();
bool isDropDownMenuExpanded();
void activate();
void deactivate();
void clearLines();
void addLine(const Common::String &text, int lineData);
void addLine(const char *text, int lineData);
void sortLines();
void setLabelStr(Common::String newLabel);
void setControlLeft(int controlLeftX);
Common::String getLineSelectedStr();
private:
static void mouseDownLDBCallback(int buttonId, void *callbackData);
static void scrollBoxLineSelectCallback(void *callbackData, void *source, int lineData, int mouseButton);
void onButtonPressed(int buttonId);
void showSelectionDropdown(bool showToggle);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,409 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/debugger.h"
#include "bladerunner/font.h"
#include "bladerunner/mouse.h"
#include "bladerunner/shape.h"
#include "bladerunner/time.h"
#include "common/rect.h"
#include "common/str.h"
#include "graphics/surface.h"
namespace BladeRunner {
UIImagePicker::UIImagePicker(BladeRunnerEngine *vm, int imageCount) {
_vm = vm;
reset();
_images.resize(imageCount);
_imageCount = imageCount;
resetImages();
}
UIImagePicker::~UIImagePicker() {
_images.clear();
reset();
}
void UIImagePicker::resetImages() {
for (int i = 0; i != _imageCount; ++i) {
resetImage(i);
}
}
bool UIImagePicker::defineImage(int i, Common::Rect rect, const Shape *shapeUp, const Shape *shapeHovered, const Shape *shapeDown, const char *tooltip) {
if (i < 0 || i >= _imageCount || _images[i].active) {
return false;
}
Image &img = _images[i];
img.rect = rect;
// for rect to be inclusive
++(img.rect.right);
++(img.rect.bottom);
img.shapeUp = shapeUp;
img.shapeHovered = shapeHovered;
img.shapeDown = shapeDown;
img.active = true;
if (tooltip != nullptr) {
img.tooltip = tooltip;
} else {
img.tooltip.clear();
}
return true;
}
bool UIImagePicker::setImageTop(int i, int top) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
img.rect.moveTo(img.rect.left, top);
return true;
}
bool UIImagePicker::setImageLeft(int i, int left) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
img.rect.moveTo(left, img.rect.top);
return true;
}
bool UIImagePicker::setImageWidth(int i, int16 width) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
img.rect.setWidth(width);
return true;
}
bool UIImagePicker::setImageShapeUp(int i, const Shape *shapeUp) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
_images[i].shapeUp = shapeUp;
return true;
}
bool UIImagePicker::setImageShapeHovered(int i, const Shape *shapeHovered) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
_images[i].shapeHovered = shapeHovered;
return true;
}
bool UIImagePicker::setImageShapeDown(int i, const Shape *shapeDown) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
_images[i].shapeDown = shapeDown;
return true;
}
bool UIImagePicker::setImageTooltip(int i, const char *tooltip) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
if (tooltip != nullptr) {
_images[i].tooltip = tooltip;
} else {
_images[i].tooltip.clear();
}
return true;
}
int UIImagePicker::getImageTop(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.top;
}
int UIImagePicker::getImageLeft(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.left;
}
int UIImagePicker::getImageWidth(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.width();
}
bool UIImagePicker::resetActiveImage(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
resetImage(i);
return true;
}
void UIImagePicker::activate(UIImagePickerCallback *mouseInCallback,
UIImagePickerCallback *mouseOutCallback,
UIImagePickerCallback *mouseDownCallback,
UIImagePickerCallback *mouseUpCallback,
void *callbackData) {
_isButtonDown = false;
_mouseInCallback = mouseInCallback;
_mouseOutCallback = mouseOutCallback;
_mouseDownCallback = mouseDownCallback;
_mouseUpCallback = mouseUpCallback;
_callbackData = callbackData;
_hoverStartTimestamp = 0u;
_isVisible = true;
_hoveredImageIndex = -1;
_pressedImageIndex = -1;
}
void UIImagePicker::deactivate() {
_isButtonDown = false;
_mouseInCallback = nullptr;
_mouseOutCallback = nullptr;
_mouseDownCallback = nullptr;
_mouseUpCallback = nullptr;
_callbackData = nullptr;
_hoverStartTimestamp = 0u;
_isVisible = false;
_hoveredImageIndex = -1;
_pressedImageIndex = -1;
}
void UIImagePicker::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
for (int i = 0; i != _imageCount; ++i) {
Image &img = _images[i];
if (!img.active) {
continue;
}
if (i == _hoveredImageIndex && i == _pressedImageIndex && _isButtonDown
&& !_vm->_mouse->isDisabled()
&& img.shapeDown) {
img.shapeDown->draw(surface, img.rect.left, img.rect.top);
} else if (i == _hoveredImageIndex && !_isButtonDown
&& !_vm->_mouse->isDisabled()
&& img.shapeHovered) {
img.shapeHovered->draw(surface, img.rect.left, img.rect.top);
} else {
// this shape should always be the fall back shape to prevent blinking
if (img.shapeUp) {
img.shapeUp->draw(surface, img.rect.left, img.rect.top);
}
}
if (_vm->_debugger->_viewUI) {
surface.frameRect(img.rect, surface.format.RGBToColor(255, 255, 255));
_vm->_mainFont->drawString(&surface, Common::String::format("%d", i), (img.rect.left + img.rect.right) / 2, (img.rect.top + img.rect.bottom) / 2, surface.w, surface.format.RGBToColor(255, 255, 255));
}
}
}
void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) {
if (!_isVisible) {
return;
}
if (
(_hoveredImageIndex == -1) ||
(_vm->_mouse->isDisabled()) ||
(!_images[_hoveredImageIndex].active) ||
(_vm->_time->current() - _hoverStartTimestamp < 1000u)
) { // unsigned difference is intentional (time difference)
return;
}
Common::String &tooltip = _images[_hoveredImageIndex].tooltip;
if (tooltip.empty()) {
return;
}
int width = _vm->_mainFont->getStringWidth(tooltip) + 1;
int height = _vm->_mainFont->getFontHeight() + 1;
Common::Rect rect;
rect.left = x - ((width / 2) + 1);
if (rect.left < 0) {
rect.left = 0;
}
rect.top = y - 10;
if (rect.top < 0) {
rect.top = 0;
}
rect.right = width + rect.left + 3;
if (rect.right >= BladeRunnerEngine::kOriginalGameWidth) {
rect.right = BladeRunnerEngine::kOriginalGameWidth - 1;
rect.left = BladeRunnerEngine::kOriginalGameWidth - 4 - width;
if (rect.left < 0) rect.left = 0; // should never happen
if (rect.right < 0) rect.right = 0; // should never happen
}
rect.bottom = height + rect.top + 2;
if (rect.bottom >= BladeRunnerEngine::kOriginalGameHeight) {
rect.bottom = BladeRunnerEngine::kOriginalGameHeight - 1;
rect.top = BladeRunnerEngine::kOriginalGameHeight - 3 - height;
if (rect.top < 0) rect.top = 0; // should never happen
if (rect.bottom < 0) rect.bottom = 0; // should never happen
}
surface.fillRect(rect, surface.format.RGBToColor(0, 0, 0));
surface.frameRect(rect, surface.format.RGBToColor(255, 255, 255));
_vm->_mainFont->drawString(&surface, tooltip, rect.left + 2, rect.top, surface.w, surface.format.RGBToColor(255, 255, 255));
}
bool UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) {
if (!_isVisible || ignore) {
return false;
}
bool actionHandled = false;
int hoveredImageIndex = -1;
for (int i = 0; i != _imageCount; ++i) {
if (_images[i].rect.contains(x, y)) {
hoveredImageIndex = i;
break;
}
}
// If mouse moved to a new image
if (hoveredImageIndex != _hoveredImageIndex) {
if (!_isButtonDown) {
if (hoveredImageIndex == -1) {
if (_mouseOutCallback) {
_mouseOutCallback(hoveredImageIndex, _callbackData);
}
} else {
if (_mouseInCallback) {
_mouseInCallback(hoveredImageIndex, _callbackData);
}
}
}
_hoverStartTimestamp = _vm->_time->current();
_hoveredImageIndex = hoveredImageIndex;
}
// If mouse button changed to pressed
if (down && !_isButtonDown) {
_isButtonDown = true;
_pressedImageIndex = _hoveredImageIndex;
if (_hoveredImageIndex != -1) {
if (_mouseDownCallback) {
_mouseDownCallback(_hoveredImageIndex, _callbackData);
actionHandled = true;
}
}
}
// If mouse button changed to released
if (up) {
if (_isButtonDown) {
if (_hoveredImageIndex == _pressedImageIndex && _pressedImageIndex != -1) {
if (_mouseUpCallback) {
_mouseUpCallback(_hoveredImageIndex, _callbackData);
actionHandled = true;
}
}
}
_isButtonDown = false;
_pressedImageIndex = -1;
}
return actionHandled;
}
void UIImagePicker::resetImage(int i) {
assert(i >= 0 && i < _imageCount);
Image &img = _images[i];
img.active = false;
img.rect.left = -1;
img.rect.top = -1;
img.rect.right = -1;
img.rect.bottom = -1;
img.shapeUp = nullptr;
img.shapeHovered = nullptr;
img.shapeDown = nullptr;
img.tooltip.clear();
}
bool UIImagePicker::hasHoveredImage() {
return _hoveredImageIndex >= 0;
}
void UIImagePicker::reset() {
_isVisible = false;
_hoveredImageIndex = -1;
_pressedImageIndex = -1;
_hoverStartTimestamp = 0u;
_isButtonDown = false;
_mouseInCallback = nullptr;
_mouseOutCallback = nullptr;
_mouseDownCallback = nullptr;
_mouseUpCallback = nullptr;
_callbackData = nullptr;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,108 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_IMAGE_PICKER_H
#define BLADERUNNER_UI_IMAGE_PICKER_H
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
namespace Graphics {
struct Surface;
}
namespace BladeRunner {
class BladeRunnerEngine;
class Shape;
typedef void UIImagePickerCallback(int, void *);
class UIImagePicker {
struct Image {
int active;
Common::Rect rect;
const Shape *shapeUp;
const Shape *shapeHovered;
const Shape *shapeDown;
Common::String tooltip;
};
BladeRunnerEngine *_vm;
int _isVisible;
int _imageCount;
int _hoveredImageIndex;
int _pressedImageIndex;
uint32 _hoverStartTimestamp;
int _isButtonDown;
Common::Array<Image> _images;
UIImagePickerCallback *_mouseInCallback;
UIImagePickerCallback *_mouseOutCallback;
UIImagePickerCallback *_mouseDownCallback;
UIImagePickerCallback *_mouseUpCallback;
void *_callbackData;
public:
UIImagePicker(BladeRunnerEngine *vm, int imageCount);
~UIImagePicker();
void resetImages();
bool defineImage(int i, Common::Rect rect, const Shape *shapeUp, const Shape *shapeHovered, const Shape *shapeDown, const char *tooltip);
bool setImageTop(int i, int top);
bool setImageLeft(int i, int left);
bool setImageWidth(int i, int16 width);
bool setImageShapeUp(int i, const Shape *shapeUp);
bool setImageShapeHovered(int i, const Shape *shapeHovered);
bool setImageShapeDown(int i, const Shape *shapeDown);
bool setImageTooltip(int i, const char *tooltip);
int getImageTop(int i);
int getImageLeft(int i);
int getImageWidth(int i);
bool resetActiveImage(int i);
void activate(UIImagePickerCallback *mouseInCallback,
UIImagePickerCallback *mouseOutCallback,
UIImagePickerCallback *mouseDownCallback,
UIImagePickerCallback *mouseUpCallback,
void *callbackData);
void deactivate();
void draw(Graphics::Surface &surface);
void drawTooltip(Graphics::Surface &surface, int x, int y);
bool handleMouseAction(int x, int y, bool down, bool up, bool ignore = false);
void resetImage(int i);
bool hasHoveredImage();
void reset();
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,154 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_input_box.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/time.h"
#include "common/keyboard.h"
#include "common/system.h"
#include "graphics/surface.h"
namespace BladeRunner {
UIInputBox::UIInputBox(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int maxLength, const Common::String &text)
: UIComponent(vm) {
_valueChangedCallback = valueChangedCallback;
_callbackData = callbackData;
_isVisible = true;
_rect = rect;
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
_maxLength = maxLength;
setText(text);
_cursorIsVisible = false;
_timeLast = _vm->_time->currentSystem();
}
void UIInputBox::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
int rectHalfWidth = (_rect.right + _rect.left) / 2;
int textHalfWidth = _vm->_mainFont->getStringWidth(_text) / 2;
_vm->_mainFont->drawString(&surface, _text, rectHalfWidth - textHalfWidth, _rect.top, surface.w, surface.format.RGBToColor(152, 112, 56));
if (_cursorIsVisible) {
surface.vLine(textHalfWidth + rectHalfWidth + 2, _rect.top, _rect.bottom - 1, surface.format.RGBToColor(248, 240, 232));
}
if (_vm->_time->currentSystem() - _timeLast > 500) {
_timeLast = _vm->_time->currentSystem();
_cursorIsVisible = !_cursorIsVisible;
}
}
void UIInputBox::setText(const Common::String &text) {
_text = text;
}
const Common::String &UIInputBox::getText() {
return _text;
}
void UIInputBox::show() {
_isVisible = true;
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
}
void UIInputBox::hide() {
_isVisible = false;
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
}
void UIInputBox::handleKeyDown(const Common::KeyState &kbd) {
if (_isVisible) {
uint8 kc = 0;
if (getValidChar(kbd.ascii, kc) && _text.size() < _maxLength) {
_text += kc;
} else if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
_text.deleteLastChar();
}
}
}
void UIInputBox::handleCustomEventStart(const Common::Event &evt) {
if (_isVisible
&& evt.customType == BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpConfirmDlg
&& !_text.empty()
&& _valueChangedCallback) {
_valueChangedCallback(_callbackData, this);
}
}
bool UIInputBox::getValidChar(const uint16 &kAscii16bit, uint8 &targetAscii) {
if (kAscii16bit != 0) {
// The above check for kAscii16bit > 0 gets rid of the tentative warning:
// "Adding \0 to String. This is permitted, but can have unwanted consequences."
// which was triggered by the .encode(Common::kDos850) operation below.
//
// The values that the KeyState::ascii field receives from the SDL backend are actually ISO 8859-1 encoded. They need to be
// reencoded to DOS so as to match the game font encoding (although we currently use UIInputBox::charIsValid() to block most
// extra characters, so it might not make much of a difference).
targetAscii = (uint8)(Common::U32String(Common::String::format("%c", kAscii16bit), Common::kISO8859_1).encode(Common::kDos850).firstChar());
return charIsValid(targetAscii);
}
return false;
}
bool UIInputBox::charIsValid(const uint8 &kc) {
// The in-game font for text input is KIA6PT which follows IBM PC Code page 437 (CCSID 437)
// This code page is identical to Code page 850 for the first 128 codes.
// This method is:
// 1) Filtering out characters not allowed in a DOS filename.
// Note, however, that it does allow ',', '.', ';', '=', '[' and ']'
// TODO Is that a bug?
// 2) Allowing codes for glyphs that exist in KIA6PT up to code 0xA8 (glyph '¿')
// and also the extra codes for 0xAD (glyph '¡') and 0xE1 (glyph 'ß')
// (in order for these extra extended ASCII codes to be included,
// the comparisons in the return clause should be between uint values).
// 3) Additionally disallows the '\x7F' character which caused a glyph '⊐' to be printed
// when the Delete key was pressed with no saved game selected,
// ie. the highlighted line on the KIA save screen is "<< NEW SLOT >>".
// The original does not show this glyph either but seems to filter the key earlier (not in this method).
// It's more effective to completely block the glyph in this method, though.
return kc >= ' '
&& kc != '<'
&& kc != '>'
&& kc != ':'
&& kc != '"'
&& kc != '/'
&& kc != '\\'
&& kc != '|'
&& kc != '?'
&& kc != '*'
&& kc != (uint8)'\x7F'
&& (kc <= (uint8)'\xA8' || kc == (uint8)'\xAD' || kc == (uint8)'\xE1');
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,67 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_ui_inputbox_H
#define BLADERUNNER_ui_inputbox_H
#include "bladerunner/ui/ui_component.h"
#include "common/rect.h"
#include "common/str.h"
namespace BladeRunner {
class UIInputBox : public UIComponent {
UIComponentCallback *_valueChangedCallback;
void *_callbackData;
bool _isVisible;
Common::Rect _rect;
uint _maxLength;
Common::String _text;
bool _cursorIsVisible;
uint32 _timeLast;
public:
UIInputBox(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int maxLength, const Common::String &text);
void draw(Graphics::Surface &surface) override;
void setText(const Common::String &text);
const Common::String &getText();
void show();
void hide();
void handleKeyDown(const Common::KeyState &kbd) override;
void handleCustomEventStart(const Common::Event &evt) override;
private:
bool getValidChar(const uint16 &kc16bit, uint8 &kc8bit);
bool charIsValid(const uint8 &kc16bit);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,824 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_scroll_box.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/time.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/ui/kia.h"
namespace BladeRunner {
const Color256 UIScrollBox::k3DFrameColors[] = {
{ 32, 32, 24 },
{ 40, 40, 40 },
{ 40, 40, 48 },
{ 72, 64, 64 },
{ 160, 136, 128 },
{ 160, 136, 128 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
const Color256 UIScrollBox::kTextBackgroundColors[] = {
{ 40, 56, 80 },
{ 48, 64, 96 },
{ 56, 72, 112 },
{ 72, 88, 128 },
{ 152, 192, 248 },
{ 0, 0, 0 }
};
const Color256 UIScrollBox::kTextColors1[] = {
{ 72, 104, 152 },
{ 96, 120, 184 },
{ 112, 144, 216 },
{ 136, 168, 248 },
{ 152, 192, 248 }
};
const Color256 UIScrollBox::kTextColors2[] = {
{ 200, 216, 248 },
{ 216, 224, 248 },
{ 224, 232, 248 },
{ 232, 240, 248 },
{ 248, 248, 248 }
};
const Color256 UIScrollBox::kTextColors3[] = {
{ 240, 232, 192 },
{ 240, 232, 208 },
{ 240, 240, 216 },
{ 248, 240, 232 },
{ 248, 248, 248 }
};
const Color256 UIScrollBox::kTextColors4[] = {
{ 152, 112, 56 },
{ 184, 144, 88 },
{ 216, 184, 112 },
{ 232, 208, 136 },
{ 248, 224, 144 }
};
UIScrollBox::UIScrollBox(BladeRunnerEngine *vm,
UIScrollBoxClickedCallback *lineSelectedCallback,
void *callbackData,
int maxLineCount,
int style,
bool center,
Common::Rect rect,
Common::Rect scrollBarRect) : UIComponent(vm) {
_selectedLineState = 0;
_scrollUpButtonState = 0;
_scrollDownButtonState = 0;
_scrollAreaUpState = 0;
_scrollAreaDownState = 0;
_scrollBarState = 0;
_scrollUpButtonHover = false;
_scrollDownButtonHover = false;
_scrollAreaUpHover = false;
_scrollAreaDownHover = false;
_scrollBarHover = false;
_hoveredLine = -1;
_selectedLineIndex = -1;
_lineSelectedCallback = lineSelectedCallback;
_callbackData = callbackData;
_isVisible = false;
_style = style; // 0, 1 or (new) 2. "2" is similar to "1" but with solid background for main area and scroll bar
_center = center;
_timeLastScroll = _vm->_time->currentSystem();
_timeLastCheckbox = _vm->_time->currentSystem();
_timeLastHighlight = _vm->_time->currentSystem();
_highlightFrame = 0;
_rect = rect;
_scrollBarRect = scrollBarRect;
_scrollBarRect.right += 15; // right side was not used, but it's useful for determining if the control is selected
_lineCount = 0;
_maxLineCount = maxLineCount;
_firstLineVisible = 0;
_maxLinesVisible = _rect.height() / kLineHeight;
_mouseButton = false;
_rect.bottom = _rect.top + kLineHeight * _maxLinesVisible - 1;
_lines.resize(_maxLineCount);
for (int i = 0; i < _maxLineCount; ++i) {
_lines[i] = new Line();
_lines[i]->lineData = -1;
_lines[i]->flags = 0x00;
_lines[i]->checkboxFrame = 5u;
}
_mouseOver = false;
}
UIScrollBox::~UIScrollBox() {
for (int i = 0; i < _maxLineCount; ++i) {
delete _lines[i];
}
}
void UIScrollBox::show() {
_selectedLineState = 0;
_scrollUpButtonState = 0;
_scrollDownButtonState = 0;
_scrollAreaUpState = 0;
_scrollAreaDownState = 0;
_scrollBarState = 0;
_hoveredLine = -1;
_selectedLineIndex = -1;
_scrollUpButtonHover = false;
_scrollDownButtonHover = false;
_scrollAreaUpHover = false;
_scrollAreaDownHover = false;
_scrollBarHover = false;
_timeLastScroll = _vm->_time->currentSystem();
_timeLastCheckbox = _vm->_time->currentSystem();
_timeLastHighlight = _vm->_time->currentSystem();
_highlightFrame = 0;
_isVisible = true;
_mouseOver = false;
}
void UIScrollBox::hide() {
_isVisible = false;
}
bool UIScrollBox::isVisible() {
return _isVisible;
}
bool UIScrollBox::hasFocus() {
return _mouseOver;
}
void UIScrollBox::setBoxTop(int top) {
_rect.moveTo(_rect.left, top);
_rect.bottom = _rect.top + kLineHeight * _maxLinesVisible - 1;
}
void UIScrollBox::setBoxLeft(int left) {
_rect.moveTo(left, _rect.top);
}
void UIScrollBox::setBoxWidth(uint16 width) {
_rect.setWidth(width);
}
int UIScrollBox::getBoxLeft() {
return _rect.left;
}
uint16 UIScrollBox::getBoxWidth() {
return _rect.width();
}
void UIScrollBox::setScrollbarTop(int top) {
_scrollBarRect.moveTo(_scrollBarRect.left, top);
}
void UIScrollBox::setScrollbarLeft(int left) {
_scrollBarRect.moveTo(left, _scrollBarRect.top);
}
void UIScrollBox::setScrollbarWidth(uint16 width) {
_scrollBarRect.setWidth(width);
_scrollBarRect.right += 15; // right side was not used, but it's useful for determining if the control is selected
}
void UIScrollBox::clearLines() {
_lineCount = 0;
_firstLineVisible = 0;
}
void UIScrollBox::addLine(const Common::String &text, int lineData, int flags) {
_lines[_lineCount]->text = text;
_lines[_lineCount]->lineData = lineData;
_lines[_lineCount]->flags = flags;
++_lineCount;
}
void UIScrollBox::addLine(const char *text, int lineData, int flags) {
_lines[_lineCount]->text = text;
_lines[_lineCount]->lineData = lineData;
_lines[_lineCount]->flags = flags;
++_lineCount;
}
void UIScrollBox::sortLines() {
qsort(_lines.data(), _lineCount, sizeof(Line *), &sortFunction);
}
void UIScrollBox::handleMouseMove(int mouseX, int mouseY) {
if (!_isVisible) {
return;
}
_mouseOver = _rect.contains(mouseX, mouseY) || _scrollBarRect.contains(mouseX, mouseY);
if (_rect.contains(mouseX, mouseY)) {
int newHoveredLine = (mouseY - _rect.top) / 10 + _firstLineVisible;
if (newHoveredLine >= _lineCount) {
newHoveredLine = -1;
}
if (newHoveredLine != _hoveredLine && newHoveredLine >= 0 && newHoveredLine < _lineCount) {
if (_lines[newHoveredLine]->lineData >= 0 && _selectedLineState == 0) {
int soundId = kSfxTEXT1;
if (_lines[newHoveredLine]->flags & 0x01 ) {
soundId = kSfxTEXT3;
}
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(soundId), 100, 0, 0, 50, 0);
}
}
_hoveredLine = newHoveredLine;
} else {
_hoveredLine = -1;
}
_scrollUpButtonHover =
(mouseX >= _scrollBarRect.left)
&& (mouseX < _scrollBarRect.left + 15)
&& (mouseY >= _scrollBarRect.top)
&& (mouseY < _scrollBarRect.top + 8);
_scrollDownButtonHover =
(mouseX >= _scrollBarRect.left)
&& (mouseX < _scrollBarRect.left + 15)
&& (mouseY > _scrollBarRect.bottom - 8)
&& (mouseY <= _scrollBarRect.bottom);
int scrollAreaHeight = _scrollBarRect.bottom - _scrollBarRect.top - 15;
int scrollBarHeight = scrollAreaHeight;
if (_lineCount > _maxLinesVisible) {
scrollBarHeight = _maxLinesVisible * scrollAreaHeight / _lineCount;
}
if (scrollBarHeight < 16) {
scrollBarHeight = 16;
}
int scrollAreaEmptySize = scrollAreaHeight - scrollBarHeight;
int scrollBarY = 0;
if (_lineCount > _maxLinesVisible) {
scrollBarY = scrollAreaEmptySize * _firstLineVisible / (_lineCount - _maxLinesVisible);
}
if (_scrollBarState == 2) {
int v12 = scrollBarHeight / 2 + 8;
if (mouseY - _scrollBarRect.top > v12 && _lineCount > _maxLinesVisible && scrollAreaEmptySize > 0) {
_firstLineVisible = (_lineCount - _maxLinesVisible) * (mouseY - _scrollBarRect.top - v12) / scrollAreaEmptySize;
if (_firstLineVisible > _lineCount - _maxLinesVisible) {
_firstLineVisible = _lineCount - _maxLinesVisible;
}
} else {
_firstLineVisible = 0;
}
if (_lineCount <= _maxLinesVisible) {
scrollBarY = 0;
} else {
scrollBarY = scrollAreaEmptySize * _firstLineVisible/ (_lineCount - _maxLinesVisible);
}
}
scrollBarY = scrollBarY + _scrollBarRect.top + 8;
_scrollBarHover =
(mouseX >= _scrollBarRect.left)
&& (mouseX < _scrollBarRect.left + 15)
&& (mouseY >= scrollBarY)
&& (mouseY < scrollBarY + scrollBarHeight);
_scrollAreaUpHover =
(mouseX >= _scrollBarRect.left)
&& (mouseX < _scrollBarRect.left + 15)
&& (mouseY >= _scrollBarRect.top + 8)
&& (mouseY < scrollBarY);
_scrollAreaDownHover =
(mouseX >= _scrollBarRect.left)
&& (mouseX < _scrollBarRect.left + 15)
&& (mouseY >= scrollBarY + scrollBarHeight)
&& (mouseY < _scrollBarRect.bottom - 8);
}
void UIScrollBox::handleMouseDown(bool alternateButton) {
if (!_isVisible) {
return;
}
_mouseButton = alternateButton;
if (_hoveredLine == -1) {
_selectedLineState = 1;
} else if (_selectedLineIndex == -1) {
_selectedLineIndex = _hoveredLine;
_selectedLineState = 2;
if (_hoveredLine < _lineCount) {
if (_lineSelectedCallback) {
_lineSelectedCallback(_callbackData, this, _lines[_selectedLineIndex]->lineData, _mouseButton);
}
if (_lines[_selectedLineIndex]->flags & 0x01) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxBEEP10), 100, 0, 0, 50, 0);
}
}
}
if (!alternateButton) {
if (_scrollUpButtonHover) {
_scrollUpButtonState = 2;
_timeLastScroll = _vm->_time->currentSystem() - 160u;
} else {
_scrollUpButtonState = 1;
}
if (_scrollDownButtonHover) {
_scrollDownButtonState = 2;
} else {
_scrollDownButtonState = 1;
}
if (_scrollBarHover) {
_scrollBarState = 2;
} else {
_scrollBarState = 1;
}
if (_scrollAreaUpHover) {
_scrollAreaUpState = 2;
_timeLastScroll = _vm->_time->currentSystem() - 160u;
} else {
_scrollAreaUpState = 1;
}
if (_scrollAreaDownHover) {
_scrollAreaDownState = 2;
_timeLastScroll = _vm->_time->currentSystem() - 160u;
} else {
_scrollAreaDownState = 1;
}
}
}
void UIScrollBox::handleMouseUp(bool alternateButton) {
if (_isVisible) {
if ( alternateButton == _mouseButton) {
_selectedLineState = 0;
_selectedLineIndex = -1;
}
if (!alternateButton) {
_scrollUpButtonState = 0;
_scrollDownButtonState = 0;
_scrollAreaUpState = 0;
_scrollAreaDownState = 0;
_scrollBarState = 0;
}
}
}
void UIScrollBox::handleMouseScroll(int direction) {
if (_mouseOver) {
if (direction > 0) {
scrollDown();
} else if (direction < 0) {
scrollUp();
}
}
}
int UIScrollBox::getSelectedLineData() {
if (_hoveredLine >= 0 && _selectedLineState != 1 && _hoveredLine < _lineCount) {
return _lines[_hoveredLine]->lineData;
}
return -1;
}
Common::String UIScrollBox::getLineText(int lineData) {
if (hasLine(lineData)) {
return _lines[_hoveredLine]->text;
}
return "";
}
int UIScrollBox::getMaxLinesVisible() {
return _maxLinesVisible;
}
int UIScrollBox::getLineCount() {
return _lineCount;
}
void UIScrollBox::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
uint32 timeNow = _vm->_time->currentSystem();
// update scrolling
if (_scrollUpButtonState == 2 && _scrollUpButtonHover) {
// unsigned difference is intentional
if ((timeNow - _timeLastScroll) > 160u) {
scrollUp();
_timeLastScroll = timeNow;
}
} else if (_scrollDownButtonState == 2 && _scrollDownButtonHover) {
// unsigned difference is intentional
if ((timeNow - _timeLastScroll) > 160u) {
scrollDown();
_timeLastScroll = timeNow;
}
} else if (_scrollAreaUpState == 2 && _scrollAreaUpHover) {
// unsigned difference is intentional
if ((timeNow - _timeLastScroll) > 160u) {
_firstLineVisible -= _maxLinesVisible - 1;
_firstLineVisible = CLIP(_firstLineVisible, 0, _lineCount - _maxLinesVisible);
_timeLastScroll = timeNow;
}
} else if (_scrollAreaDownState == 2 && _scrollAreaDownHover) {
// unsigned difference is intentional
if ((timeNow - _timeLastScroll) > 160u) {
_firstLineVisible += _maxLinesVisible - 1;
_firstLineVisible = CLIP(_firstLineVisible, 0, _lineCount - _maxLinesVisible);
_timeLastScroll = timeNow;
}
}
// update checkboxes
// unsigned difference is intentional
uint32 timeDiffCheckBox = timeNow - _timeLastCheckbox;
if (timeDiffCheckBox > 67u) {
_timeLastCheckbox = timeNow;
for (int i = 0; i < _lineCount; ++i) {
if (_lines[i]->flags & 0x01) { // has checkbox
if (_lines[i]->flags & 0x02) { // checkbox checked
if (_lines[i]->checkboxFrame < 5u) {
_lines[i]->checkboxFrame += timeDiffCheckBox / 67u;
}
if (_lines[i]->checkboxFrame > 5u) {
_lines[i]->checkboxFrame = 5u;
}
} else { // checkbox not checked
if (_lines[i]->checkboxFrame > 0u) {
_lines[i]->checkboxFrame = (_lines[i]->checkboxFrame < (timeDiffCheckBox / 67u)) ? 0u : _lines[i]->checkboxFrame - (timeDiffCheckBox / 67u);
}
if (_lines[i]->checkboxFrame == 0u) { // original was < 0, int
_lines[i]->checkboxFrame = 0u;
}
}
}
}
}
// update highlight
// unsigned difference is intentional
if ((timeNow - _timeLastHighlight) > 67u) {
_timeLastHighlight = timeNow;
_highlightFrame = (_highlightFrame + 1) % 8;
}
// draw text lines
int linesVisible = 0;
int lastLineVisible = 0;
if (_maxLinesVisible < _lineCount - _firstLineVisible) {
linesVisible = _maxLinesVisible;
lastLineVisible = _firstLineVisible + _maxLinesVisible;
} else {
linesVisible = _lineCount - _firstLineVisible;
lastLineVisible = _lineCount;
}
if (_firstLineVisible < lastLineVisible) {
int y = _rect.top;
int y1 = _rect.top + 8;
int y2 = _rect.top + 2;
int i = _firstLineVisible;
do {
int startingColorIndex = 3;
if (i - _firstLineVisible < 3) {
startingColorIndex = i - _firstLineVisible;
}
int endingColorIndex = 3;
if (i - _firstLineVisible >= linesVisible - 3) {
endingColorIndex = linesVisible - (i - _firstLineVisible + 1);
}
int colorIndex = endingColorIndex;
if (startingColorIndex < endingColorIndex) {
colorIndex = startingColorIndex;
}
bool v35 = false;
int color = 0;
if ((((_selectedLineState == 0 && i == _hoveredLine) || (_selectedLineState == 2 && i == _selectedLineIndex && _selectedLineIndex == _hoveredLine)) && _lines[i]->lineData != -1) || _lines[i]->flags & 0x04) {
v35 = true;
if (_style) {
color = surface.format.RGBToColor(kTextColors2[colorIndex].r, kTextColors2[colorIndex].g, kTextColors2[colorIndex].b);
} else {
color = surface.format.RGBToColor(kTextColors3[colorIndex].r, kTextColors3[colorIndex].g, kTextColors3[colorIndex].b);
}
}
else {
if (_style) {
color = surface.format.RGBToColor(kTextColors1[colorIndex].r, kTextColors1[colorIndex].g, kTextColors1[colorIndex].b);
} else {
color = surface.format.RGBToColor(kTextColors4[colorIndex].r, kTextColors4[colorIndex].g, kTextColors4[colorIndex].b);
}
}
int x = _rect.left;
if (_lines[i]->flags & 0x01) { // has checkbox
int checkboxShapeId = 0;
if (_style == 0) {
if (_lines[i]->checkboxFrame || v35) {
if (_lines[i]->checkboxFrame != 5u || v35) {
checkboxShapeId = _lines[i]->checkboxFrame + 62u;
} else {
checkboxShapeId = 61;
}
} else {
checkboxShapeId = 60;
}
} else if (_lines[i]->checkboxFrame || v35) {
if (_lines[i]->checkboxFrame != 5u || v35) {
checkboxShapeId = _lines[i]->checkboxFrame + 54u;
} else {
checkboxShapeId = 53;
}
} else {
checkboxShapeId = 52;
}
_vm->_kia->_shapes->get(checkboxShapeId)->draw(surface, x - 1, y);
x += 11;
}
if (_lines[i]->flags & 0x10) { // highlighted line
if (_lines[i]->flags & 0x20) {
int highlightShapeId = _highlightFrame;
if (highlightShapeId > 4) {
highlightShapeId = 8 - highlightShapeId;
}
_vm->_kia->_shapes->get(highlightShapeId + 85)->draw(surface, x, y2);
}
x += 6;
}
if (_lines[i]->flags & 0x08) { // has background rectangle
int colorBackground = 0;
if (_vm->_cutContent && (_lines[i]->flags & 0x40)) {
// A KIA clue marked as hidden/private, but already shared with Mainframe
// Note, proper hidden clues will not have this mark and will get colorBackground
// from below (case _style > 0)
colorBackground = surface.format.RGBToColor(80, 46, 22);
} else {
if (_style == 2) {
colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r / 8, kTextBackgroundColors[colorIndex].g / 8, kTextBackgroundColors[colorIndex].b / 8);
} else if (_style > 0) {
colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r, kTextBackgroundColors[colorIndex].g, kTextBackgroundColors[colorIndex].b);
} else {
colorBackground = surface.format.RGBToColor(80, 56, 32);
}
}
if (_style == 2) {
// New: style = 2 (original unused)
// original behavior -- No padding between the colored background of lines, simulate solid background (gradient)
surface.fillRect(Common::Rect(CLIP(x - 1, 0, 639), y, _rect.right + 1, y + kLineHeight), colorBackground);
} else {
// original behavior -- there is padding between the colored background of lines
surface.fillRect(Common::Rect(x, y, _rect.right + 1, y1 + 1), colorBackground);
}
}
if (_center) {
x = _rect.left + (_rect.width() - _vm->_mainFont->getStringWidth(_lines[i]->text)) / 2;
}
_vm->_mainFont->drawString(&surface, _lines[i]->text, x, y, surface.w, color);
y1 += kLineHeight;
y2 += kLineHeight;
y += kLineHeight;
++i;
} while (i < lastLineVisible);
}
if (_style == 2 && getLineCount() >= getMaxLinesVisible()) {
// New: style = 2 (original unused)
// Solid background color for scrollbar
int scrollBarFillColor = surface.format.RGBToColor(k3DFrameColors[0].r / 2, k3DFrameColors[0].g / 2, k3DFrameColors[0].b / 2);
surface.fillRect(Common::Rect(_scrollBarRect.left, _scrollBarRect.top, CLIP(_scrollBarRect.left + 15, 0, 639), _scrollBarRect.bottom), scrollBarFillColor);
}
if (_style != 2
|| (_style == 2 && getLineCount() >= getMaxLinesVisible())
) {
// draw scroll up button
int scrollUpButtonShapeId = 0;
if (_scrollUpButtonState) {
if (_scrollUpButtonState == 2) {
if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 72;
} else {
scrollUpButtonShapeId = 71;
}
} else {
scrollUpButtonShapeId = 70;
}
} else if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 71;
} else {
scrollUpButtonShapeId = 70;
}
_vm->_kia->_shapes->get(scrollUpButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.top);
// draw scroll down button
int scrollDownButtonShapeId = 0;
if (_scrollDownButtonState) {
if (_scrollDownButtonState == 2) {
if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 75;
} else {
scrollDownButtonShapeId = 74;
}
} else {
scrollDownButtonShapeId = 73;
}
} else if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 74;
} else {
scrollDownButtonShapeId = 73;
}
_vm->_kia->_shapes->get(scrollDownButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.bottom - 7);
int scrollAreaSize = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
int scrollBarHeight = 0;
if (_lineCount <= _maxLinesVisible) {
scrollBarHeight = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
} else {
scrollBarHeight = _maxLinesVisible * scrollAreaSize / _lineCount;
}
scrollBarHeight = MAX(scrollBarHeight, 16);
int v56 = 0;
if (_lineCount <= _maxLinesVisible) {
v56 = 0;
} else {
v56 = _firstLineVisible * (scrollAreaSize - scrollBarHeight) / (_lineCount - _maxLinesVisible);
}
int v58 = v56 + _scrollBarRect.top + 8;
if (_scrollBarState == 2) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 1, 1);
} else if (!_scrollBarState && _scrollBarHover) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v56 + _scrollBarRect.top + 8, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 1);
} else {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 0);
}
}
}
void UIScrollBox::checkAll() {
for (int i = 0; i < _lineCount; ++i) {
if (_lines[i]->flags & 0x01) {
_lines[i]->flags |= 0x02;
}
}
}
void UIScrollBox::uncheckAll() {
for (int i = 0; i < _lineCount; ++i) {
if (_lines[i]->flags & 0x01) {
_lines[i]->flags &= ~0x02;
}
}
}
void UIScrollBox::toggleCheckBox(int lineData) {
int i = findLine(lineData);
if (i != -1) {
if (_lines[i]->flags & 0x02) {
_lines[i]->flags &= ~0x02;
} else {
_lines[i]->flags |= 0x02;
}
}
}
bool UIScrollBox::hasLine(int lineData) {
return findLine(lineData) != -1;
}
void UIScrollBox::resetHighlight(int lineData) {
int i = findLine(lineData);
if (i != -1) {
_lines[i]->flags &= ~0x20;
}
}
void UIScrollBox::setFlags(int lineData, int flags) {
int i = findLine(lineData);
if (i != -1) {
_lines[i]->flags |= flags;
}
}
void UIScrollBox::resetFlags(int lineData, int flags) {
int i = findLine(lineData);
if (i != -1) {
_lines[i]->flags &= ~flags;
}
}
int UIScrollBox::sortFunction(const void *item1, const void *item2) {
Line *line1 = *(Line * const *)item1;
Line *line2 = *(Line * const *)item2;
return line1->text.compareToIgnoreCase(line2->text);
}
void UIScrollBox::draw3DFrame(Graphics::Surface &surface, Common::Rect rect, bool pressed, int style) {
int color1, color2;
if (pressed) {
color1 = surface.format.RGBToColor(k3DFrameColors[style + 6].r, k3DFrameColors[style + 6].g, k3DFrameColors[style + 6].b);
color2 = surface.format.RGBToColor(k3DFrameColors[style + 4].r, k3DFrameColors[style + 4].g, k3DFrameColors[style + 4].b);
} else {
color1 = surface.format.RGBToColor(k3DFrameColors[style + 4].r, k3DFrameColors[style + 4].g, k3DFrameColors[style + 4].b);
color2 = surface.format.RGBToColor(k3DFrameColors[style + 6].r, k3DFrameColors[style + 6].g, k3DFrameColors[style + 6].b);
}
int color3 = surface.format.RGBToColor(k3DFrameColors[style].r, k3DFrameColors[style].g, k3DFrameColors[style].b);
int fillColor = surface.format.RGBToColor(k3DFrameColors[style + 2].r, k3DFrameColors[style + 2].g, k3DFrameColors[style + 2].b);
surface.fillRect(Common::Rect(rect.left + 1, rect.top + 1, rect.right - 1, rect.bottom - 1), fillColor);
surface.hLine(rect.left + 1, rect.top, rect.right - 2, color1);
surface.hLine(rect.left + 1, rect.bottom - 1, rect.right - 2, color2);
surface.vLine(rect.left, rect.top, rect.bottom - 2, color1);
surface.vLine(rect.right - 1, rect.top + 1, rect.bottom - 1, color2);
surface.hLine(rect.right - 1, rect.top, rect.right - 1, color3);
surface.hLine(rect.left, rect.bottom - 1, rect.left, color3);
}
void UIScrollBox::scrollUp() {
if (_firstLineVisible > 0) {
--_firstLineVisible;
}
}
void UIScrollBox::scrollDown() {
if (_lineCount - _firstLineVisible > _maxLinesVisible) {
++_firstLineVisible;
}
}
int UIScrollBox::findLine(int lineData) {
for (int i = 0; i < _lineCount; ++i) {
if (_lines[i]->lineData == lineData) {
return i;
}
}
return -1;
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,162 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_SCROLLBOX_H
#define BLADERUNNER_UI_SCROLLBOX_H
#include "bladerunner/color.h"
#include "bladerunner/ui/ui_component.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
namespace BladeRunner {
typedef void UIScrollBoxClickedCallback(void *callbackData, void *source, int lineData, int mouseButton);
class UIScrollBox : public UIComponent {
static const int kLineHeight = 10;
static const Color256 k3DFrameColors[];
static const Color256 kTextBackgroundColors[];
static const Color256 kTextColors1[];
static const Color256 kTextColors2[];
static const Color256 kTextColors3[];
static const Color256 kTextColors4[];
struct Line {
Common::String text;
int lineData;
int flags;
uint32 checkboxFrame;
};
int _selectedLineState;
int _scrollUpButtonState;
int _scrollDownButtonState;
int _scrollAreaUpState;
int _scrollAreaDownState;
int _scrollBarState;
int _hoveredLine;
int _selectedLineIndex;
bool _scrollUpButtonHover;
bool _scrollDownButtonHover;
bool _scrollAreaUpHover;
bool _scrollAreaDownHover;
bool _scrollBarHover;
bool _mouseButton;
UIScrollBoxClickedCallback *_lineSelectedCallback;
void *_callbackData;
bool _isVisible;
int _style;
bool _center;
uint32 _timeLastScroll;
uint32 _timeLastCheckbox;
uint32 _timeLastHighlight;
int _highlightFrame;
Common::Rect _rect;
Common::Rect _scrollBarRect;
int _lineCount;
int _maxLineCount;
Common::Array<Line *> _lines;
int _maxLinesVisible;
int _firstLineVisible;
bool _mouseOver;
public:
UIScrollBox(BladeRunnerEngine *vm,
UIScrollBoxClickedCallback *lineSelectedCallback,
void *callbackData,
int maxLineCount,
int style,
bool center,
Common::Rect rect,
Common::Rect scrollBarRect);
~UIScrollBox() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
void handleMouseScroll(int direction) override;
void show();
void hide();
bool isVisible();
bool hasFocus();
void setBoxTop(int top);
void setBoxLeft(int left);
void setBoxWidth(uint16 width);
void setScrollbarTop(int top);
void setScrollbarLeft(int left);
void setScrollbarWidth(uint16 width);
int getBoxLeft();
uint16 getBoxWidth();
void clearLines();
void addLine(const Common::String &text, int lineData, int flags);
void addLine(const char *text, int lineData, int flags);
void sortLines();
int getSelectedLineData();
Common::String getLineText(int lineData);
int getMaxLinesVisible();
int getLineCount();
void checkAll();
void uncheckAll();
void toggleCheckBox(int lineData);
bool hasLine(int lineData);
void resetHighlight(int lineData);
void setFlags(int lineData, int flags);
void resetFlags(int lineData, int flags);
private:
static int sortFunction(const void *line1, const void *line2);
void draw3DFrame(Graphics::Surface &surface, Common::Rect rect, bool pressed, int style);
void scrollUp();
void scrollDown();
int findLine(int lineData);
};
} // End of namespace BladeRunner
#endif

View File

@@ -0,0 +1,173 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "bladerunner/ui/ui_slider.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/game_info.h"
#include "bladerunner/game_constants.h"
namespace BladeRunner {
const Color256 UISlider::kColors[] = {
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 }, // Pressed (framing rectange)
{ 184, 144, 88 },
{ 200, 160, 96 },
{ 216, 184, 112 },
{ 232, 200, 128 },
{ 240, 224, 144 }
};
UISlider::UISlider(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int maxValue, int value)
: UIComponent(vm) {
_valueChangedCallback = valueChangedCallback;
_callbackData = callbackData;
_maxValue = MAX(0, maxValue);
_value = CLIP(value, 0, _maxValue - 1);
_rect = rect;
_isEnabled = true;
_currentFrameColor = 0;
_hasFocus = false;
_pressedStatus = 0;
_mouseX = 0;
}
void UISlider::draw(Graphics::Surface &surface) {
if (_rect.isEmpty()) {
return;
}
int frameColor;
if (_pressedStatus == 1) {
frameColor = 10;
} else if (_hasFocus && _pressedStatus != 2 && _isEnabled) {
frameColor = 5;
} else {
frameColor = 0;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_currentFrameColor < frameColor) {
++_currentFrameColor;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_currentFrameColor > frameColor) {
--_currentFrameColor;
}
surface.frameRect(_rect, surface.format.RGBToColor(kColors[_currentFrameColor].r, kColors[_currentFrameColor].g, kColors[_currentFrameColor].b));
int sliderX = 0;
if (_maxValue <= 1) {
sliderX = _rect.left;
} else {
sliderX = _rect.left + ((_value * _rect.width()) / (_maxValue - 1));
}
if (_pressedStatus == 1) {
int sliderValue = ((_maxValue - 1) * (_mouseX - _rect.left)) / _rect.width();
sliderX = _rect.left + ((sliderValue * _rect.width()) / (_maxValue - 1));
sliderX = CLIP(sliderX, (int)_rect.left, (int)_rect.right);
}
if (_rect.left + 1 < _rect.right - 1) {
int striding = _rect.left + sliderX;
for (int x = _rect.left + 1; x < _rect.right - 1; ++x) {
int colorIndex = 15 - (abs(sliderX - x) >> 2);
if (!_isEnabled) {
colorIndex /= 2;
}
if (colorIndex < 3) {
colorIndex = 3;
}
uint32 color = surface.format.RGBToColor(kColors[colorIndex].r, kColors[colorIndex].g, kColors[colorIndex].b);
if ((striding + x) & 1 || x == sliderX) {
color = surface.format.RGBToColor(0, 0, 0);
}
surface.vLine(x, _rect.top + 1, _rect.bottom - 2, color);
}
}
}
void UISlider::handleMouseMove(int mouseX, int mouseY) {
_mouseX = mouseX;
if (_rect.contains(mouseX, mouseY)) {
if (!_hasFocus && _isEnabled && _pressedStatus == 0) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
_hasFocus = true;
} else {
_hasFocus = false;
}
}
void UISlider::handleMouseDown(bool alternateButton) {
if (_isEnabled && !alternateButton) {
if (_hasFocus) {
_pressedStatus = 1;
} else {
_pressedStatus = 2;
}
}
}
void UISlider::handleMouseUp(bool alternateButton) {
if (!alternateButton) {
if (_pressedStatus == 1) {
if (_rect.width() == 0) {
_value = 0;
} else {
_value = ((_maxValue - 1) * (_mouseX - _rect.left)) / _rect.width();
}
_value = CLIP(_value, 0, _maxValue - 1);
if (_valueChangedCallback) {
_valueChangedCallback(_callbackData, this);
}
}
_pressedStatus = 0;
}
}
void UISlider::setValue(int value) {
_value = CLIP(value, 0, _maxValue - 1);
}
} // End of namespace BladeRunner

View File

@@ -0,0 +1,61 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_UI_SLIDER_H
#define BLADERUNNER_UI_SLIDER_H
#include "bladerunner/color.h"
#include "bladerunner/ui/ui_component.h"
#include "common/rect.h"
namespace BladeRunner {
class UISlider : public UIComponent {
static const Color256 kColors[];
UIComponentCallback *_valueChangedCallback;
void *_callbackData;
bool _isEnabled;
int _maxValue;
Common::Rect _rect;
int _currentFrameColor;
bool _hasFocus;
int _pressedStatus;
int _mouseX;
public:
int _value;
UISlider(BladeRunnerEngine *vm, UIComponentCallback *valueChangedCallback, void *callbackData, Common::Rect rect, int maxValue, int value);
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
void setValue(int value);
};
} // End of namespace BladeRunner
#endif

File diff suppressed because it is too large Load Diff

178
engines/bladerunner/ui/vk.h Normal file
View File

@@ -0,0 +1,178 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BLADERUNNER_VK_H
#define BLADERUNNER_VK_H
#include "common/array.h"
#include "common/str.h"
#include "graphics/surface.h"
namespace BladeRunner {
class BladeRunnerEngine;
class VKScript;
class Shapes;
class UIImagePicker;
class VQAPlayer;
class VK {
friend class Debugger;
struct Question {
bool isPresent;
bool wasAsked;
int sentenceId;
int relatedSentenceId;
};
BladeRunnerEngine *_vm;
VKScript *_script;
UIImagePicker *_buttons;
Shapes *_shapes;
VQAPlayer *_vqaPlayerMain;
VQAPlayer *_vqaPlayerEye;
int _vqaFrameMain;
bool _vqaLoopEnded;
Graphics::Surface _surfaceEye;
bool _isOpen;
int _actorId;
bool _calibrationStarted;
bool _testStarted;
Common::Array<Common::Array<Question> > _questions;
int _ambientVolumeFactorOutsideVK; // should be in [0, 100]
int _musicVolumeFactorOutsideVK; // should be in [0, 100]
int _soundTrackId1;
int _soundTrackId2;
int _soundTrackId3;
int _calibration;
int _calibrationRatio;
int _calibrationCounter;
int _humanProbability;
int _humanGauge;
int _humanGaugeTarget;
int _humanGaugeDelta;
uint32 _timeNextHumanGaugeStepDiff;
uint32 _timeNextHumanGaugeStepStart;
int _replicantProbability;
int _replicantGauge;
int _replicantGaugeTarget;
int _replicantGaugeDelta;
uint32 _timeNextReplicantGaugeStepDiff;
uint32 _timeNextReplicantGaugeStepStart;
int _anxiety;
int _needleValue;
int _needleValueMax;
int _needleValueTarget;
int _needleValueDelta;
uint32 _timeNextNeedleStepStart;
uint32 _timeNextNeedleOscillateStart;
uint32 _timeNeedleReturnStart;
bool _isClosing;
uint32 _timeCloseStart;
int _blinkState;
uint32 _timeNextBlinkStart;
uint32 _timeNextGaugesBlinkStart;
bool _isAdjusting;
int _adjustment;
int _adjustmentTarget;
int _adjustmentDelta;
uint32 _timeNextAdjustementStepStart;
int _eyeLineSelected;
int _eyeLineX;
int _eyeLineXLast;
int _eyeLineY;
int _eyeLineYLast;
int _eyeLineXDelta;
int _eyeLineYDelta;
uint32 _timeNextEyeLineStepStart;
uint32 _timeNextEyeLineStart;
public:
VK(BladeRunnerEngine *vm);
~VK();
void open(int actorId, int calibrationRatio);
bool isOpen() const;
void close();
void tick();
// void resume();
void handleMouseDown(int mouseX, int mouseY, bool mainButton);
void handleMouseUp(int mouseX, int mouseY, bool mainButton);
void playSpeechLine(int actorId, int sentenceId, float pauseDuration);
void addQuestion(int intensity, int sentenceId, int relatedSentenceId);
void subjectReacts(int intensity, int humanResponse, int replicantResponse, int anxiety);
void eyeAnimates(int loopId);
private:
static void mouseDownCallback(int buttonId, void *callbackData);
static void mouseUpCallback(int buttonId, void *callbackData);
static void loopEnded(void *callbackData, int frame, int loopId);
void reset();
void init();
void draw();
void drawNeedle(Graphics::Surface &surface);
void drawEye(Graphics::Surface &surface);
void drawEyeCrosshair(Graphics::Surface &surface, uint32 timeNow);
void drawMouse(Graphics::Surface &surface);
void drawGauge(Graphics::Surface &surface, int value, int x, int y, int width);
void drawHumanGauge(Graphics::Surface &surface);
void drawReplicantGauge(Graphics::Surface &surface);
void calibrate();
void beginTest();
void startAdjustement();
void stopAdjustement();
void animateAdjustment(int target);
void setAdjustment(int x);
void setAdjustmentFromMouse();
void findRelatedQuestionBySentenceId(int inSentenceId, int &outRelatedQuestionId, int &outRelatedIntensity);
void askQuestion(int intensity);
};
} // End of namespace BladeRunner
#endif