scummvm/engines/bladerunner/bladerunner.cpp

1691 lines
38 KiB
C++
Raw Normal View History

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "bladerunner/bladerunner.h"
#include "bladerunner/actor.h"
#include "bladerunner/actor_dialogue_queue.h"
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/audio_mixer.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/audio_speech.h"
#include "bladerunner/chapters.h"
#include "bladerunner/combat.h"
#include "bladerunner/crimes_database.h"
#include "bladerunner/debugger.h"
2017-08-22 18:57:50 +02:00
#include "bladerunner/dialogue_menu.h"
2016-10-06 21:23:46 +02:00
#include "bladerunner/font.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
#include "bladerunner/image.h"
#include "bladerunner/item_pickup.h"
#include "bladerunner/items.h"
#include "bladerunner/lights.h"
#include "bladerunner/mouse.h"
#include "bladerunner/music.h"
#include "bladerunner/outtake.h"
#include "bladerunner/obstacles.h"
2017-08-27 22:39:36 +02:00
#include "bladerunner/overlays.h"
2018-03-17 16:14:48 +01:00
#include "bladerunner/police_maze.h"
#include "bladerunner/regions.h"
2018-03-17 16:14:48 +01:00
#include "bladerunner/savefile.h"
#include "bladerunner/scene.h"
#include "bladerunner/scene_objects.h"
#include "bladerunner/screen_effects.h"
#include "bladerunner/set.h"
#include "bladerunner/script/ai_script.h"
#include "bladerunner/script/init_script.h"
#include "bladerunner/script/kia_script.h"
#include "bladerunner/script/police_maze.h"
#include "bladerunner/script/scene_script.h"
#include "bladerunner/settings.h"
#include "bladerunner/shape.h"
#include "bladerunner/slice_animations.h"
#include "bladerunner/slice_renderer.h"
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
#include "bladerunner/ui/elevator.h"
2018-01-31 00:37:19 +01:00
#include "bladerunner/ui/esper.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/spinner.h"
#include "bladerunner/ui/vk.h"
#include "bladerunner/vqa_decoder.h"
#include "bladerunner/waypoints.h"
2017-03-28 17:50:04 +02:00
#include "bladerunner/zbuffer.h"
#include "common/array.h"
#include "common/error.h"
#include "common/events.h"
2018-03-17 16:14:48 +01:00
#include "common/savefile.h"
#include "common/system.h"
#include "engines/util.h"
#include "engines/advancedDetector.h"
#include "graphics/pixelformat.h"
namespace BladeRunner {
BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *desc)
: Engine(syst),
_rnd("bladerunner") {
_windowIsActive = true;
_gameIsRunning = true;
_playerLosesControlCounter = 0;
_playerActorIdle = false;
_playerDead = false;
_speechSkipped = false;
_gameOver = false;
_gameAutoSave = 0;
_gameIsLoading = false;
_sceneIsLoading = false;
_runningActorId = -1;
_isWalkingInterruptible = false;
_interruptWalking = false;
_walkSoundId = -1;
_walkSoundVolume = 0;
2016-09-29 22:30:50 +02:00
_walkSoundBalance = 0;
_crimesDatabase = nullptr;
switch (desc->language) {
case Common::EN_ANY:
2018-02-13 23:08:37 +01:00
_languageCode = "E";
break;
case Common::DE_DEU:
2018-02-13 23:08:37 +01:00
_languageCode = "G";
break;
case Common::FR_FRA:
2018-02-13 23:08:37 +01:00
_languageCode = "F";
break;
case Common::IT_ITA:
2018-02-13 23:08:37 +01:00
_languageCode = "I";
break;
case Common::RU_RUS:
2018-02-13 23:08:37 +01:00
_languageCode = "R";
break;
case Common::ES_ESP:
2018-02-13 23:08:37 +01:00
_languageCode = "S";
break;
default:
2018-02-13 23:08:37 +01:00
_languageCode = "E";
}
2018-02-01 20:40:49 +01:00
_screenEffects = nullptr;
_combat = nullptr;
_actorDialogueQueue = nullptr;
_settings = nullptr;
_itemPickup = nullptr;
_lights = nullptr;
_obstacles = nullptr;
_sceneScript = nullptr;
_gameInfo = nullptr;
_waypoints = nullptr;
_gameVars = nullptr;
_view = nullptr;
_sceneObjects = nullptr;
_gameFlags = nullptr;
_items = nullptr;
_audioMixer = nullptr;
_audioPlayer = nullptr;
_music = nullptr;
_audioSpeech = nullptr;
_ambientSounds = nullptr;
_chapters = nullptr;
_overlays = nullptr;
_zbuffer = nullptr;
_playerActor = nullptr;
_textActorNames = nullptr;
_textCrimes = nullptr;
_textClueTypes = nullptr;
_textKIA = nullptr;
_textSpinnerDestinations = nullptr;
_textVK = nullptr;
_textOptions = nullptr;
_dialogueMenu = nullptr;
_suspectsDatabase = nullptr;
_kia = nullptr;
_spinner = nullptr;
_elevator = nullptr;
_mainFont = nullptr;
_esper = nullptr;
_vk = nullptr;
2018-02-01 20:40:49 +01:00
_mouse = nullptr;
_sliceAnimations = nullptr;
_sliceRenderer = nullptr;
_crimesDatabase = nullptr;
_scene = nullptr;
_aiScripts = nullptr;
for (int i = 0; i != kActorCount; ++i) {
_actors[i] = nullptr;
2018-02-01 20:40:49 +01:00
}
_debugger = nullptr;
walkingReset();
_actorUpdateCounter = 0;
}
BladeRunnerEngine::~BladeRunnerEngine() {
}
bool BladeRunnerEngine::hasFeature(EngineFeature f) const {
return f == kSupportsRTL;
}
Common::Error BladeRunnerEngine::run() {
Graphics::PixelFormat format = createRGB555();
initGraphics(640, 480, &format);
_system->showMouse(true);
if (!startup()) {
shutdown();
return Common::Error(Common::kUnknownError, "Failed to initialize resources");
}
#if BLADERUNNER_DEBUG_GAME
{
#else
if (warnUserAboutUnsupportedGame()) {
#endif
init2();
/* TODO: Check for save games and enter KIA */
gameLoop();
}
shutdown();
return Common::kNoError;
}
bool BladeRunnerEngine::startup(bool hasSavegames) {
// These are static objects in original game
_screenEffects = new ScreenEffects(this, 0x8000);
// TODO: end credits
_actorDialogueQueue = new ActorDialogueQueue(this);
_settings = new Settings(this);
_itemPickup = new ItemPickup(this);
_lights = new Lights(this);
// TODO: outtake player - but this is done bit differently
_policeMaze = new PoliceMaze(this);
_obstacles = new Obstacles(this);
_sceneScript = new SceneScript(this);
_debugger = new Debugger(this);
// This is the original startup in the game
bool r;
_surfaceFront.create(640, 480, createRGB555());
_surfaceBack.create(640, 480, createRGB555());
_surface4.create(640, 480, createRGB555());
_gameTime = new Time(this);
r = openArchive("STARTUP.MIX");
if (!r)
return false;
_gameInfo = new GameInfo(this);
if (!_gameInfo)
return false;
r = _gameInfo->open("GAMEINFO.DAT");
if (!r) {
return false;
}
_combat = new Combat(this);
// TODO: Create datetime - not used
// TODO: Create graphics surfaces 1-4
// TODO: Allocate audio cache
if (hasSavegames) {
if (!loadSplash()) {
return false;
}
}
_waypoints = new Waypoints(this, _gameInfo->getWaypointCount());
// TODO: Cover waypoints
// TODO: Flee waypoints
2018-03-17 16:14:48 +01:00
_gameVars = new int[_gameInfo->getGlobalVarCount()]();
// TODO: Actor AI DLL init
// Seed rand
// TODO: Sine and cosine lookup tables for intervals of 1.0, 4.0, and 12.0
_view = new View();
_sceneObjects = new SceneObjects(this, _view);
_gameFlags = new GameFlags();
_gameFlags->setFlagCount(_gameInfo->getFlagCount());
_items = new Items(this);
_audioMixer = new AudioMixer(this);
_audioPlayer = new AudioPlayer(this);
_music = new Music(this);
_audioSpeech = new AudioSpeech(this);
_ambientSounds = new AmbientSounds(this);
// TODO: Read BLADE.INI
_chapters = new Chapters(this);
if (!_chapters)
return false;
if (!openArchive("MUSIC.MIX"))
return false;
if (!openArchive("SFX.MIX"))
return false;
if (!openArchive("SPCHSFX.TLK"))
return false;
2017-08-27 22:39:36 +02:00
_overlays = new Overlays(this);
_overlays->init();
2017-03-28 17:50:04 +02:00
_zbuffer = new ZBuffer();
_zbuffer->init(640, 480);
int actorCount = (int)_gameInfo->getActorCount();
assert(actorCount < kActorCount);
for (int i = 0; i != actorCount; ++i) {
_actors[i] = new Actor(this, i);
_actors[i]->setup(i);
}
_actors[kActorVoiceOver] = new Actor(this, kActorVoiceOver);
_playerActor = _actors[_gameInfo->getPlayerId()];
_playerActor->setFPS(15);
_playerActor->timerStart(6, 200);
// TODO: Set actor ids (redundant?)
2018-03-17 16:14:48 +01:00
_policeMaze = new PoliceMaze(this);
if (!_policeMaze->init())
return false;
_textActorNames = new TextResource(this);
if (!_textActorNames->open("ACTORS"))
return false;
_textCrimes = new TextResource(this);
if (!_textCrimes->open("CRIMES"))
return false;
_textClueTypes = new TextResource(this);
if (!_textClueTypes->open("CLUETYPE"))
return false;
_textKIA = new TextResource(this);
if (!_textKIA->open("KIA"))
return false;
_textSpinnerDestinations = new TextResource(this);
if (!_textSpinnerDestinations->open("SPINDEST"))
return false;
_textVK = new TextResource(this);
if (!_textVK->open("VK"))
return false;
_textOptions = new TextResource(this);
if (!_textOptions->open("OPTIONS"))
return false;
2017-08-22 18:57:50 +02:00
_dialogueMenu = new DialogueMenu(this);
if (!_dialogueMenu->loadText("DLGMENU"))
return false;
_suspectsDatabase = new SuspectsDatabase(this, _gameInfo->getSuspectCount());
_kia = new KIA(this);
2017-07-31 00:11:00 +02:00
_spinner = new Spinner(this);
2017-08-24 22:34:40 +02:00
_elevator = new Elevator(this);
// TODO: Scores
2016-10-06 21:23:46 +02:00
_mainFont = new Font(this);
_mainFont->open("KIA6PT.FON", 640, 480, -1, 0, 0x252D);
_mainFont->setSpacing(1, 0);
for (int i = 0; i != 43; ++i) {
Shape *shape = new Shape(this);
2018-01-31 00:37:19 +01:00
shape->open("SHAPES.SHP", i);
_shapes.push_back(shape);
}
2018-01-31 00:37:19 +01:00
_esper = new ESPER(this);
_vk = new VK(this);
_mouse = new Mouse(this);
// _mouse->setCursorPosition(320, 240);
_mouse->setCursor(0);
_sliceAnimations = new SliceAnimations(this);
r = _sliceAnimations->open("INDEX.DAT");
if (!r)
return false;
// TODO: Support cdframes
r = _sliceAnimations->openHDFrames();
2017-08-26 21:27:54 +02:00
if (!r) {
return false;
2017-08-26 21:27:54 +02:00
}
r = _sliceAnimations->openCoreAnim();
2017-08-26 21:27:54 +02:00
if (!r) {
return false;
2017-08-26 21:27:54 +02:00
}
_sliceRenderer = new SliceRenderer(this);
_sliceRenderer->setScreenEffects(_screenEffects);
_crimesDatabase = new CrimesDatabase(this, "CLUES", _gameInfo->getClueCount());
// TODO: Scene
_scene = new Scene(this);
// Load INIT.DLL
InitScript initScript(this);
2015-02-07 11:41:52 +01:00
initScript.SCRIPT_Initialize_Game();
// TODO: Load AI-ACT1.DLL
_aiScripts = new AIScripts(this, actorCount);
initChapterAndScene();
return true;
}
void BladeRunnerEngine::initChapterAndScene() {
// TODO: Init actors...
2017-03-23 16:20:10 +01:00
for (int i = 0, end = _gameInfo->getActorCount(); i != end; ++i) {
_aiScripts->initialize(i);
2017-03-23 16:20:10 +01:00
}
2017-03-23 16:20:10 +01:00
for (int i = 0, end = _gameInfo->getActorCount(); i != end; ++i) {
_actors[i]->changeAnimationMode(kAnimationModeIdle);
2017-03-23 16:20:10 +01:00
}
for (int i = 1, end = _gameInfo->getActorCount(); i != end; ++i) { // skip first actor, probably player
_actors[i]->movementTrackNext(true);
}
_settings->setChapter(1);
_settings->setNewSetAndScene(_gameInfo->getInitialSetId(), _gameInfo->getInitialSceneId());
}
void BladeRunnerEngine::shutdown() {
_mixer->stopAll();
// TODO: Write BLADE.INI
delete _vk;
_vk = nullptr;
2018-01-31 00:37:19 +01:00
delete _esper;
_esper = nullptr;
delete _mouse;
_mouse = nullptr;
for (uint i = 0; i != _shapes.size(); ++i) {
delete _shapes[i];
}
_shapes.clear();
// TODO: Shutdown Scene
delete _scene;
if (_chapters) {
if (_chapters->hasOpenResources())
_chapters->closeResources();
delete _chapters;
_chapters = nullptr;
}
delete _crimesDatabase;
_crimesDatabase = nullptr;
delete _sliceRenderer;
_sliceRenderer = nullptr;
delete _sliceAnimations;
_sliceAnimations = nullptr;
delete _textActorNames;
_textActorNames = nullptr;
delete _textCrimes;
_textCrimes = nullptr;
delete _textClueTypes;
_textClueTypes = nullptr;
delete _textKIA;
_textKIA = nullptr;
delete _textSpinnerDestinations;
_textSpinnerDestinations = nullptr;
delete _textVK;
_textVK = nullptr;
delete _textOptions;
_textOptions = nullptr;
delete _dialogueMenu;
_dialogueMenu = nullptr;
delete _ambientSounds;
_ambientSounds = nullptr;
2017-08-27 22:39:36 +02:00
delete _overlays;
_overlays = nullptr;
delete _audioSpeech;
_audioSpeech = nullptr;
delete _music;
_music = nullptr;
delete _audioPlayer;
_audioPlayer = nullptr;
delete _audioMixer;
_audioMixer = nullptr;
if (isArchiveOpen("MUSIC.MIX")) {
closeArchive("MUSIC.MIX");
}
if (isArchiveOpen("SFX.MIX")) {
closeArchive("SFX.MIX");
}
if (isArchiveOpen("SPCHSFX.TLK")) {
closeArchive("SPCHSFX.TLK");
}
if (_mainFont) {
2016-10-06 21:23:46 +02:00
_mainFont->close();
delete _mainFont;
_mainFont = nullptr;
}
delete _items;
_items = nullptr;
delete _gameFlags;
_gameFlags = nullptr;
delete _view;
_view = nullptr;
delete _sceneObjects;
_sceneObjects = nullptr;
// TODO: Delete sine and cosine lookup tables
delete _aiScripts;
_aiScripts = nullptr;
delete[] _gameVars;
_gameVars = nullptr;
delete _waypoints;
_waypoints = nullptr;
// TODO: Delete Cover waypoints
// TODO: Delete Flee waypoints
// TODO: Delete Scores
delete _elevator;
_elevator = nullptr;
delete _spinner;
_spinner = nullptr;
delete _kia;
_kia = nullptr;
delete _suspectsDatabase;
_suspectsDatabase = nullptr;
// TODO: Delete datetime - not used
int actorCount = (int)_gameInfo->getActorCount();
for (int i = 0; i != actorCount; ++i) {
delete _actors[i];
_actors[i] = nullptr;
}
delete _actors[kActorVoiceOver];
_actors[kActorVoiceOver] = nullptr;
_playerActor = nullptr;
delete _gameInfo;
_gameInfo = nullptr;
// TODO: Delete graphics surfaces here
_surface4.free();
_surfaceBack.free();
_surfaceFront.free();
if (isArchiveOpen("STARTUP.MIX")) {
closeArchive("STARTUP.MIX");
}
// TODO: Delete MIXArchives here
delete _gameTime;
_gameTime = nullptr;
// These are static objects in original game
delete _debugger;
_debugger = nullptr;
delete _zbuffer;
_zbuffer = nullptr;
delete _itemPickup;
_itemPickup = nullptr;
delete _policeMaze;
_policeMaze = nullptr;
delete _obstacles;
_obstacles = nullptr;
delete _actorDialogueQueue;
_actorDialogueQueue = nullptr;
delete _combat;
_combat = nullptr;
delete _screenEffects;
_screenEffects = nullptr;
delete _lights;
_lights = nullptr;
delete _settings;
_settings = nullptr;
delete _sceneScript;
_sceneScript = nullptr;
}
bool BladeRunnerEngine::loadSplash() {
Image img(this);
if (!img.open("SPLASH.IMG")) {
return false;
}
img.copyToSurface(&_surfaceFront);
blitToScreen(_surfaceFront);
return true;
}
bool BladeRunnerEngine::init2() {
return true;
}
Common::Point BladeRunnerEngine::getMousePos() const {
Common::Point p = _eventMan->getMousePos();
p.x = CLIP(p.x, int16(0), int16(639));
p.y = CLIP(p.y, int16(0), int16(479));
return p;
2017-07-31 00:11:00 +02:00
}
bool BladeRunnerEngine::isMouseButtonDown() const {
return _eventMan->getButtonState() != 0;
}
void BladeRunnerEngine::gameLoop() {
_gameIsRunning = true;
do {
/* TODO: check player death */
gameTick();
} while (_gameIsRunning);
}
void BladeRunnerEngine::gameTick() {
handleEvents();
if (_gameIsRunning && _windowIsActive) {
// TODO: Only run if not in Kia, script, nor AI
if (!_sceneScript->isInsideScript() && !_aiScripts->isInsideScript()) {
_settings->openNewScene();
}
// TODO: Autosave
//probably not needed, this version of tick is just loading data from buffer
//_audioMixer->tick();
2018-01-31 00:37:19 +01:00
if (_kia->isOpen()) {
_kia->tick();
return;
}
2017-07-31 00:11:00 +02:00
if (_spinner->isOpen()) {
_spinner->tick();
_ambientSounds->tick();
return;
}
2018-01-31 00:37:19 +01:00
if (_esper->isOpen()) {
_esper->tick();
return;
}
if (_vk->isOpen()) {
_vk->tick();
_ambientSounds->tick();
return;
}
2017-08-24 22:34:40 +02:00
if (_elevator->isOpen()) {
_elevator->tick();
_ambientSounds->tick();
return;
}
// TODO: Scores
_actorDialogueQueue->tick();
if (_scene->didPlayerWalkIn()) {
_sceneScript->playerWalkedIn();
}
2017-08-22 18:57:50 +02:00
bool inDialogueMenu = _dialogueMenu->isVisible();
if (!inDialogueMenu) {
for (int i = 0; i < (int)_gameInfo->getActorCount(); ++i) {
_actors[i]->tickCombat();
}
2017-08-22 18:57:50 +02:00
}
2017-03-28 17:50:04 +02:00
_policeMaze->tick();
2017-08-22 18:57:50 +02:00
// TODO: Gun range announcements
2017-03-28 17:50:04 +02:00
_zbuffer->clean();
_ambientSounds->tick();
bool backgroundChanged = false;
int frame = _scene->advanceFrame();
if (frame >= 0) {
_sceneScript->sceneFrameAdvanced(frame);
backgroundChanged = true;
}
(void)backgroundChanged;
blit(_surfaceBack, _surfaceFront);
2017-08-27 22:39:36 +02:00
_overlays->tick();
2017-08-22 18:57:50 +02:00
if (!inDialogueMenu) {
actorsUpdate();
2017-08-22 18:57:50 +02:00
}
if (_settings->getNewScene() == -1 || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
_sliceRenderer->setView(_view);
// Tick and draw all actors in current set
int setId = _scene->getSetId();
for (int i = 0, end = _gameInfo->getActorCount(); i != end; ++i) {
if (_actors[i]->getSetId() == setId) {
Common::Rect screenRect;
if (_actors[i]->tick(backgroundChanged, &screenRect)) {
_zbuffer->mark(screenRect);
}
}
}
_items->tick();
_itemPickup->tick();
_itemPickup->draw();
2017-07-31 00:11:00 +02:00
Common::Point p = getMousePos();
2017-08-22 18:57:50 +02:00
if (_dialogueMenu->isVisible()) {
_dialogueMenu->tick(p.x, p.y);
_dialogueMenu->draw(_surfaceFront);
2017-08-22 18:57:50 +02:00
}
if (_debugger->_viewZBuffer) {
_surfaceFront.copyRectToSurface(_zbuffer->getData(), 1280, 0, 0, 640, 480);
}
_mouse->tick(p.x, p.y);
_mouse->draw(_surfaceFront, p.x, p.y);
// TODO: Process AUD
2016-10-03 12:38:43 +02:00
if (_walkSoundId >= 0) {
const char *name = _gameInfo->getSfxTrack(_walkSoundId);
_audioPlayer->playAud(name, _walkSoundVolume, _walkSoundBalance, _walkSoundBalance, 50, 0);
_walkSoundId = -1;
}
if (_debugger->_viewSceneObjects) {
_debugger->drawSceneObjects();
}
blitToScreen(_surfaceFront);
_system->delayMillis(10);
}
}
}
void BladeRunnerEngine::actorsUpdate() {
int actorCount = (int)_gameInfo->getActorCount();
int setId = _scene->getSetId();
2018-02-13 23:08:37 +01:00
if (setId != kSetUG18 || _gameVars[kVariableChapter] != 4 || !_gameFlags->query(670) || !_aiScripts->isInsideScript()) {
for (int i = 0; i < actorCount; i++) {
Actor *actor = _actors[i];
if (actor->getSetId() == setId || i == _actorUpdateCounter) {
_aiScripts->update(i);
actor->timersUpdate();
}
}
++_actorUpdateCounter;
if (_actorUpdateCounter >= actorCount) {
_actorUpdateCounter = 0;
}
}
}
void BladeRunnerEngine::walkingReset() {
_mouseClickTimeLast = 0;
_mouseClickTimeDiff = 0;
_walkingToExitId = -1;
_isInsideScriptExit = false;
_walkingToRegionId = -1;
_isInsideScriptRegion = false;
_walkingToObjectId = -1;
_isInsideScriptObject = false;
_walkingToItemId = -1;
_isInsideScriptItem = false;
_walkingToEmpty = false;
_walkingToEmptyX = 0;
_walkingToEmptyY = 0;
_isInsideScriptEmpty = false;
_walkingToActorId = -1;
_isInsideScriptActor = false;
}
void BladeRunnerEngine::handleEvents() {
if (shouldQuit()) {
_gameIsRunning = false;
return;
}
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYUP:
handleKeyUp(event);
break;
case Common::EVENT_KEYDOWN:
handleKeyDown(event);
break;
case Common::EVENT_LBUTTONUP:
handleMouseAction(event.mouse.x, event.mouse.y, true, false);
break;
case Common::EVENT_RBUTTONUP:
case Common::EVENT_MBUTTONUP:
handleMouseAction(event.mouse.x, event.mouse.y, false, false);
break;
2016-10-03 12:38:43 +02:00
case Common::EVENT_LBUTTONDOWN:
handleMouseAction(event.mouse.x, event.mouse.y, true, true);
break;
2016-10-03 12:38:43 +02:00
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_MBUTTONDOWN:
handleMouseAction(event.mouse.x, event.mouse.y, false, true);
break;
2016-10-03 12:38:43 +02:00
default:
; // nothing to do
}
}
}
void BladeRunnerEngine::handleKeyUp(Common::Event &event) {
if (event.kbd.keycode == Common::KEYCODE_RETURN) {
_speechSkipped = true;
}
2018-01-31 00:37:19 +01:00
// TODO:
if (!playerHasControl() /*|| ActorInWalkingLoop*/) {
return;
}
2018-01-31 00:37:19 +01:00
if (_kia->isOpen()) {
_kia->handleKeyUp(event.kbd);
return;
}
2018-01-31 00:37:19 +01:00
if (_spinner->isOpen()) {
return;
}
if (_elevator->isOpen()) {
return;
}
if (_esper->isOpen()) {
return;
}
if (_vk->isOpen()) {
return;
}
2018-01-31 00:37:19 +01:00
if (_dialogueMenu->isOpen()) {
return;
}
//TODO: scores
switch (event.kbd.keycode) {
case Common::KEYCODE_TAB:
_kia->openLastOpened();
break;
case Common::KEYCODE_ESCAPE:
_kia->open(kKIASectionSettings);
break;
case Common::KEYCODE_SPACE:
// TODO: combat::switchCombatMode(&Combat);
break;
default:
break;
}
}
void BladeRunnerEngine::handleKeyDown(Common::Event &event) {
if ((event.kbd.keycode == Common::KEYCODE_d) && (event.kbd.flags & Common::KBD_CTRL)) {
getDebugger()->attach();
getDebugger()->onFrame();
return;
}
2018-01-31 00:37:19 +01:00
//TODO:
if (!playerHasControl() /* || ActorWalkingLoop || ActorSpeaking || VqaIsPlaying */) {
2018-01-31 00:37:19 +01:00
return;
}
if (_kia->isOpen()) {
_kia->handleKeyDown(event.kbd);
}
2018-01-31 00:37:19 +01:00
if (_spinner->isOpen()) {
return;
}
if (_elevator->isOpen()) {
return;
}
if (_esper->isOpen()) {
return;
}
if (_dialogueMenu->isOpen()) {
return;
}
//TODO: scores
switch (event.kbd.keycode) {
case Common::KEYCODE_F1:
_kia->open(kKIASectionHelp);
break;
case Common::KEYCODE_F2:
_kia->open(kKIASectionSave);
break;
case Common::KEYCODE_F3:
_kia->open(kKIASectionLoad);
break;
case Common::KEYCODE_F4:
_kia->open(kKIASectionCrimes);
break;
case Common::KEYCODE_F5:
_kia->open(kKIASectionSuspects);
break;
case Common::KEYCODE_F6:
_kia->open(kKIASectionClues);
break;
case Common::KEYCODE_F10:
_kia->open(kKIASectionQuit);
break;
default:
break;
}
}
void BladeRunnerEngine::handleMouseAction(int x, int y, bool mainButton, bool buttonDown) {
x = CLIP(x, 0, 639);
y = CLIP(y, 0, 479);
int timeNow = getTotalPlayTime();
if (buttonDown) {
_mouseClickTimeDiff = timeNow - _mouseClickTimeLast;
_mouseClickTimeLast = timeNow;
}
if (!playerHasControl() || _mouse->isDisabled()) {
return;
}
2018-01-31 00:37:19 +01:00
if (_kia->isOpen()) {
if (buttonDown) {
_kia->handleMouseDown(x, y, mainButton);
} else {
_kia->handleMouseUp(x, y, mainButton);
}
return;
}
2017-07-31 00:11:00 +02:00
if (_spinner->isOpen()) {
if (buttonDown) {
_spinner->handleMouseDown(x, y);
} else {
_spinner->handleMouseUp(x, y);
2018-01-31 00:37:19 +01:00
}
return;
}
if (_esper->isOpen()) {
if (buttonDown) {
_esper->handleMouseDown(x, y, mainButton);
2018-01-31 00:37:19 +01:00
} else {
_esper->handleMouseUp(x, y, mainButton);
2017-07-31 00:11:00 +02:00
}
return;
}
if (_vk->isOpen()) {
if (buttonDown) {
_vk->handleMouseDown(x, y, mainButton);
} else {
_vk->handleMouseUp(x, y, mainButton);
}
return;
}
2017-08-24 22:34:40 +02:00
if (_elevator->isOpen()) {
if (buttonDown) {
_elevator->handleMouseDown(x, y);
} else {
_elevator->handleMouseUp(x, y);
}
return;
}
2017-08-22 18:57:50 +02:00
if (_dialogueMenu->waitingForInput()) {
if (mainButton && !buttonDown) {
2017-08-22 18:57:50 +02:00
_dialogueMenu->mouseUp();
}
return;
}
if (mainButton) {
Vector3 scenePosition = _mouse->getXYZ(x, y);
bool isClickable;
bool isObstacle;
bool isTarget;
int sceneObjectId = _sceneObjects->findByXYZ(&isClickable, &isObstacle, &isTarget, scenePosition, true, false, true);
int exitIndex = _scene->_exits->getRegionAtXY(x, y);
int regionIndex = _scene->_regions->getRegionAtXY(x, y);
if ((sceneObjectId < kSceneObjectOffsetActors || sceneObjectId >= kSceneObjectOffsetActors) && exitIndex >= 0) {
handleMouseClickExit(exitIndex, x, y, buttonDown);
} else if (regionIndex >= 0) {
handleMouseClickRegion(regionIndex, x, y, buttonDown);
} else if (sceneObjectId == -1) {
handleMouseClickEmpty(x, y, scenePosition, buttonDown);
} else if (sceneObjectId >= kSceneObjectOffsetActors && sceneObjectId < kSceneObjectOffsetItems) {
handleMouseClickActor(sceneObjectId - kSceneObjectOffsetActors, mainButton, buttonDown, scenePosition, x, y);
} else if (sceneObjectId >= kSceneObjectOffsetItems && sceneObjectId < kSceneObjectOffsetObjects) {
handleMouseClickItem(sceneObjectId - kSceneObjectOffsetItems, buttonDown);
} else if (sceneObjectId >= kSceneObjectOffsetObjects && sceneObjectId <= 293) {
handleMouseClick3DObject(sceneObjectId - kSceneObjectOffsetObjects, buttonDown, isClickable, isTarget);
}
} else if (buttonDown) {
if (_playerActor->inWalkLoop()) {
_playerActor->stopWalking(false);
}
_combat->change();
}
}
void BladeRunnerEngine::handleMouseClickExit(int exitId, int x, int y, bool buttonDown) {
debug("clicked on exit %d %d %d", exitId, x, y);
if (_isWalkingInterruptible && exitId != _walkingToExitId) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToExitId = exitId;
return;
}
if (buttonDown) {
return;
}
if (_isInsideScriptExit && exitId == _walkingToExitId) {
_playerActor->run();
if (_mouseClickTimeDiff <= 10000) {
_playerActor->increaseFPS();
}
} else {
_walkingToExitId = exitId;
_walkingToRegionId = -1;
_walkingToObjectId = -1;
_walkingToItemId = -1;
_walkingToEmpty = false;
_walkingToActorId = -1;
_isInsideScriptExit = true;
_sceneScript->clickedOnExit(exitId);
_isInsideScriptExit = false;
}
}
void BladeRunnerEngine::handleMouseClickRegion(int regionId, int x, int y, bool buttonDown) {
debug("clicked on region %d %d %d", regionId, x, y);
if (_isWalkingInterruptible && regionId != _walkingToRegionId) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToRegionId = regionId;
return;
}
if (buttonDown || _mouse->isInactive()) {
return;
}
if (_isInsideScriptRegion && regionId == _walkingToRegionId) {
_playerActor->run();
if (_mouseClickTimeDiff <= 10000) {
_playerActor->increaseFPS();
}
} else {
_walkingToExitId = -1;
_walkingToRegionId = regionId;
_walkingToObjectId = -1;
_walkingToItemId = -1;
_walkingToEmpty = false;
_walkingToActorId = -1;
_isInsideScriptRegion = true;
_sceneScript->clickedOn2DRegion(regionId);
_isInsideScriptRegion = false;
}
}
void BladeRunnerEngine::handleMouseClick3DObject(int objectId, bool buttonDown, bool isClickable, bool isTarget) {
const char *objectName = _scene->objectGetName(objectId);
debug("Clicked on object %s", objectName);
if (_isWalkingInterruptible && objectId != _walkingToObjectId) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToObjectId = objectId;
return;
}
if (_mouse->isInactive()) {
return;
}
if (!_combat->isActive()) {
if (buttonDown || !isClickable) {
return;
}
if (_isInsideScriptObject && objectId == _walkingToObjectId) {
_playerActor->run();
if (_mouseClickTimeDiff <= 10000) {
_playerActor->increaseFPS();
}
} else {
_walkingToExitId = -1;
_walkingToRegionId = -1;
_walkingToObjectId = objectId;
_walkingToItemId = -1;
_walkingToEmpty = false;
_walkingToActorId = -1;
_isInsideScriptObject = true;
_sceneScript->clickedOn3DObject(objectName, false);
_isInsideScriptObject = false;
}
} else {
if (!buttonDown || !isTarget) {
return;
}
_playerActor->stopWalking(false);
_playerActor->faceObject(objectName, false);
_playerActor->changeAnimationMode(kAnimationModeCombatAttack, false);
_settings->decreaseAmmo();
_audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getHitSound()), 100, 0, 0, 90, 0);
//TODO mouse::randomize(Mouse);
_isInsideScriptObject = true;
_sceneScript->clickedOn3DObject(objectName, true);
_isInsideScriptObject = false;
}
}
void BladeRunnerEngine::handleMouseClickEmpty(int x, int y, Vector3 &scenePosition, bool buttonDown) {
debug("Clicked on nothing %f, %f, %f", scenePosition.x, scenePosition.y, scenePosition.z);
if (_isWalkingInterruptible) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToEmpty = false;
return;
}
_isInsideScriptEmpty = true;
bool sceneMouseClick = _sceneScript->mouseClick(x, y);
_isInsideScriptEmpty = false;
if (sceneMouseClick) {
return;
}
int actorId = Actor::findTargetUnderMouse(this, x, y);
int itemId = _items->findTargetUnderMouse(x, y);
if (_combat->isActive() && buttonDown && (actorId > 0 || itemId > 0)) {
_playerActor->stopWalking(false);
if (actorId > 0) {
_playerActor->faceActor(actorId, false);
} else {
_playerActor->faceItem(itemId, false);
}
_playerActor->changeAnimationMode(kAnimationModeCombatAttack, false);
_settings->decreaseAmmo();
_audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getMissSound()), 100, 0, 0, 90, 0);
//TODO mouse::randomize(Mouse);
if (actorId > 0) {
_aiScripts->shotAtAndMissed(actorId);
}
} else {
if (buttonDown) {
return;
}
_walkingToExitId = -1;
_walkingToRegionId = -1;
_walkingToObjectId = -1;
_walkingToItemId = -1;
_walkingToEmpty = true;
_walkingToActorId = -1;
if (_combat->isActive() && (actorId > 0 || itemId > 0)) {
return;
}
int xDist = abs(_walkingToEmptyX - x);
int yDist = abs(_walkingToEmptyY - y);
_walkingToEmptyX = x;
_walkingToEmptyY = y;
bool inWalkbox = false;
float altitude = _scene->_set->getAltitudeAtXZ(scenePosition.x, scenePosition.z, &inWalkbox);
if (!inWalkbox || scenePosition.y >= altitude + 6.0f) {
return;
}
bool shouldRun = _playerActor->isRunning();
if (_mouseClickTimeDiff <= 10000 && xDist < 10 && yDist < 10) {
shouldRun = true;
}
_playerActor->walkTo(shouldRun, scenePosition, false);
if (shouldRun && _playerActor->isWalking()) {
_playerActor->increaseFPS();
}
}
}
void BladeRunnerEngine::handleMouseClickItem(int itemId, bool buttonDown) {
debug("Clicked on item %d", itemId);
if (_isWalkingInterruptible && itemId != _walkingToItemId) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToItemId = itemId;
return;
}
if (_mouse->isInactive()) {
return;
}
if (!_combat->isActive()) {
if (buttonDown) {
return;
}
if (_isInsideScriptItem && itemId == _walkingToItemId) {
_playerActor->run();
if (_mouseClickTimeDiff <= 10000) {
_playerActor->increaseFPS();
}
} else {
_walkingToExitId = -1;
_walkingToRegionId = -1;
_walkingToObjectId = -1;
_walkingToItemId = itemId;
_walkingToEmpty = false;
_walkingToActorId = -1;
_isInsideScriptItem = true;
_sceneScript->clickedOnItem(itemId, false);
_isInsideScriptItem = false;
}
} else {
if (!buttonDown || !_items->isTarget(itemId) /* || _mouse->isRandomized() */) {
return;
}
_playerActor->stopWalking(false);
_playerActor->faceItem(itemId, false);
_playerActor->changeAnimationMode(kAnimationModeCombatAttack, false);
_settings->decreaseAmmo();
_audioPlayer->playAud(_gameInfo->getSfxTrack(_combat->getHitSound()), 100, 0, 0, 90, 0);
//TODO mouse::randomize(Mouse);
_isInsideScriptItem = true;
_sceneScript->clickedOnItem(itemId, true);
_isInsideScriptItem = false;
}
}
void BladeRunnerEngine::handleMouseClickActor(int actorId, bool mainButton, bool buttonDown, Vector3 &scenePosition, int x, int y) {
debug("Clicked on actor %d", actorId);
if (_isWalkingInterruptible && actorId != _walkingToActorId) {
_isWalkingInterruptible = false;
_interruptWalking = true;
walkingReset();
_walkingToActorId = actorId;
return;
}
if (_mouse->isInactive()) {
return;
}
if (!buttonDown) {
if (actorId == kActorMcCoy) {
if (mainButton) {
if (!_combat->isActive()) {
_kia->openLastOpened();
}
} else if (!_playerActor->inWalkLoop()) {
_combat->change();
}
return;
}
if (_isInsideScriptActor && actorId == _walkingToActorId) {
_playerActor->run();
if (_mouseClickTimeDiff <= 10000) {
_playerActor->increaseFPS();
}
} else {
_walkingToExitId = -1;
_walkingToRegionId = -1;
_walkingToObjectId = -1;
_walkingToItemId = -1;
_walkingToEmpty = false;
_walkingToActorId = actorId;
_isInsideScriptActor = true;
bool processedBySceneScript = _sceneScript->clickedOnActor(actorId);
_isInsideScriptActor = false;
if (!_combat->isActive() && !processedBySceneScript) {
_aiScripts->clickedByPlayer(actorId);
}
}
} else {
Actor *actor = _actors[actorId];
if (!_combat->isActive() || actorId == kActorMcCoy || !actor->isTarget() || actor->isRetired() /*|| _mouse->isRandomized()*/) {
return;
}
_playerActor->stopWalking(false);
_playerActor->faceActor(actorId, false);
_playerActor->changeAnimationMode(kAnimationModeCombatAttack, false);
_settings->decreaseAmmo();
bool missed = _playerActor->isObstacleBetween(actor->getXYZ());
_audioPlayer->playAud(_gameInfo->getSfxTrack(missed ? _combat->getMissSound() : _combat->getHitSound()), 100, 0, 0, 90, 0);
//TODO mouse::randomize(Mouse);
if (missed) {
_aiScripts->shotAtAndMissed(actorId);
} else {
_isInsideScriptActor = true;
bool canShoot = _aiScripts->shotAtAndHit(actorId);
_isInsideScriptActor = false;
if (!canShoot) {
_combat->shoot(actorId, scenePosition, x);
}
}
}
}
void BladeRunnerEngine::gameWaitForActive() {
while (!_windowIsActive) {
handleEvents();
}
}
void BladeRunnerEngine::loopActorSpeaking() {
if (!_audioSpeech->isPlaying()) {
return;
}
playerLosesControl();
do {
gameTick();
} while (_gameIsRunning && _audioSpeech->isPlaying());
playerGainsControl();
}
void BladeRunnerEngine::outtakePlay(int id, bool noLocalization, int container) {
Common::String name = _gameInfo->getOuttake(id);
OuttakePlayer player(this);
player.play(name, noLocalization, container);
}
bool BladeRunnerEngine::openArchive(const Common::String &name) {
2018-01-28 11:14:29 +01:00
int i;
// If archive is already open, return true
for (i = 0; i != kArchiveCount; ++i) {
if (_archives[i].isOpen() && _archives[i].getName() == name) {
return true;
}
}
// Find first available slot
for (i = 0; i != kArchiveCount; ++i) {
if (!_archives[i].isOpen()) {
break;
}
}
if (i == kArchiveCount) {
/* TODO: BLADE.EXE retires the least recently used
* archive when it runs out of slots. */
error("openArchive: No more archive slots");
return false;
}
_archives[i].open(name);
return _archives[i].isOpen();
}
bool BladeRunnerEngine::closeArchive(const Common::String &name) {
2018-01-28 11:14:29 +01:00
for (int i = 0; i != kArchiveCount; ++i) {
if (_archives[i].isOpen() && _archives[i].getName() == name) {
_archives[i].close();
return true;
}
}
debug("closeArchive: Archive %s not open.", name.c_str());
return false;
}
bool BladeRunnerEngine::isArchiveOpen(const Common::String &name) const {
2018-01-28 11:14:29 +01:00
for (int i = 0; i != kArchiveCount; ++i) {
if (_archives[i].isOpen() && _archives[i].getName() == name)
return true;
}
return false;
}
Common::SeekableReadStream *BladeRunnerEngine::getResourceStream(const Common::String &name) {
2018-01-28 11:14:29 +01:00
for (int i = 0; i != kArchiveCount; ++i) {
if (!_archives[i].isOpen()) {
continue;
}
if (false) {
debug("getResource: Searching archive %s for %s.", _archives[i].getName().c_str(), name.c_str());
}
Common::SeekableReadStream *stream = _archives[i].createReadStreamForMember(name);
if (stream) {
return stream;
}
}
warning("getResource: Resource %s not found", name.c_str());
return nullptr;
}
bool BladeRunnerEngine::playerHasControl() {
return _playerLosesControlCounter == 0;
}
void BladeRunnerEngine::playerLosesControl() {
if (++_playerLosesControlCounter == 1) {
_mouse->disable();
}
// debug("Player Lost Control (%d)", _playerLosesControlCounter);
}
void BladeRunnerEngine::playerGainsControl() {
if (_playerLosesControlCounter == 0) {
warning("Unbalanced call to BladeRunnerEngine::playerGainsControl");
}
if (_playerLosesControlCounter > 0)
--_playerLosesControlCounter;
// debug("Player Gained Control (%d)", _playerLosesControlCounter);
if (_playerLosesControlCounter == 0) {
_mouse->enable();
}
}
2018-03-17 16:14:48 +01:00
bool BladeRunnerEngine::saveGame(const Common::String &filename, byte *thumbnail) {
warning("BladeRunnerEngine::saveGame not finished");
if (!playerHasControl() || _sceneScript->isInsideScript() || _aiScripts->isInsideScript()) {
return false;
}
Common::OutSaveFile *commonSaveFile = getSaveFileManager()->openForSaving(filename, false);
if (commonSaveFile->err()) {
return false;
}
SaveFile s(commonSaveFile);
s.padBytes(9600); // TODO: thumbnail
s.write(-1.0f);
_settings->save(s);
// s.debug(" - SCENE - ");
_scene->save(s);
// s.debug(" - EXIST - ");
_scene->_exits->save(s);
// s.debug(" - REGIONS - ");
_scene->_regions->save(s);
// s.debug(" - SET - ");
_scene->_set->save(s);
// s.debug(" - GAMEVARS - ");
for (uint i = 0; i != _gameInfo->getGlobalVarCount(); ++i) {
s.write(_gameVars[i]);
}
// TODO
// _music->save(s);
// s.debug(" - MUSIC - ");
s.padBytes(0x56);
// _audioPlayer->save(s) // zero func
// _audioSpeech->save(s) // zero func
// s.debug(" - COMBAT - ");
_combat->save(s);
// s.debug(" - GAMEFLAGS - ");
_gameFlags->save(s);
// s.debug(" - ITEMS - ");
_items->save(s);
// s.debug(" - SCENEOBJECTS - ");
_sceneObjects->save(s);
// s.debug(" - AMBIENTSOUNDS - ");
_ambientSounds->save(s);
// s.debug(" - OVERLAYS - ");
_overlays->save(s);
// s.debug(" - SPINNER - ");
_spinner->save(s);
// TODO
// _scores->save(s);
s.padBytes(0x28);
_dialogueMenu->save(s);
_obstacles->save(s);
_actorDialogueQueue->save(s);
_waypoints->save(s);
for (uint i = 0; i != _gameInfo->getActorCount(); ++i) {
_actors[i]->save(s);
int animationState, animationFrame, a3, a4;
_aiScripts->queryAnimationState(i, &animationState, &animationFrame, &a3, &a4);
s.write(animationState);
s.write(animationFrame);
s.write(a3);
s.write(a4);
}
_actors[kActorVoiceOver]->save(s);
_policeMaze->save(s);
_crimesDatabase->save(s);
s.finalize();
assert(0 && "ok");
return !commonSaveFile->err();
}
void BladeRunnerEngine::ISez(const char *str) {
debug("\t%s", str);
}
void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) {
_system->copyRectToScreen(src.getPixels(), src.pitch, 0, 0, src.w, src.h);
_system->updateScreen();
}
GUI::Debugger *BladeRunnerEngine::getDebugger() {
return _debugger;
}
void blit(const Graphics::Surface &src, Graphics::Surface &dst) {
dst.copyRectToSurface(src.getPixels(), src.pitch, 0, 0, src.w, src.h);
}
} // End of namespace BladeRunner