diff options
Diffstat (limited to 'engines/bladerunner')
38 files changed, 1390 insertions, 645 deletions
diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp index 85c5d89240..c29956ca8b 100644 --- a/engines/bladerunner/actor.cpp +++ b/engines/bladerunner/actor.cpp @@ -23,7 +23,6 @@ #include "bladerunner/actor.h" #include "bladerunner/bladerunner.h" - #include "bladerunner/actor_clues.h" #include "bladerunner/actor_combat.h" #include "bladerunner/actor_walk.h" @@ -37,6 +36,7 @@ #include "bladerunner/scene_objects.h" #include "bladerunner/script/scene.h" #include "bladerunner/script/ai.h" +#include "bladerunner/set.h" #include "bladerunner/slice_animations.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/waypoints.h" @@ -767,7 +767,7 @@ void Actor::setBoundingBox(const Vector3 &position, bool retired) { float Actor::distanceFromView(View *view) const{ float xDist = this->_position.x - view->_cameraPosition.x; - float zDist = this->_position.z - view->_cameraPosition.z; + float zDist = this->_position.z + view->_cameraPosition.z; return sqrt(xDist * xDist + zDist * zDist); } @@ -863,7 +863,7 @@ void Actor::faceHeading(int heading, bool animate) { } void Actor::modifyFriendlinessToOther(int otherActorId, signed int change) { - _friendlinessToOther[otherActorId] = MIN(MAX(_friendlinessToOther[otherActorId] + change, 0), 100); + _friendlinessToOther[otherActorId] = CLIP(_friendlinessToOther[otherActorId] + change, 0, 100); } void Actor::setFriendlinessToOther(int otherActorId, int friendliness) { @@ -895,29 +895,29 @@ void Actor::setImmunityToObstacles(bool isImmune) { } void Actor::modifyCurrentHP(signed int change) { - _currentHP = MIN(MAX(_currentHP + change, 0), 100); + _currentHP = CLIP(_currentHP + change, 0, 100); if (_currentHP > 0) retire(false, 0, 0, -1); } void Actor::modifyMaxHP(signed int change) { - _maxHP = MIN(MAX(_maxHP + change, 0), 100); + _maxHP = CLIP(_maxHP + change, 0, 100); } void Actor::modifyCombatAggressiveness(signed int change) { - _combatAggressiveness = MIN(MAX(_combatAggressiveness + change, 0), 100); + _combatAggressiveness = CLIP(_combatAggressiveness + change, 0, 100); } void Actor::modifyHonesty(signed int change) { - _honesty = MIN(MAX(_honesty + change, 0), 100); + _honesty = CLIP(_honesty + change, 0, 100); } void Actor::modifyIntelligence(signed int change) { - _intelligence = MIN(MAX(_intelligence + change, 0), 100); + _intelligence = CLIP(_intelligence + change, 0, 100); } void Actor::modifyStability(signed int change) { - _stability = MIN(MAX(_stability + change, 0), 100); + _stability = CLIP(_stability + change, 0, 100); } void Actor::setFlagDamageAnimIfMoving(bool value) { @@ -1043,7 +1043,7 @@ void Actor::speechPlay(int sentenceId, bool voiceOver) { int screenX = 320; //, screenY = 0; //TODO: transform to screen space using fov; balance = 127 * (2 * screenX - 640) / 640; - balance = MIN(127, MAX(-127, balance)); + balance = CLIP<int>(balance, -127, 127); } _vm->_audioSpeech->playSpeech(name, balance); @@ -1092,12 +1092,12 @@ void Actor::copyClues(int actorId) { int Actor::soundVolume() const { float dist = distanceFromView(_vm->_view); - return 255.0f * MAX(MIN(dist / 1200.0f, 1.0f), 0.0f); + return 35.0f * CLIP(1.0f - (dist / 1200.0f), 0.0f, 1.0f); } int Actor::soundBalance() const { Vector3 screenPosition = _vm->_view->calculateScreenPosition(_position); - return 127.0f * (MAX(MIN(screenPosition.x / 640.0f, 1.0f), 0.0f) * 2.0f - 1.0f); + return 35.0f * (CLIP(screenPosition.x / 640.0f, 0.0f, 1.0f) * 2.0f - 1.0f); } bool Actor::walkFindU1(const Vector3 &startPosition, const Vector3 &targetPosition, float size, Vector3 *newDestination) { diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp index 749dda2b15..cd848f9fbf 100644 --- a/engines/bladerunner/actor_clues.cpp +++ b/engines/bladerunner/actor_clues.cpp @@ -22,6 +22,8 @@ #include "bladerunner/actor_clues.h" +#include "bladerunner/bladerunner.h" +#include "bladerunner/gameinfo.h" #include "bladerunner/crimes_database.h" #include "common/debug.h" diff --git a/engines/bladerunner/actor_clues.h b/engines/bladerunner/actor_clues.h index f7ec877ad2..d053ef532b 100644 --- a/engines/bladerunner/actor_clues.h +++ b/engines/bladerunner/actor_clues.h @@ -23,12 +23,10 @@ #ifndef BLADERUNNER_ACTOR_CLUES_H #define BLADERUNNER_ACTOR_CLUES_H -#include "bladerunner/bladerunner.h" - -#include "bladerunner/gameinfo.h" - namespace BladeRunner { +class BladeRunnerEngine; + struct ActorClue { int _clueId; int _weight; diff --git a/engines/bladerunner/actor_combat.h b/engines/bladerunner/actor_combat.h index ee29bf4f0e..3763f46be8 100644 --- a/engines/bladerunner/actor_combat.h +++ b/engines/bladerunner/actor_combat.h @@ -23,12 +23,12 @@ #ifndef BLADERUNNER_ACTOR_COMBAT_H #define BLADERUNNER_ACTOR_COMBAT_H -#include "bladerunner/bladerunner.h" - #include "bladerunner/vector.h" namespace BladeRunner { +class BladeRunnerEngine; + class ActorCombat { BladeRunnerEngine *_vm; diff --git a/engines/bladerunner/ambient_sounds.cpp b/engines/bladerunner/ambient_sounds.cpp index aaf6c016d3..7790b03506 100644 --- a/engines/bladerunner/ambient_sounds.cpp +++ b/engines/bladerunner/ambient_sounds.cpp @@ -34,7 +34,7 @@ namespace BladeRunner { #define NON_LOOPING_SOUNDS 25 #define LOOPING_SOUNDS 3 -AmbientSounds::AmbientSounds(BladeRunnerEngine *vm) : _vm(vm) { +AmbientSounds::AmbientSounds(BladeRunnerEngine *vm) : _vm(vm) { _nonLoopingSounds = new NonLoopingSound[NON_LOOPING_SOUNDS]; _loopingSounds = new LoopingSound[LOOPING_SOUNDS]; _ambientVolume = 65; @@ -64,45 +64,133 @@ static inline void sort(int &a, int &b) { } void AmbientSounds::addSound( - int id, - int timeRangeBegin, int timeRangeEnd, - int volumeRangeBegin, int volumeRangeEnd, - int unk1RangeBegin, int unk1RangeEnd, - int unk2RangeBegin, int unk2RangeEnd, - int priority, int unk3) { - const char *name = _vm->_gameInfo->getSfxTrack(id); - - sort(volumeRangeBegin, volumeRangeEnd); - sort(unk1RangeBegin, unk1RangeEnd); - sort(unk2RangeBegin, unk2RangeEnd); + int sfxId, + int timeMin, int timeMax, + int volumeMin, int volumeMax, + int panStartMin, int panStartMax, + int panEndMin, int panEndMax, + int priority, int unk) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); + + sort(volumeMin, volumeMax); + sort(panStartMin, panStartMax); + sort(panEndMin, panEndMax); addSoundByName( - name, - timeRangeBegin, timeRangeEnd, - volumeRangeBegin, volumeRangeEnd, - unk1RangeBegin, unk1RangeEnd, - unk2RangeBegin, unk2RangeEnd, - priority, unk3 - ); + name, + timeMin, timeMax, + volumeMin, volumeMax, + panStartMin, panStartMax, + panEndMin, panEndMax, + priority, unk + ); } -void AmbientSounds::addLoopingSound(int sfx_id, int volume, int unk, int fadeInTime) { - const char *name = _vm->_gameInfo->getSfxTrack(sfx_id); +void AmbientSounds::removeNonLoopingSound(int sfxId, bool stopPlaying) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); + int32 hash = mix_id(name); + int index = findNonLoopingTrackByHash(hash); + if (index >= 0) { + removeNonLoopingSoundByIndex(index, stopPlaying); + } +} + +void AmbientSounds::removeAllNonLoopingSounds(bool stopPlaying) { + for (int i = 0; i < NON_LOOPING_SOUNDS; i++) { + removeNonLoopingSoundByIndex(i, stopPlaying); + } +} + +void AmbientSounds::addSpeech(int actorId, int sentenceId, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk) { + sort(volumeMin, volumeMax); + sort(panStartMin, panStartMax); + sort(panEndMin, panEndMax); + + char name[13]; + sprintf(name, "%02d-%04d.AUD", actorId, sentenceId); //TODO somewhere here should be also language code + addSoundByName(name, + timeMin, timeMax, + volumeMin, volumeMax, + panStartMin, panStartMax, + panEndMin, panEndMax, + priority, unk); +} + +void AmbientSounds::playSound(int sfxId, int volume, int panStart, int panEnd, int priority) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); + + _vm->_audioPlayer->playAud(name, volume * _ambientVolume / 100, panStart, panEnd, priority, AudioPlayer::OVERRIDE_VOLUME); +} + +void AmbientSounds::addLoopingSound(int sfxId, int volume, int pan, int delay) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); int32 hash = mix_id(name); - if (findLoopingTrackByHash(hash) >= 0) + if (findLoopingTrackByHash(hash) >= 0) { return; + } int i = findAvailableLoopingTrack(); - if (i == -1) + if (i == -1) { return; + } + LoopingSound &track = _loopingSounds[i]; + + track.isActive = true; + strcpy(track.name, name); + track.hash = hash; + track.pan = pan; + track.volume = volume; + + int actualVolumeStart = volume * _ambientVolume / 100; + int actualVolumeEnd = actualVolumeStart; + + if (delay > 0) { + actualVolumeStart = 0; + } + + track.audioPlayerTrack = _vm->_audioPlayer->playAud(name, actualVolumeStart, pan, pan, 99, AudioPlayer::LOOP | AudioPlayer::OVERRIDE_VOLUME); + + if (track.audioPlayerTrack == -1) { + removeLoopingSoundByIndex(i, 0); + } else { + if (delay) { + _vm->_audioPlayer->adjustVolume(track.audioPlayerTrack, actualVolumeEnd, delay, false); + } + } +} + +void AmbientSounds::adjustLoopingSound(int sfxId, int volume, int pan, int delay) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); + int32 hash = mix_id(name); + int index = findLoopingTrackByHash(hash); - int actualVolume = volume * _ambientVolume / 100; + if (index >= 0 && _loopingSounds[index].audioPlayerTrack != -1 && _vm->_audioPlayer->isActive(_loopingSounds[index].audioPlayerTrack)) { + if (volume != -1) { + _loopingSounds[index].volume = volume; + _vm->_audioPlayer->adjustVolume(_loopingSounds[index].audioPlayerTrack, _ambientVolume * volume / 100, delay, false); + } + if (pan != -101) { + _loopingSounds[index].pan = pan; + _vm->_audioPlayer->adjustPan(_loopingSounds[index].audioPlayerTrack, pan, delay); + } + } +} - int balance = 0; +void AmbientSounds::removeLoopingSound(int sfxId, int delay) { + const char *name = _vm->_gameInfo->getSfxTrack(sfxId); + int32 hash = mix_id(name); + int index = findLoopingTrackByHash(hash); + if (index >= 0) { + removeLoopingSoundByIndex(index, delay); + } +} - _vm->_audioPlayer->playAud(name, actualVolume, balance, balance, 100, AudioPlayer::LOOP | AudioPlayer::OVERRIDE_VOLUME); +void AmbientSounds::removeAllLoopingSounds(int delay) { + for (int i = 0; i < LOOPING_SOUNDS; i++) { + removeLoopingSoundByIndex(i, delay); + } } void AmbientSounds::tick() { @@ -111,37 +199,38 @@ void AmbientSounds::tick() { for (int i = 0; i != NON_LOOPING_SOUNDS; ++i) { NonLoopingSound &track = _nonLoopingSounds[i]; - if (!track.isActive || track.nextPlayTime > now) + if (!track.isActive || track.nextPlayTime > now) { continue; + } - int pan1, pan2; - - pan1 = _vm->_rnd.getRandomNumberRng(track.pan1begin, track.pan1end); - if (track.pan2begin == -101) { - pan2 = pan1; + int panEnd; + int panStart = _vm->_rnd.getRandomNumberRng(track.panStartMin, track.panStartMax); + if (track.panEndMin == -101) { + panEnd = panStart; } else { - pan2 = _vm->_rnd.getRandomNumberRng(track.pan2begin, track.pan2end); + panEnd = _vm->_rnd.getRandomNumberRng(track.panEndMin, track.panEndMax); } - track.volume = _vm->_rnd.getRandomNumberRng(track.volume1, track.volume2); + track.volume = _vm->_rnd.getRandomNumberRng(track.volumeMin, track.volumeMax); - track.audio_player_track = _vm->_audioPlayer->playAud( - track.name, - track.volume * _ambientVolume / 100, - pan1, pan2, - track.priority, - AudioPlayer::OVERRIDE_VOLUME - ); - - track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.time1, track.time2); + track.audioPlayerTrack = _vm->_audioPlayer->playAud( + track.name, + track.volume * _ambientVolume / 100, + panStart, + panEnd, + track.priority, + AudioPlayer::OVERRIDE_VOLUME + ); + track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.timeMin, track.timeMax); } } int AmbientSounds::findAvailableNonLoopingTrack() { for (int i = 0; i != NON_LOOPING_SOUNDS; ++i) { - if (!_nonLoopingSounds[i].isActive) + if (!_nonLoopingSounds[i].isActive) { return i; + } } return -1; @@ -151,8 +240,9 @@ int AmbientSounds::findNonLoopingTrackByHash(int32 hash) { for (int i = 0; i != NON_LOOPING_SOUNDS; ++i) { NonLoopingSound &track = _nonLoopingSounds[i]; - if (track.isActive && track.hash == hash) + if (track.isActive && track.hash == hash) { return i; + } } return -1; @@ -160,8 +250,9 @@ int AmbientSounds::findNonLoopingTrackByHash(int32 hash) { int AmbientSounds::findAvailableLoopingTrack() { for (int i = 0; i != LOOPING_SOUNDS; ++i) { - if (!_loopingSounds[i].isActive) + if (!_loopingSounds[i].isActive) { return i; + } } return -1; @@ -171,46 +262,77 @@ int AmbientSounds::findLoopingTrackByHash(int32 hash) { for (int i = 0; i != LOOPING_SOUNDS; ++i) { LoopingSound &track = _loopingSounds[i]; - if (track.isActive && track.hash == hash) + if (track.isActive && track.hash == hash) { return i; + } } return -1; } void AmbientSounds::addSoundByName( - const char *name, - int timeRangeBegin, int timeRangeEnd, - int volumeRangeBegin, int volumeRangeEnd, - int pan1begin, int pan1end, - int pan2begin, int pan2end, - int priority, int unk3) { + const char *name, + int timeMin, int timeMax, + int volumeMin, int volumeMax, + int panStartMin, int panStartMax, + int panEndMin, int panEndMax, + int priority, int unk) { if (strlen(name) > 12) { error("AmbientSounds::addSoundByName: Overlong name '%s'", name); } int i = findAvailableNonLoopingTrack(); - if (i < 0) + if (i < 0) { return; + } NonLoopingSound &track = _nonLoopingSounds[i]; - uint32 now = g_system->getMillis(); + uint32 now = _vm->getTotalPlayTime(); - track.isActive = true; + track.isActive = true; strcpy(track.name, name); - track.hash = mix_id(name); - track.time1 = 1000 * timeRangeBegin; - track.time2 = 1000 * timeRangeEnd; - track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.time1, track.time2); - track.volume1 = volumeRangeBegin; - track.volume2 = volumeRangeEnd; - track.volume = 0; - track.pan1begin = pan1begin; - track.pan1end = pan1end; - track.pan2begin = pan2begin; - track.pan2end = pan2end; - track.priority = priority; + track.hash = mix_id(name); + track.timeMin = 1000 * timeMin; + track.timeMax = 1000 * timeMax; + track.nextPlayTime = now + _vm->_rnd.getRandomNumberRng(track.timeMin, track.timeMax); + track.volumeMin = volumeMin; + track.volumeMax = volumeMax; + track.volume = 0; + track.panStartMin = panStartMin; + track.panStartMax = panStartMax; + track.panEndMin = panEndMin; + track.panEndMax = panEndMax; + track.priority = priority; +} + +void AmbientSounds::removeNonLoopingSoundByIndex(int index, bool stopPlaying) { + NonLoopingSound &track = _nonLoopingSounds[index]; + if (stopPlaying) { + if (track.isActive && track.audioPlayerTrack != -1 && _vm->_audioPlayer->isActive(track.audioPlayerTrack)) { + _vm->_audioPlayer->stop(track.audioPlayerTrack, stopPlaying); + } + } + track.isActive = false; + track.audioPlayerTrack = -1; + // track.field_45 = 0; +} + +void AmbientSounds::removeLoopingSoundByIndex(int index, int delay) { + LoopingSound &track = _loopingSounds[index]; + if (track.isActive && track.audioPlayerTrack != -1 && _vm->_audioPlayer->isActive(track.audioPlayerTrack)) { + if (delay > 0) { + _vm->_audioPlayer->adjustVolume(track.audioPlayerTrack, 0, delay, false); + } else { + _vm->_audioPlayer->stop(track.audioPlayerTrack, false); + } + } + track.isActive = false; + track.name[0] = 0; + track.hash = 0; + track.audioPlayerTrack = -1; + track.volume = 0; + track.pan = 0; } } // End of namespace BladeRunner diff --git a/engines/bladerunner/ambient_sounds.h b/engines/bladerunner/ambient_sounds.h index e5428e9f80..292d8173f2 100644 --- a/engines/bladerunner/ambient_sounds.h +++ b/engines/bladerunner/ambient_sounds.h @@ -30,23 +30,21 @@ namespace BladeRunner { class BladeRunnerEngine; class AmbientSounds { - BladeRunnerEngine *_vm; - struct NonLoopingSound { bool isActive; char name[13]; int32 hash; - int32 audio_player_track; - int32 time1; - int32 time2; + int32 audioPlayerTrack; + int32 timeMin; + int32 timeMax; uint32 nextPlayTime; - int32 volume1; - int32 volume2; + int32 volumeMin; + int32 volumeMax; int32 volume; - int32 pan1begin; - int32 pan1end; - int32 pan2begin; - int32 pan2end; + int32 panStartMin; + int32 panStartMax; + int32 panEndMin; + int32 panEndMax; int32 priority; }; @@ -54,9 +52,13 @@ class AmbientSounds { bool isActive; char name[13]; int32 hash; + int audioPlayerTrack; int32 volume; + int pan; }; + BladeRunnerEngine *_vm; + NonLoopingSound *_nonLoopingSounds; LoopingSound *_loopingSounds; int _ambientVolume; @@ -66,25 +68,31 @@ public: ~AmbientSounds(); void addSound( - int id, - int timeRangeBegin, int timeRangeEnd, - int volumeRangeBegin, int volumeRangeEnd, - int pan1begin, int pan1end, - int pan2begin, int pan2end, - int priority, int unk3 + int sfxId, + int timeMin, int timeMax, + int volumeMin, int volumeMax, + int panStartMin, int panStartMax, + int panEndMin, int panEndMax, + int priority, int unk ); - // removeSound - // addSpeechSound - // removeSpeechSound - // playSound - // playSpeech - // removeAllNonLoopingSounds - - // addLoopingSound - void addLoopingSound(int sfx_id, int volume, int unk, int fadeInTime); - // adjustLoopingSound - // removeLoopingSound - // removeAllLoopingSounds + void removeNonLoopingSound(int sfxId, bool stopPlaying); + void removeAllNonLoopingSounds(bool stopPlaying); + + void addSpeech( + int actorId, int sentenceId, + int timeMin, int timeMax, + int volumeMin, int volumeMax, + int panStartMin, int panStartMax, + int panEndMin, int panEndMax, + int priority, int unk); + void playSound(int sfxId, int volume, int panStart, int panEnd, int priority); + + void addLoopingSound(int sfxId, int volume, int pan, int delay); + void adjustLoopingSound(int sfxId, int volume, int pan, int delay); + // it seems there is little confusion in original code about delay parameter, + // sometimes it is used as boolean in same way as stopPlaying from non looping + void removeLoopingSound(int sfxId, int delay); + void removeAllLoopingSounds(int delay); void tick(); @@ -108,11 +116,14 @@ private: void addSoundByName( const char *name, - int timeRangeBegin, int timeRangeEnd, - int volumeRangeBegin, int volumeRangeEnd, - int unk1RangeBegin, int unk1RangeEnd, - int unk2RangeBegin, int unk2RangeEnd, - int priority, int unk3); + int timeMin, int timeMax, + int volumeMin, int volumeMax, + int panStartMin, int panStartMax, + int panEndMin, int panEndMax, + int priority, int unk); + + void removeNonLoopingSoundByIndex(int index, bool stopPlaying); + void removeLoopingSoundByIndex(int index, int delay); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/archive.cpp b/engines/bladerunner/archive.cpp index 005145599d..9c19ced88c 100644 --- a/engines/bladerunner/archive.cpp +++ b/engines/bladerunner/archive.cpp @@ -145,7 +145,7 @@ Common::SeekableReadStream *MIXArchive::createReadStreamForMember(const Common:: uint32 i = indexForId(id); if (i == _entry_count) - return NULL; + return nullptr; uint32 start = _entries[i].offset + 6 + 12 * _entry_count; uint32 end = _entries[i].length + start; diff --git a/engines/bladerunner/aud_stream.cpp b/engines/bladerunner/aud_stream.cpp index e859b3347e..c74421bbd0 100644 --- a/engines/bladerunner/aud_stream.cpp +++ b/engines/bladerunner/aud_stream.cpp @@ -41,11 +41,15 @@ AudStream::AudStream(AudioCache *cache, int32 hash) void AudStream::init(byte *data) { _data = data; - _end = _data + READ_LE_UINT32(_data + 2) + 12; - assert(_end - _data >= 12); - + _frequency = READ_LE_UINT16(_data); + _size = READ_LE_UINT32(_data + 2); + _sizeDecompressed = READ_LE_UINT32(_data + 6); + _flags = *(_data + 10); _compressionType = *(_data + 11); + _end = _data + _size + 12; + assert(_end - _data >= 12); + _deafBlockRemain = 0; _p = _data + 12; } @@ -113,4 +117,16 @@ bool AudStream::rewind() { return true; } +int AudStream::getLength() +{ + int bytesPerSecond = _frequency; + if (_flags & 1) { // 16 bit + bytesPerSecond *= 2; + } + if (_flags & 2) { // stereo + bytesPerSecond *= 2; + } + return (1000 * _sizeDecompressed) / bytesPerSecond; +} + } // End of namespace BladeRunner diff --git a/engines/bladerunner/aud_stream.h b/engines/bladerunner/aud_stream.h index 3279bdada3..f3117d7072 100644 --- a/engines/bladerunner/aud_stream.h +++ b/engines/bladerunner/aud_stream.h @@ -39,8 +39,12 @@ class AudStream : public Audio::RewindableAudioStream { byte *_end; AudioCache *_cache; int32 _hash; - byte _compressionType; uint16 _deafBlockRemain; + uint16 _frequency; + uint32 _size; + uint32 _sizeDecompressed; + byte _flags; + byte _compressionType; ADPCMWestwoodDecoder _decoder; @@ -53,9 +57,10 @@ public: int readBuffer(int16 *buffer, const int numSamples); bool isStereo() const { return false; } - int getRate() const { return READ_LE_UINT16(_data); }; + int getRate() const { return _frequency; }; bool endOfData() const { return _p == _end; } bool rewind(); + int getLength(); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_mixer.cpp b/engines/bladerunner/audio_mixer.cpp new file mode 100644 index 0000000000..0bd7b04b89 --- /dev/null +++ b/engines/bladerunner/audio_mixer.cpp @@ -0,0 +1,191 @@ +/* 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/audio_mixer.h" + +#include "bladerunner/bladerunner.h" + +#include "audio/audiostream.h" +#include "audio/mixer.h" + +#include "common/timer.h" + +namespace BladeRunner { +AudioMixer::AudioMixer(BladeRunnerEngine *vm): + _vm(vm) +{ + for (int i = 0; i < kAudioMixerChannels; i++) { + _channels[i].isPresent = false; + } + _vm->getTimerManager()->installTimerProc(timerCallback, 25 * 1000 , this, "BladeRunnerAudioMixerTimer"); +} + +AudioMixer::~AudioMixer() { + for (int i = 0; i < kAudioMixerChannels; i++) { + stop(i, 0); + } + _vm->getTimerManager()->removeTimerProc(timerCallback); +} + +int AudioMixer::playStream(Audio::Mixer::SoundType type, Audio::RewindableAudioStream *stream, int priority, bool loop, int volume, int pan, void (*endCallback)(int, void*), void *callbackData) { + Common::StackLock lock(_mutex); + + int channel = -1; + int lowestPriority = 1000000; + int lowestPriorityChannel = -1; + for (int i = 0; i < kAudioMixerChannels; i++) { + if (!_channels[i].isPresent) { + channel = i; + break; + } + if (_channels[i].priority < lowestPriority) { + lowestPriority = _channels[i].priority; + lowestPriorityChannel = i; + } + } + if (channel == -1) { + if (priority < lowestPriority) { + return -1; + } + stop(lowestPriorityChannel, 0); + channel = lowestPriorityChannel; + } + + _channels[channel].isPresent = true; + _channels[channel].stream = stream; + _channels[channel].priority = priority; + _channels[channel].loop = loop; + _channels[channel].volume = volume; + _channels[channel].volumeTarget = 0; + _channels[channel].volumeDelta = 0; + _channels[channel].pan = pan; + _channels[channel].panTarget = 0; + _channels[channel].panDelta = 0; + _channels[channel].endCallback = endCallback; + _channels[channel].callbackData = callbackData; + + + Audio::AudioStream* audioStream = stream; + + if (loop) { + audioStream = new Audio::LoopingAudioStream(stream, 0, DisposeAfterUse::YES); + } + + _vm->_mixer->playStream( + type, + &_channels[channel].handle, + audioStream, + -1, + volume * 255 / 100, + pan * 127 / 100); + + return channel; +} + +void AudioMixer::stop(int channel, int time) { + Common::StackLock lock(_mutex); + + if (_channels[channel].isPresent) { + if (time) { + adjustVolume(channel, 0, time); + } else { + _channels[channel].isPresent = false; + _vm->_mixer->stopHandle(_channels[channel].handle); + if (_channels[channel].endCallback != nullptr) { + _channels[channel].endCallback(channel, _channels[channel].callbackData); + } + } + } +} + +bool AudioMixer::isActive(int channel) { + Common::StackLock lock(_mutex); + + return _channels[channel].isPresent && _vm->_mixer->isSoundHandleActive(_channels[channel].handle); +} + +void AudioMixer::timerCallback(void *self) { + ((AudioMixer*)self)->tick(); +} + +void AudioMixer::adjustVolume(int channel, int newVolume, int time) +{ + Common::StackLock lock(_mutex); + + if (_channels[channel].isPresent) { + _channels[channel].volumeTarget = newVolume; + _channels[channel].volumeDelta = ((newVolume - _channels[channel].volume) / (time / 60.0f)) / 40.0f; + } +} + +void AudioMixer::adjustPan(int channel, int newPan, int time) +{ + Common::StackLock lock(_mutex); + + if (_channels[channel].isPresent) { + newPan = CLIP(newPan, -100, 100); + _channels[channel].panTarget = newPan; + _channels[channel].panDelta = ((newPan - _channels[channel].pan) / (time / 60.0f)) / 40.0f; + } +} + +void AudioMixer::tick() +{ + Common::StackLock lock(_mutex); + + for (int i = 0; i < kAudioMixerChannels; i++) { + Channel *channel = &_channels[i]; + if (!channel->isPresent) { + continue; + } + + if (channel->volumeDelta != 0.0f) { + channel->volume = CLIP(channel->volume + channel->volumeDelta, 0.0f, 100.0f); + + if ((channel->volumeDelta < 0 && channel->volume <= channel->volumeTarget) || (channel->volumeDelta > 0 && channel->volume >= channel->volumeTarget)) { + channel->volumeDelta = 0.0f; + } + + _vm->_mixer->setChannelVolume(channel->handle, channel->volume * 255 / 100); + + if (channel->volume <= 0.0f) { + stop(i, 0); + } + } + + if (channel->panDelta != 0.0) { + channel->pan = CLIP(channel->pan + channel->panDelta, -100.0f, 100.0f); + + if ((channel->panDelta < 0 && channel->pan <= channel->panTarget) || (channel->panDelta > 0 && channel->pan >= channel->panTarget)) { + channel->panDelta = 0.0f; + } + + _vm->_mixer->setChannelBalance(channel->handle, channel->pan * 127 / 100); + } + + if (!_vm->_mixer->isSoundHandleActive(channel->handle) || channel->stream->endOfStream()) { + stop(i, 0); + } + } +} + +} // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_mixer.h b/engines/bladerunner/audio_mixer.h new file mode 100644 index 0000000000..75a0f08201 --- /dev/null +++ b/engines/bladerunner/audio_mixer.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BLADERUNNER_AUDIO_MIXER_H +#define BLADERUNNER_AUDIO_MIXER_H + +#include "audio/audiostream.h" +#include "audio/mixer.h" + +#include "common/mutex.h" + +namespace BladeRunner { + +class BladeRunnerEngine; + +class AudioMixer { + static const int kAudioMixerChannels = 9; + + struct Channel { + bool isPresent; + int priority; + bool loop; + Audio::SoundHandle handle; + Audio::AudioStream *stream; + float volume; + float volumeDelta; + float volumeTarget; + float pan; + float panDelta; + float panTarget; + void (*endCallback)(int channel, void *data); + void *callbackData; + }; + + BladeRunnerEngine *_vm; + + Channel _channels[kAudioMixerChannels]; + Common::Mutex _mutex; + +public: + AudioMixer(BladeRunnerEngine *vm); + ~AudioMixer(); + + int playStream(Audio::Mixer::SoundType type, Audio::RewindableAudioStream *stream, int priority, bool loop, int volume, int pan, void(*onEndCallback)(int, void *), void *callbackData); + void stop(int channel, int delay); + + void adjustVolume(int channel, int newVolume, int time); + void adjustPan(int channel, int newPan, int time); + + void resume(int channel, int delay); + void pause(int channel, int delay); + +private: + bool isActive(int channel); + void tick(); + static void timerCallback(void *refCon); +}; + +} // End of namespace BladeRunner + +#endif diff --git a/engines/bladerunner/audio_player.cpp b/engines/bladerunner/audio_player.cpp index 0c2b747c32..f5ba83c4b3 100644 --- a/engines/bladerunner/audio_player.cpp +++ b/engines/bladerunner/audio_player.cpp @@ -20,15 +20,13 @@ * */ -#include "audio_player.h" - -#include "bladerunner/archive.h" -#include "bladerunner/aud_stream.h" +#include "bladerunner/audio_player.h" #include "bladerunner/bladerunner.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" +#include "bladerunner/archive.h" +#include "bladerunner/aud_stream.h" +#include "bladerunner/audio_mixer.h" #include "common/debug.h" #include "common/stream.h" @@ -79,7 +77,7 @@ byte *AudioCache::findByHash(int32 hash) { } } - return NULL; + return nullptr; } void AudioCache::storeByHash(int32 hash, Common::SeekableReadStream *stream) { @@ -110,7 +108,7 @@ void AudioCache::incRef(int32 hash) { return; } } - assert(0 && "AudioCache::incRef: hash not found"); + assert(false && "AudioCache::incRef: hash not found"); } void AudioCache::decRef(int32 hash) { @@ -123,7 +121,7 @@ void AudioCache::decRef(int32 hash) { return; } } - assert(0 && "AudioCache::decRef: hash not found"); + assert(false && "AudioCache::decRef: hash not found"); } AudioPlayer::AudioPlayer(BladeRunnerEngine *vm) : _vm(vm) { @@ -133,68 +131,111 @@ AudioPlayer::AudioPlayer(BladeRunnerEngine *vm) : _vm(vm) { _tracks[i].hash = 0; _tracks[i].priority = 0; } + + _sfxVolume = 65; } AudioPlayer::~AudioPlayer() { + stopAll(); delete _cache; } -bool AudioPlayer::isTrackActive(Track *track) { - if (!track->isMaybeActive) - return false; +void AudioPlayer::stopAll() { + for (int i = 0; i != kTracks; ++i) { + stop(i, false); + } + for (int i = 0; i != kTracks; ++i) { + while (isActive(i)) { + // wait for all tracks to finish + } + } +} + +void AudioPlayer::adjustVolume(int track, int volume, int delay, bool overrideVolume) { + if (track < 0 || track >= kTracks || !_tracks[track].isActive || _tracks[track].channel == -1) { + return; + } + + int actualVolume = volume; + if (!overrideVolume) { + actualVolume = actualVolume * _sfxVolume / 100; + } - return track->isMaybeActive = _vm->_mixer->isSoundHandleActive(track->soundHandle); + _tracks[track].volume = volume; + _vm->_audioMixer->adjustVolume(_tracks[track].channel, volume, 60 * delay); } -void AudioPlayer::stopAll() { - for (int i = 0; i != TRACKS; ++i) { - _vm->_mixer->stopHandle(_tracks[i].soundHandle); +void AudioPlayer::adjustPan(int track, int pan, int delay) { + if (track < 0 || track >= kTracks || !_tracks[track].isActive || _tracks[track].channel == -1) { + return; } + + _tracks[track].pan = pan; + _vm->_audioMixer->adjustPan(_tracks[track].channel, pan, 60 * delay); } -void AudioPlayer::fadeAndStopTrack(Track *track, int time) { - (void)time; +//void AudioPlayer::tick() { +// for (int i = 0; i != 6; ++i) { +// Track *ti = &_tracks[i]; +// } +//} - _vm->_mixer->stopHandle(track->soundHandle); +void AudioPlayer::remove(int channel) { + Common::StackLock lock(_mutex); + for (int i = 0; i != kTracks; ++i) { + if (_tracks[i].channel == channel) { + _tracks[i].isActive = false; + _tracks[i].priority = 0; + _tracks[i].channel = -1; + //_cache->decRef(_tracks[i].hash); + break; + } + } +} + +void AudioPlayer::mixerChannelEnded(int channel, void *data) { + AudioPlayer *audioPlayer = (AudioPlayer *)data; + audioPlayer->remove(channel); } int AudioPlayer::playAud(const Common::String &name, int volume, int panFrom, int panTo, int priority, byte flags) { /* Find first available track or, alternatively, the lowest priority playing track */ - Track *track = NULL; - int lowestPriority = 1000000; - Track *lowestPriorityTrack = NULL; + int track = -1; + int lowestPriority = 1000000; + int lowestPriorityTrack = -1; for (int i = 0; i != 6; ++i) { - Track *ti = &_tracks[i]; - if (!isTrackActive(ti)) { - track = ti; + if (!isActive(i)) { + track = i; break; } - if (lowestPriorityTrack == NULL || ti->priority < lowestPriority) { - lowestPriority = ti->priority; - lowestPriorityTrack = ti; + if (lowestPriorityTrack == -1 || _tracks[i].priority < lowestPriority) { + lowestPriority = _tracks[i].priority; + lowestPriorityTrack = i; } } /* If there's no available track, stop the lowest priority track if it's lower than * the new priority */ - if (track == NULL && lowestPriority < priority) { - fadeAndStopTrack(lowestPriorityTrack, 1); + if (track == -1 && lowestPriority < priority) { + stop(lowestPriorityTrack, true); track = lowestPriorityTrack; } /* If there's still no available track, give up */ - if (track == NULL) + if (track == -1) { return -1; + } /* Load audio resource and store in cache. Playback will happen directly from there. */ int32 hash = mix_id(name); if (!_cache->findByHash(hash)) { Common::SeekableReadStream *r = _vm->getResourceStream(name); - if (!r) + if (!r) { return -1; + } int32 size = r->size(); while (!_cache->canAllocate(size)) { @@ -207,34 +248,55 @@ int AudioPlayer::playAud(const Common::String &name, int volume, int panFrom, in delete r; } - AudStream *audStream = new AudStream(_cache, hash); + AudStream *audioStream = new AudStream(_cache, hash); - Audio::AudioStream *audioStream = audStream; - if (flags & LOOP) { - audioStream = new Audio::LoopingAudioStream(audStream, 0, DisposeAfterUse::YES); + int actualVolume = volume; + if (!(flags & OVERRIDE_VOLUME)) { + actualVolume = _sfxVolume * volume / 100; } - Audio::SoundHandle soundHandle; - // debug("PlayStream: %s", name.c_str()); - int balance = panFrom; - - _vm->_mixer->playStream( + int channel = _vm->_audioMixer->playStream( Audio::Mixer::kPlainSoundType, - &soundHandle, audioStream, - -1, - volume * 255 / 100, - balance); + priority, + flags & LOOP, + actualVolume, + panFrom, + mixerChannelEnded, + this); + + if (channel == -1) { + return -1; + } + + if (panFrom != panTo) { + _vm->_audioMixer->adjustPan(channel, panTo, (60 * audioStream->getLength()) / 1000); + } + + _tracks[track].isActive = true; + _tracks[track].channel = channel; + _tracks[track].priority = priority; + _tracks[track].hash = hash; + _tracks[track].volume = actualVolume; + + return track; +} + +bool AudioPlayer::isActive(int track) { + Common::StackLock lock(_mutex); + if (track < 0 || track >= kTracks) { + return false; + } - track->isMaybeActive = true; - track->soundHandle = soundHandle; - track->priority = priority; - track->hash = hash; - track->volume = volume; + return _tracks[track].isActive; +} - return track - &_tracks[0]; +void AudioPlayer::stop(int track, bool immediately) { + if (isActive(track)) { + _vm->_audioMixer->stop(_tracks[track].channel, immediately ? 0 : 60); + } } } // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_player.h b/engines/bladerunner/audio_player.h index 4260a7b32b..c573c64840 100644 --- a/engines/bladerunner/audio_player.h +++ b/engines/bladerunner/audio_player.h @@ -20,22 +20,18 @@ * */ -#ifndef BLADERUNNER_AUDIO_H -#define BLADERUNNER_AUDIO_H +#ifndef BLADERUNNER_AUDIO_PLAYER_H +#define BLADERUNNER_AUDIO_PLAYER_H -#include "audio/mixer.h" #include "common/array.h" #include "common/mutex.h" #include "common/str.h" -#include "common/types.h" namespace BladeRunner { class BladeRunnerEngine; class AudioCache; -#define TRACKS 6 - /* * This is a poor imitation of Bladerunner's resource cache */ @@ -54,6 +50,7 @@ class AudioCache { uint32 _totalSize; uint32 _maxSize; uint32 _accessCounter; + public: AudioCache() : _totalSize(0), @@ -72,23 +69,25 @@ public: }; class AudioPlayer { - BladeRunnerEngine *_vm; - AudioCache *_cache; + static const int kTracks = 6; struct Track { - bool isMaybeActive; - Audio::SoundHandle soundHandle; + bool isActive; + int channel; int priority; int32 hash; int volume; + int pan; - Track() : isMaybeActive(false) {} + Track() : isActive(false) {} }; - Track _tracks[TRACKS]; + BladeRunnerEngine *_vm; - bool isTrackActive(Track *track); - void fadeAndStopTrack(Track *track, int time); + Common::Mutex _mutex; + AudioCache *_cache; + Track _tracks[kTracks]; + int _sfxVolume; public: AudioPlayer(BladeRunnerEngine *vm); @@ -99,9 +98,16 @@ public: OVERRIDE_VOLUME = 2 }; - int playAud(const Common::String &name, int volume, int panFrom, int panTo, int priority, byte flags = 0); - + int playAud(const Common::String &name, int volume, int panStart, int panEnd, int priority, byte flags = 0); + bool isActive(int track); + void stop(int track, bool immediately); void stopAll(); + void adjustVolume(int track, int volume, int delay, bool overrideVolume); + void adjustPan(int track, int pan, int delay); + +private: + void remove(int channel); + static void mixerChannelEnded(int channel, void *data); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_speech.cpp b/engines/bladerunner/audio_speech.cpp index 2d06025892..7177cf0f9c 100644 --- a/engines/bladerunner/audio_speech.cpp +++ b/engines/bladerunner/audio_speech.cpp @@ -23,6 +23,7 @@ #include "bladerunner/audio_speech.h" #include "bladerunner/aud_stream.h" +#include "bladerunner/audio_mixer.h" #include "bladerunner/bladerunner.h" #include "common/debug.h" @@ -31,17 +32,29 @@ namespace BladeRunner { #define BUFFER_SIZE 200000 +void AudioSpeech::ended() { + //Common::StackLock lock(_mutex); + _isActive = false; + _channel = -1; +} + +void AudioSpeech::mixerChannelEnded(int channel, void *data) { + AudioSpeech *audioSpeech = (AudioSpeech*)data; + audioSpeech->ended(); +} + AudioSpeech::AudioSpeech(BladeRunnerEngine *vm) : _vm(vm) { _volume = 50; - _isMaybeActive = false; + _isActive = false; _data = new byte[BUFFER_SIZE]; + _channel = -1; } AudioSpeech::~AudioSpeech() { delete[] _data; } -bool AudioSpeech::playSpeech(const char *name, int balance) { +bool AudioSpeech::playSpeech(const char *name, int pan) { // debug("AudioSpeech::playSpeech(\"%s\")", name); Common::ScopedPtr<Common::SeekableReadStream> r(_vm->getResourceStream(name)); @@ -67,28 +80,35 @@ bool AudioSpeech::playSpeech(const char *name, int balance) { AudStream *audioStream = new AudStream(_data); - _vm->_mixer->playStream( - Audio::Mixer::kPlainSoundType, - &_soundHandle, + // TODO: shorty mode - set rate of sound to 33khz + + _channel = _vm->_audioMixer->playStream( + Audio::Mixer::kSpeechSoundType, audioStream, - -1, - _volume * 255 / 100, - balance); + 100, + false, + _volume, + pan, + mixerChannelEnded, + this); - _isMaybeActive = true; + _isActive = true; return true; } void AudioSpeech::stopSpeech() { - _vm->_mixer->stopHandle(_soundHandle); + //Common::StackLock lock(_mutex); + if (_channel != -1) { + _vm->_audioMixer->stop(_channel, 0); + } } bool AudioSpeech::isPlaying() { - if (!_isMaybeActive) - return false; - - return _isMaybeActive = _vm->_mixer->isSoundHandleActive(_soundHandle); + if (_channel == -1) { + return false; + } + return _isActive; } } // End of namespace BladeRunner diff --git a/engines/bladerunner/audio_speech.h b/engines/bladerunner/audio_speech.h index ae7065865b..f3e4395d5d 100644 --- a/engines/bladerunner/audio_speech.h +++ b/engines/bladerunner/audio_speech.h @@ -23,18 +23,18 @@ #ifndef BLADERUNNER_AUDIO_SPEECH_H #define BLADERUNNER_AUDIO_SPEECH_H -#include "audio/mixer.h" +#include "common/types.h" namespace BladeRunner { class BladeRunnerEngine; class AudioSpeech { -private: BladeRunnerEngine *_vm; + int _volume; - bool _isMaybeActive; - Audio::SoundHandle _soundHandle; + bool _isActive; + int _channel; byte *_data; public: @@ -45,6 +45,10 @@ public: void stopSpeech(); bool isPlaying(); void setVolume(int volume) { _volume = volume; } + +private: + void ended(); + static void mixerChannelEnded(int channel, void *data); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index a3ac82df0e..7fe3f9ed2e 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -25,6 +25,7 @@ #include "bladerunner/actor.h" #include "bladerunner/adq.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" @@ -42,6 +43,7 @@ #include "bladerunner/mouse.h" #include "bladerunner/outtake.h" #include "bladerunner/obstacles.h" +#include "bladerunner/regions.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/script/init.h" @@ -52,6 +54,7 @@ #include "bladerunner/slice_animations.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/spinner.h" +#include "bladerunner/suspects_database.h" #include "bladerunner/text_resource.h" #include "bladerunner/vqa_decoder.h" #include "bladerunner/waypoints.h" @@ -65,7 +68,7 @@ #include "engines/util.h" #include "graphics/pixelformat.h" -#include "suspects_database.h" + namespace BladeRunner { @@ -207,7 +210,7 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { _items = new Items(this); - // Setup sound output + _audioMixer = new AudioMixer(this); _audioPlayer = new AudioPlayer(this); @@ -290,7 +293,6 @@ bool BladeRunnerEngine::startup(bool hasSavegames) { // TODO: KIA - // TODO: Spinner Interface _spinner = new Spinner(this); _elevator = new Elevator(this); @@ -436,7 +438,7 @@ void BladeRunnerEngine::shutdown() { delete _audioPlayer; - // Shutdown sound output + delete _audioMixer; if (isArchiveOpen("MUSIC.MIX")) closeArchive("MUSIC.MIX"); @@ -485,7 +487,8 @@ void BladeRunnerEngine::shutdown() { // TODO: Delete Elevators - // TODO: Delete Spinner Interface + delete _spinner; + _spinner = nullptr; // TODO: Delete KIA @@ -586,9 +589,15 @@ void BladeRunnerEngine::gameTick() { if (_gameIsRunning && _windowIsActive) { // TODO: Only run if not in Kia, script, nor AI - _settings->openNewScene(); + if (!_sceneScript->IsInsideScript() && !_aiScripts->IsInsideScript()) { + _settings->openNewScene(); + } // TODO: Autosave + + //probably not needed, this version of tick is just loading data from buffer + //_audioMixer->tick(); + // TODO: Kia if (_spinner->isOpen()) { @@ -664,14 +673,13 @@ void BladeRunnerEngine::gameTick() { if (_dialogueMenu->isVisible()) { _dialogueMenu->tick(p.x, p.y); - _dialogueMenu->draw(); + _dialogueMenu->draw(_surfaceGame); } _mouse->tick(p.x, p.y); _mouse->draw(_surfaceGame, p.x, p.y); // TODO: Process AUD - // TODO: Footstep sound if (_walkSoundId >= 0) { const char *name = _gameInfo->getSfxTrack(_walkSoundId); @@ -824,21 +832,41 @@ void BladeRunnerEngine::handleEvents() { 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; case Common::EVENT_LBUTTONDOWN: + handleMouseAction(event.mouse.x, event.mouse.y, true, true); + break; case Common::EVENT_RBUTTONDOWN: - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONUP: { - bool buttonLeft = event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP; - bool buttonDown = event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN; - - handleMouseAction(event.mouse.x, event.mouse.y, buttonLeft, buttonDown); - } + case Common::EVENT_MBUTTONDOWN: + handleMouseAction(event.mouse.x, event.mouse.y, false, true); + break; default: - ; + ; // nothing to do } } } +void BladeRunnerEngine::handleKeyUp(Common::Event &event) { + if (event.kbd.keycode == Common::KEYCODE_RETURN) { + _speechSkipped = true; + } +} + +void BladeRunnerEngine::handleKeyDown(Common::Event &event) { +} + void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool buttonDown) { if (!playerHasControl() || _mouse->isDisabled()) return; @@ -868,40 +896,42 @@ void BladeRunnerEngine::handleMouseAction(int x, int y, bool buttonLeft, bool bu return; } - Vector3 mousePosition = _mouse->getXYZ(x, y); + if (buttonLeft && !buttonDown) { + Vector3 mousePosition = _mouse->getXYZ(x, y); - int isClickable; - int isObstacle; - int isTarget; + int isClickable; + int isObstacle; + int isTarget; - int sceneObjectId = _sceneObjects->findByXYZ(&isClickable, &isObstacle, &isTarget, mousePosition.x, mousePosition.y, mousePosition.z, 1, 0, 1); - int exitIndex = _scene->_exits->getRegionAtXY(x, y); + int sceneObjectId = _sceneObjects->findByXYZ(&isClickable, &isObstacle, &isTarget, mousePosition.x, mousePosition.y, mousePosition.z, true, false, true); + int exitIndex = _scene->_exits->getRegionAtXY(x, y); - if ((sceneObjectId < 0 || sceneObjectId > 73) && exitIndex >= 0) { - handleMouseClickExit(x, y, exitIndex); - return; - } + if ((sceneObjectId < 0 || sceneObjectId > 73) && exitIndex >= 0) { + handleMouseClickExit(x, y, exitIndex); + return; + } - int regionIndex = _scene->_regions->getRegionAtXY(x, y); - if (regionIndex >= 0) { - handleMouseClickRegion(x, y, regionIndex); - return; - } + int regionIndex = _scene->_regions->getRegionAtXY(x, y); + if (regionIndex >= 0) { + handleMouseClickRegion(x, y, regionIndex); + return; + } - if (sceneObjectId == -1) { - bool isRunning; - _playerActor->loopWalkToXYZ(mousePosition, 0, false, false, false, &isRunning); - debug("Clicked on nothing %f, %f, %f", mousePosition.x, mousePosition.y, mousePosition.z); - return; - } else if (sceneObjectId >= 0 && sceneObjectId <= 73) { - handleMouseClickActor(x, y, sceneObjectId); - return; - } else if (sceneObjectId >= 74 && sceneObjectId <= 197) { - handleMouseClickItem(x, y, sceneObjectId - 74); - return; - } else if (sceneObjectId >= 198 && sceneObjectId <= 293) { - handleMouseClick3DObject(x, y, sceneObjectId - 198, isClickable, isTarget); - return; + if (sceneObjectId == -1) { + bool isRunning; + _playerActor->loopWalkToXYZ(mousePosition, 0, false, false, false, &isRunning); + debug("Clicked on nothing %f, %f, %f", mousePosition.x, mousePosition.y, mousePosition.z); + return; + } else if (sceneObjectId >= 0 && sceneObjectId <= 73) { + handleMouseClickActor(x, y, sceneObjectId); + return; + } else if (sceneObjectId >= 74 && sceneObjectId <= 197) { + handleMouseClickItem(x, y, sceneObjectId - 74); + return; + } else if (sceneObjectId >= 198 && sceneObjectId <= 293) { + handleMouseClick3DObject(x, y, sceneObjectId - 198, isClickable, isTarget); + return; + } } } @@ -939,8 +969,9 @@ void BladeRunnerEngine::gameWaitForActive() { } void BladeRunnerEngine::loopActorSpeaking() { - if (!_audioSpeech->isPlaying()) + if (!_audioSpeech->isPlaying()) { return; + } playerLosesControl(); @@ -1019,7 +1050,7 @@ Common::SeekableReadStream *BladeRunnerEngine::getResourceStream(const Common::S } debug("getResource: Resource %s not found.", name.c_str()); - return 0; + return nullptr; } bool BladeRunnerEngine::playerHasControl() { diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h index d58f9a5601..5164f353dd 100644 --- a/engines/bladerunner/bladerunner.h +++ b/engines/bladerunner/bladerunner.h @@ -32,7 +32,10 @@ #include "engines/engine.h" #include "graphics/surface.h" -#include "suspects_database.h" + +namespace Common { +struct Event; +} namespace BladeRunner { @@ -49,6 +52,7 @@ class Actor; class ADQ; class AIScripts; class AmbientSounds; +class AudioMixer; class AudioPlayer; class AudioSpeech; class Chapters; @@ -72,6 +76,7 @@ class Shape; class SliceAnimations; class SliceRenderer; class Spinner; +class SuspectsDatabase; class TextResource; class View; class Waypoints; @@ -89,6 +94,7 @@ public: ADQ *_adq; AIScripts *_aiScripts; AmbientSounds *_ambientSounds; + AudioMixer *_audioMixer; AudioPlayer *_audioPlayer; AudioSpeech *_audioSpeech; Chapters *_chapters; @@ -176,6 +182,8 @@ public: void gameTick(); void actorsUpdate(); void handleEvents(); + void handleKeyUp(Common::Event &event); + void handleKeyDown(Common::Event &event); void handleMouseAction(int x, int y, bool buttonLeft, bool buttonDown); void handleMouseClickExit(int x, int y, int exitIndex); void handleMouseClickRegion(int x, int y, int regionIndex); diff --git a/engines/bladerunner/color.h b/engines/bladerunner/color.h index a93d79b34a..f9796526cb 100644 --- a/engines/bladerunner/color.h +++ b/engines/bladerunner/color.h @@ -32,7 +32,6 @@ struct Color { float g; float b; - Color() { } diff --git a/engines/bladerunner/dialogue_menu.cpp b/engines/bladerunner/dialogue_menu.cpp index 2ef137f9d4..40ec3aee00 100644 --- a/engines/bladerunner/dialogue_menu.cpp +++ b/engines/bladerunner/dialogue_menu.cpp @@ -23,13 +23,14 @@ #include "bladerunner/dialogue_menu.h" #include "bladerunner/bladerunner.h" - #include "bladerunner/font.h" #include "bladerunner/mouse.h" +#include "bladerunner/settings.h" #include "bladerunner/shape.h" #include "bladerunner/text_resource.h" #include "common/debug.h" +#include "common/rect.h" #include "common/util.h" #define LINE_HEIGHT 9 @@ -87,7 +88,7 @@ bool DialogueMenu::showAt(int x, int y) { } bool DialogueMenu::hide() { - _waitingForInput = 0; + _waitingForInput = false; if (!_isVisible) { return false; } @@ -102,24 +103,27 @@ bool DialogueMenu::clearList() { return true; } -bool DialogueMenu::addToList(int answer, int a3, int a4, int a5, int a6) { - if (_listSize >= 10) +bool DialogueMenu::addToList(int answer, bool done, int priorityPolite, int priorityNormal, int prioritySurly) { + if (_listSize >= 10) { return false; - if (getAnswerIndex(answer) != -1) + } + if (getAnswerIndex(answer) != -1) { return false; + } const char *text = _textResource->getText(answer); - if (!text || strlen(text) >= 50) + if (!text || strlen(text) >= 50) { return false; + } int index = _listSize++; - strcpy(_items[index].text, text); + _items[index].text = text; _items[index].answerValue = answer; - _items[index].field_36 = 0; - _items[index].field_46 = a3; - _items[index].field_3A = a4; - _items[index].field_3E = a5; - _items[index].field_42 = a6; + _items[index].colorIntensity = 0; + _items[index].isDone = done; + _items[index].priorityPolite = priorityPolite; + _items[index].priorityNormal = priorityNormal; + _items[index].prioritySurly = prioritySurly; // CHECK(madmoose): BLADE.EXE calls this needlessly // calculatePosition(); @@ -127,7 +131,7 @@ bool DialogueMenu::addToList(int answer, int a3, int a4, int a5, int a6) { return true; } -bool DialogueMenu::addToListNeverRepeatOnceSelected(int answer, int a3, int a4, int a5) { +bool DialogueMenu::addToListNeverRepeatOnceSelected(int answer, int priorityPolite, int priorityNormal, int prioritySurly) { for (int i = 0; i != _neverRepeatListSize; ++i) { if (answer == _neverRepeatValues[i] && _neverRepeatWasSelected[i]) { return true; @@ -137,7 +141,7 @@ bool DialogueMenu::addToListNeverRepeatOnceSelected(int answer, int a3, int a4, _neverRepeatValues[_neverRepeatListSize] = answer; _neverRepeatWasSelected[_neverRepeatListSize] = false; ++_neverRepeatListSize; - return addToList(answer, 0, a3, a4, a5); + return addToList(answer, false, priorityPolite, priorityNormal, prioritySurly); } int DialogueMenu::queryInput() { @@ -149,20 +153,22 @@ int DialogueMenu::queryInput() { _selectedItemIndex = 0; answer = _items[0].answerValue; } else if (_listSize == 2) { - if (_items[0].field_46) { + if (_items[0].isDone) { _selectedItemIndex = 1; answer = _items[0].answerValue; - } else if (_items[0].field_46) { + } else if (_items[0].isDone) { _selectedItemIndex = 0; answer = _items[1].answerValue; } } if (answer == -1) { - int agenda = 4; //_vm->_settings.getPlayerAgenda(); - if (agenda == 4) { + int agenda = _vm->_settings->getPlayerAgenda(); + agenda = kPlayerAgendaUserChoice; + if (agenda == kPlayerAgendaUserChoice) { _waitingForInput = true; do { + // TODO: game resuming // if (!_vm->_gameRunning) // break; @@ -176,14 +182,13 @@ int DialogueMenu::queryInput() { _vm->gameTick(); } while (_waitingForInput); - - } else if (agenda == 3) { + } else if (agenda == kPlayerAgendaErratic) { int tries = 0; bool searching = true; int i; do { i = _vm->_rnd.getRandomNumber(_listSize - 1); - if (!_items[i].field_46) { + if (!_items[i].isDone) { searching = false; } else if (++tries > 1000) { searching = false; @@ -192,7 +197,21 @@ int DialogueMenu::queryInput() { } while (searching); _selectedItemIndex = i; } else { - error("unimplemented..."); + int priority = -1; + for (int i = 0; i < _listSize; i++) { + int priorityCompare = -1; + if (agenda == kPlayerAgendaPolite) { + priorityCompare = _items[i].priorityPolite; + } else if (agenda == kPlayerAgendaNormal) { + priorityCompare = _items[i].priorityNormal; + } else if (agenda == kPlayerAgendaSurly) { + priorityCompare = _items[i].prioritySurly; + } + if (priority < priorityCompare) { + priority = priorityCompare; + _selectedItemIndex = i; + } + } } } @@ -205,7 +224,7 @@ int DialogueMenu::queryInput() { } if (_selectedItemIndex >= 0) { - debug("DM Query Input: %d %s", answer, _items[_selectedItemIndex].text); + debug("DM Query Input: %d %s", answer, _items[_selectedItemIndex].text.c_str()); } return answer; @@ -234,18 +253,38 @@ void DialogueMenu::tick(int x, int y) { _selectedItemIndex = line; } -void DialogueMenu::draw() { - if (!_isVisible || _listSize == 0) +void DialogueMenu::draw(Graphics::Surface &s) { + if (!_isVisible || _listSize == 0) { return; + } + + int fadeInItemIndex = _fadeInItemIndex; + if (fadeInItemIndex < listSize()) { + ++_fadeInItemIndex; + } for (int i = 0; i != _listSize; ++i) { + int targetColorIntensity = 0; if (i == _selectedItemIndex) { - _items[i].field_36 = 31; + targetColorIntensity = 31; } else { - _items[i].field_36 = 16; + targetColorIntensity = 16; + } + if (i > fadeInItemIndex) { + targetColorIntensity = 0; } - // TODO(madmoose): There's more logic here + if (_items[i].colorIntensity < targetColorIntensity) { + _items[i].colorIntensity += 4; + if(_items[i].colorIntensity > targetColorIntensity) { + _items[i].colorIntensity = targetColorIntensity; + } + } else if (_items[i].colorIntensity > targetColorIntensity) { + _items[i].colorIntensity -= 2; + if (_items[i].colorIntensity < targetColorIntensity) { + _items[i].colorIntensity = targetColorIntensity; + } + } } const int x1 = _screenX; @@ -253,21 +292,28 @@ void DialogueMenu::draw() { const int x2 = _screenX + BORDER_SIZE + _maxItemWidth; const int y2 = _screenY + BORDER_SIZE + _listSize * LINE_HEIGHT; - Graphics::Surface &s = _vm->_surfaceGame; - darkenRect(s, x1 + 8, y1 + 8, x2 + 2, y2 + 2); + int x = x1 + BORDER_SIZE; + int y = y1 + BORDER_SIZE; + + Common::Point mouse = _vm->getMousePos(); + if (mouse.x >= x && mouse.x < x2) { + s.vLine(mouse.x, y1 + 8, y2 + 2, 0x2108); + } + if (mouse.y >= y && mouse.y < y2) { + s.hLine(x1 + 8, mouse.y, x2 + 2, 0x2108); + } + _shapes[0].draw(s, x1, y1); _shapes[3].draw(s, x2, y1); _shapes[2].draw(s, x1, y2); _shapes[5].draw(s, x2, y2); - int x = x1 + BORDER_SIZE; - int y = y1 + BORDER_SIZE; for (int i = 0; i != _listSize; ++i) { _shapes[1].draw(s, x1, y); _shapes[4].draw(s, x2, y); - uint16 color = ((_items[i].field_36 >> 1) << 10) | ((_items[i].field_36 >> 1) << 6) | _items[i].field_36; + uint16 color = ((_items[i].colorIntensity >> 1) << 10) | ((_items[i].colorIntensity >> 1) << 6) | _items[i].colorIntensity; _vm->_mainFont->drawColor(_items[i].text, s, x, y, color); y += LINE_HEIGHT; } @@ -307,6 +353,7 @@ void DialogueMenu::calculatePosition(int unusedX, int unusedY) { _screenX = CLIP(_screenX, 0, 640 - w); _screenY = CLIP(_screenY, 0, 480 - h); + _fadeInItemIndex = 0; debug("calculatePosition: %d %d %d %d %d", _screenX, _screenY, _centerX, _centerY, _maxItemWidth); } @@ -324,13 +371,13 @@ void DialogueMenu::clear() { _selectedItemIndex = 0; _listSize = 0; for (int i = 0; i != 10; ++i) { - _items[0].text[0] = '\0'; - _items[0].answerValue = -1; - _items[0].field_36 = 0; - _items[0].field_42 = -1; - _items[0].field_3A = -1; - _items[0].field_3E = -1; - _items[0].field_46 = 0; + _items[i].text.clear(); + _items[i].answerValue = -1; + _items[i].isDone = 0; + _items[i].priorityPolite = -1; + _items[i].priorityNormal = -1; + _items[i].prioritySurly = -1; + _items[i].colorIntensity = 0; } _neverRepeatListSize = 0; for (int i = 0; i != 100; ++i) { diff --git a/engines/bladerunner/dialogue_menu.h b/engines/bladerunner/dialogue_menu.h index 7a6b99e967..1ab90fdac0 100644 --- a/engines/bladerunner/dialogue_menu.h +++ b/engines/bladerunner/dialogue_menu.h @@ -26,7 +26,7 @@ #include "bladerunner/shape.h" #include "common/array.h" - +#include "common/str.h" #include "graphics/surface.h" namespace BladeRunner { @@ -35,13 +35,13 @@ class BladeRunnerEngine; class TextResource; struct DialogueItem { - char text[50]; + Common::String text; int answerValue; - int field_36; - int field_3A; - int field_3E; - int field_42; - int field_46; + int colorIntensity; + int priorityPolite; + int priorityNormal; + int prioritySurly; + int isDone; }; class DialogueMenu { @@ -67,6 +67,8 @@ class DialogueMenu { int _maxItemWidth; DialogueItem _items[10]; + int _fadeInItemIndex; + public: DialogueMenu(BladeRunnerEngine *vm); ~DialogueMenu(); @@ -74,23 +76,26 @@ public: bool loadText(const char *name); bool show(); - bool showAt(int x, int y); bool hide(); + bool addToList(int answer, bool done, int priorityPolite, int priorityNormal, int prioritySurly); + bool addToListNeverRepeatOnceSelected(int answer, int priorityPolite, int priorityNormal, int prioritySurly); bool clearList(); - bool addToList(int answer, int a3, int a4, int a5, int a6); - bool addToListNeverRepeatOnceSelected(int answer, int a3, int a4, int a5); int queryInput(); int listSize(); bool isVisible(); bool isOpen(); void tick(int x, int y); - void draw(); + void draw(Graphics::Surface &s); + + void mouseUp(); + bool waitingForInput(); + +private: + bool showAt(int x, int y); int getAnswerIndex(int answer); const char *getText(int id); void calculatePosition(int unusedX = 0, int unusedY = 0); - void mouseUp(); - bool waitingForInput(); void clear(); void reset(); diff --git a/engines/bladerunner/elevator.cpp b/engines/bladerunner/elevator.cpp index e9b731e17b..99f3160141 100644 --- a/engines/bladerunner/elevator.cpp +++ b/engines/bladerunner/elevator.cpp @@ -29,10 +29,12 @@ #include "bladerunner/gameinfo.h" #include "bladerunner/mouse.h" #include "bladerunner/shape.h" +#include "bladerunner/script/script.h" #include "bladerunner/ui_image_picker.h" #include "bladerunner/vqa_player.h" #include "common/rect.h" +#include "common/str.h" #include "common/system.h" namespace BladeRunner { @@ -73,7 +75,7 @@ int Elevator::activate(int elevatorId) { return 0; } - _vqaPlayer->setLoop(1, -1, 0, nullptr, nullptr); + _vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr); _vm->_mouse->setCursor(0); for (int i = 0; i != 16; ++i) { @@ -86,21 +88,21 @@ int Elevator::activate(int elevatorId) { if (elevatorId == 1) { _imagePicker->defineImage( 0, - 220, 298, 308, 392, + Common::Rect(220, 298, 308, 392), nullptr, _shapes[11], _shapes[14], nullptr); _imagePicker->defineImage( 1, - 259, 259, 302, 292, + Common::Rect(259, 259, 302, 292), nullptr, _shapes[10], _shapes[13], nullptr); _imagePicker->defineImage( 2, - 227, 398, 301, 434, + Common::Rect(227, 398, 301, 434), nullptr, _shapes[12], _shapes[15], @@ -108,7 +110,7 @@ int Elevator::activate(int elevatorId) { } else { _imagePicker->defineImage( 4, - 395, 131, 448, 164, + Common::Rect(395, 131, 448, 164), nullptr, _shapes[0], _shapes[5], @@ -116,7 +118,7 @@ int Elevator::activate(int elevatorId) { ); _imagePicker->defineImage( 3, - 395, 165, 448, 198, + Common::Rect(395, 165, 448, 198), nullptr, _shapes[1], _shapes[6], @@ -124,7 +126,7 @@ int Elevator::activate(int elevatorId) { ); _imagePicker->defineImage( 5, - 395, 199, 448, 232, + Common::Rect(395, 199, 448, 232), nullptr, _shapes[2], _shapes[7], @@ -132,7 +134,7 @@ int Elevator::activate(int elevatorId) { ); _imagePicker->defineImage( 6, - 395, 233, 448, 264, + Common::Rect(395, 233, 448, 264), nullptr, _shapes[3], _shapes[8], @@ -140,7 +142,7 @@ int Elevator::activate(int elevatorId) { ); _imagePicker->defineImage( 7, - 395, 265, 448, 295, + Common::Rect(395, 265, 448, 295), nullptr, _shapes[4], _shapes[9], @@ -148,7 +150,7 @@ int Elevator::activate(int elevatorId) { ); } - _imagePicker->setCallbacks( + _imagePicker->activate( elevator_mouseInCallback, elevator_mouseOutCallback, elevator_mouseDownCallback, @@ -165,11 +167,14 @@ int Elevator::activate(int elevatorId) { _vm->gameTick(); } while (_buttonClicked == -1); + _imagePicker->deactivate(); + _vqaPlayer->close(); delete _vqaPlayer; - for (int i = 0; i != (int)_shapes.size(); ++i) + for (int i = 0; i != (int)_shapes.size(); ++i) { delete _shapes[i]; + } _shapes.clear(); _vm->closeArchive("MODE.MIX"); @@ -191,18 +196,19 @@ bool Elevator::isOpen() const { } int Elevator::handleMouseUp(int x, int y) { - _imagePicker->handleMouseAction(x, y, false, true, 0); + _imagePicker->handleMouseAction(x, y, false, true, false); return false; } int Elevator::handleMouseDown(int x, int y) { - _imagePicker->handleMouseAction(x, y, true, false, 0); + _imagePicker->handleMouseAction(x, y, true, false, false); return false; } void Elevator::tick() { - if (!_vm->_gameIsRunning) + if (!_vm->_gameIsRunning) { return; + } int frame = _vqaPlayer->update(); assert(frame >= -1); @@ -234,8 +240,8 @@ void Elevator::buttonClick(int buttonId) { void Elevator::reset() { _isOpen = false; - _vqaPlayer = 0; - _imagePicker = 0; + _vqaPlayer = nullptr; + _imagePicker = nullptr; _actorId = -1; _sentenceId = -1; _timeSpeakDescription = 0; @@ -243,33 +249,33 @@ void Elevator::reset() { void Elevator::buttonFocus(int buttonId) { switch (buttonId) { - case 7: - setupDescription(39, 140); - break; - case 6: - setupDescription(39, 130); - break; - case 5: - setupDescription(39, 120); - break; - case 4: - setupDescription(39, 100); - break; - case 3: - setupDescription(39, 110); - break; - case 2: - setupDescription(39, 130); - break; - case 1: - setupDescription(39, 100); - break; - case 0: - setupDescription(39, 150); - break; - default: - resetDescription(); - break; + 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; } } @@ -289,8 +295,9 @@ void Elevator::resetDescription() { void Elevator::tickDescription() { int now = _vm->getTotalPlayTime(); - if (_actorId <= 0 || now < _timeSpeakDescription) + if (_actorId <= 0 || now < _timeSpeakDescription) { return; + } _vm->_actors[_actorId]->speechPlay(_sentenceId, false); _actorId = -1; diff --git a/engines/bladerunner/font.cpp b/engines/bladerunner/font.cpp index 8ab5cfab13..2f56b3f12d 100644 --- a/engines/bladerunner/font.cpp +++ b/engines/bladerunner/font.cpp @@ -136,6 +136,10 @@ int Font::getTextWidth(const Common::String &text) { return totalWidth - _spacing1; } +int Font::getTextHeight(const Common::String &text) { + return _maxHeight; +} + void Font::reset() { _maxWidth = 0; _maxHeight = 0; diff --git a/engines/bladerunner/font.h b/engines/bladerunner/font.h index de790b0244..91a8184e4b 100644 --- a/engines/bladerunner/font.h +++ b/engines/bladerunner/font.h @@ -71,6 +71,7 @@ public: void drawColor(const Common::String &text, Graphics::Surface &surface, int x, int y, uint16 color); int getTextWidth(const Common::String &text); + int getTextHeight(const Common::String &text); private: void reset(); diff --git a/engines/bladerunner/gameflags.cpp b/engines/bladerunner/gameflags.cpp index 0e04a1c49d..261eff9547 100644 --- a/engines/bladerunner/gameflags.cpp +++ b/engines/bladerunner/gameflags.cpp @@ -27,7 +27,7 @@ namespace BladeRunner { GameFlags::GameFlags() - : flags(NULL), flagCount(0) { + : flags(nullptr), flagCount(0) { } GameFlags::~GameFlags() { diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk index 5c995984de..61a95352bf 100644 --- a/engines/bladerunner/module.mk +++ b/engines/bladerunner/module.mk @@ -10,6 +10,7 @@ MODULE_OBJS = \ ambient_sounds.o \ archive.o \ aud_stream.o \ + audio_mixer.o \ audio_player.o \ audio_speech.o \ bladerunner.o \ diff --git a/engines/bladerunner/mouse.cpp b/engines/bladerunner/mouse.cpp index 4c5a9b91a0..42d6ffef09 100644 --- a/engines/bladerunner/mouse.cpp +++ b/engines/bladerunner/mouse.cpp @@ -23,9 +23,11 @@ #include "bladerunner/mouse.h" #include "bladerunner/bladerunner.h" +#include "bladerunner/regions.h" #include "bladerunner/scene.h" #include "bladerunner/scene_objects.h" #include "bladerunner/shape.h" +#include "bladerunner/view.h" #include "bladerunner/zbuffer.h" #include "graphics/surface.h" diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp index f73d0d9882..54bc97abe4 100644 --- a/engines/bladerunner/scene.cpp +++ b/engines/bladerunner/scene.cpp @@ -22,23 +22,53 @@ #include "bladerunner/scene.h" -#include "bladerunner/bladerunner.h" - #include "bladerunner/actor.h" #include "bladerunner/adq.h" +#include "bladerunner/bladerunner.h" #include "bladerunner/chapters.h" #include "bladerunner/gameinfo.h" #include "bladerunner/items.h" -#include "bladerunner/settings.h" #include "bladerunner/scene_objects.h" -#include "bladerunner/script/scene.h" +#include "bladerunner/set.h" +#include "bladerunner/settings.h" #include "bladerunner/slice_renderer.h" +#include "bladerunner/regions.h" +#include "bladerunner/vqa_player.h" +#include "bladerunner/script/scene.h" #include "bladerunner/spinner.h" #include "common/str.h" namespace BladeRunner { +Scene::Scene(BladeRunnerEngine *vm) + : _vm(vm), + _setId(-1), + _sceneId(-1), + _vqaPlayer(nullptr), + _defaultLoop(0), + _defaultLoopSet(false), + _specialLoopMode(0), + _specialLoop(0), + _specialLoopAtEnd(false), + _introFinished(false), + _nextSetId(-1), + _nextSceneId(-1), + _frame(0), + _actorStartFacing(0), + _playerWalkedIn(false), + _set(new Set(vm)), + _regions(new Regions()), + _exits(new Regions()) { +} + +Scene::~Scene() { + delete _set; + delete _regions; + delete _exits; + delete _vqaPlayer; +} + bool Scene::open(int setId, int sceneId, bool isLoadingGame) { if (!isLoadingGame) { _vm->_adq->flush(1, false); @@ -73,21 +103,25 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { vqaName = Common::String::format("%s_%d.VQA", setName.c_str(), MIN(currentResourceId, 3)); } - if (_vqaPlayer != nullptr) + if (_vqaPlayer != nullptr) { delete _vqaPlayer; + } _vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceInterface); Common::String sceneName = _vm->_gameInfo->getSceneName(sceneId); - if (!_vm->_sceneScript->Open(sceneName)) + if (!_vm->_sceneScript->Open(sceneName)) { return false; + } - if (!isLoadingGame) + if (!isLoadingGame) { _vm->_sceneScript->InitializeScene(); + } Common::String setResourceName = Common::String::format("%s-MIN.SET", sceneName.c_str()); - if (!_set->open(setResourceName)) + if (!_set->open(setResourceName)) { return false; + } _vm->_sliceRenderer->setView(*_vm->_view); @@ -98,11 +132,12 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { return true; } - if (!_vqaPlayer->open(vqaName)) + if (!_vqaPlayer->open(vqaName)) { return false; + } if (_specialLoop == -1) { - _vqaPlayer->setLoop(_defaultLoop, -1, 2, nullptr, nullptr); + _vqaPlayer->setLoop(_defaultLoop, -1, kLoopSetModeImmediate, nullptr, nullptr); _defaultLoopSet = true; _specialLoopAtEnd = false; } @@ -177,21 +212,20 @@ int Scene::advanceFrame() { _vqaPlayer->updateView(_vm->_view); _vqaPlayer->updateLights(_vm->_lights); } - - if (_specialLoopMode && _specialLoopMode != 2 && _specialLoopMode != 3) { - if (_specialLoopMode == 1) { + if (_specialLoopMode && _specialLoopMode != kSceneLoopMode2 && _specialLoopMode != kSceneLoopModeSpinner) { + if (_specialLoopMode == kSceneLoopModeChangeSet) { if (frame == -3) { // TODO: when will this happen? bad data in/eof of vqa _vm->_settings->setNewSetAndScene(_nextSetId, _nextSceneId); _vm->playerGainsControl(); } } else if (!_specialLoopAtEnd) { - _vqaPlayer->setLoop(_defaultLoop + 1, -1, 0, &Scene::loopEndedStatic, this); + _vqaPlayer->setLoop(_defaultLoop + 1, -1, kLoopSetModeJustStart, &Scene::loopEndedStatic, this); _specialLoopAtEnd = true; } - } else if (!this->_defaultLoopSet) { - _vqaPlayer->setLoop(_defaultLoop, -1, 1, &Scene::loopEndedStatic, this); + } else if (!_defaultLoopSet) { + _vqaPlayer->setLoop(_defaultLoop, -1, kLoopSetModeEnqueue, &Scene::loopEndedStatic, this); _defaultLoopSet = true; - if (_specialLoopMode == 0) { + if (_specialLoopMode == kSceneLoopModeLoseControl) { _vm->playerLosesControl(); } } @@ -212,27 +246,27 @@ void Scene::loopSetDefault(int loopId) { _defaultLoop = loopId; } -void Scene::loopStartSpecial(int specialLoopMode, int loopId, int flags) { +void Scene::loopStartSpecial(int specialLoopMode, int loopId, bool immediately) { _specialLoopMode = specialLoopMode; _specialLoop = loopId; - int unknown = -1; - if (_specialLoopMode == 1) { - unknown = 0; + int repeats = -1; + if (_specialLoopMode == kSceneLoopModeChangeSet) { + repeats = 0; } - int loopMode = 1; - if (flags) { - loopMode = 2; + int loopMode = kLoopSetModeEnqueue; + if (immediately) { + loopMode = kLoopSetModeImmediate; } - _vqaPlayer->setLoop(_specialLoop, unknown, loopMode, &Scene::loopEndedStatic, this); - if (_specialLoopMode == 1) { - this->_nextSetId = _vm->_settings->getNewSet(); - this->_nextSceneId = _vm->_settings->getNewScene(); + _vqaPlayer->setLoop(_specialLoop, repeats, loopMode, &Scene::loopEndedStatic, this); + if (_specialLoopMode == kSceneLoopModeChangeSet) { + _nextSetId = _vm->_settings->getNewSet(); + _nextSceneId = _vm->_settings->getNewScene(); } - if (flags) { - this->_specialLoopAtEnd = true; + if (immediately) { + _specialLoopAtEnd = true; loopEnded(0, _specialLoop); } } @@ -288,30 +322,30 @@ const char *Scene::objectGetName(int objectId) { } void Scene::loopEnded(int frame, int loopId) { - if (_specialLoopMode && _specialLoopMode != 2 && _specialLoopMode != 3) { - if (_specialLoopMode == 1) { + if (_specialLoopMode && _specialLoopMode != kSceneLoopMode2 && _specialLoopMode != kSceneLoopModeSpinner) { + if (_specialLoopMode == kSceneLoopModeChangeSet) { _defaultLoopSet = true; _specialLoopAtEnd = false; _vm->playerLosesControl(); } } else if (_specialLoopAtEnd) { - _vqaPlayer->setLoop(_defaultLoop, -1, 1, &Scene::loopEndedStatic, this); + _vqaPlayer->setLoop(_defaultLoop, -1, kLoopSetModeEnqueue, &Scene::loopEndedStatic, this); _defaultLoopSet = true; _specialLoopAtEnd = false; - if (_specialLoopMode == 0) { + if (_specialLoopMode == kSceneLoopModeLoseControl) { _vm->playerLosesControl(); } } else { - if (_specialLoopMode == 0) { + if (_specialLoopMode == kSceneLoopModeLoseControl) { _vm->playerGainsControl(); _playerWalkedIn = true; } - if (_specialLoopMode == 3) { - _vm->_spinner->setIsOpen(); + if (_specialLoopMode == kSceneLoopModeSpinner) { + _vm->_spinner->open(); } _specialLoopMode = -1; _specialLoop = -1; - _vqaPlayer->setLoop(_defaultLoop + 1, -1, 0, nullptr, nullptr); + _vqaPlayer->setLoop(_defaultLoop + 1, -1, kLoopSetModeJustStart, nullptr, nullptr); _specialLoopAtEnd = true; } } diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h index c90258ce53..65ad5bf431 100644 --- a/engines/bladerunner/scene.h +++ b/engines/bladerunner/scene.h @@ -23,16 +23,22 @@ #ifndef BLADERUNNER_SCENE_H #define BLADERUNNER_SCENE_H -#include "bladerunner/bladerunner.h" - -#include "bladerunner/regions.h" -#include "bladerunner/set.h" -#include "bladerunner/view.h" -#include "bladerunner/vqa_player.h" +#include "bladerunner/vector.h" namespace BladeRunner { class BladeRunnerEngine; +class BoundingBox; +class Regions; +class Set; +class VQAPlayer; + +enum SceneLoopMode { + kSceneLoopModeLoseControl = 0, + kSceneLoopModeChangeSet = 1, + kSceneLoopMode2 = 2, + kSceneLoopModeSpinner = 3 +}; class Scene { BladeRunnerEngine *_vm; @@ -58,42 +64,23 @@ private: public: Set *_set; - Regions* _regions; - Regions* _exits; + Regions *_regions; + Regions *_exits; // _default_loop_id = 0; // _scene_vqa_frame_number = -1; public: - Scene(BladeRunnerEngine *vm) - : _vm(vm), - _setId(-1), - _sceneId(-1), - _vqaPlayer(nullptr), - _defaultLoop(0), - _introFinished(false), - _nextSetId(-1), - _nextSceneId(-1), - _playerWalkedIn(false), - _set(new Set(vm)), - _regions(new Regions()), - _exits(new Regions()) - {} - - ~Scene() { - delete _set; - delete _regions; - delete _exits; - delete _vqaPlayer; - } + Scene(BladeRunnerEngine *vm); + ~Scene(); bool open(int setId, int sceneId, bool isLoadingGame); bool close(bool isLoadingGame); int advanceFrame(); void setActorStart(Vector3 position, int facing); - void loopSetDefault(int a); - void loopStartSpecial(int a, int b, int c); + void loopSetDefault(int loopId); + void loopStartSpecial(int specialLoopMode, int loopId, bool immediately); int getSetId() const { return _setId; } int getSceneId() const { return _sceneId; } diff --git a/engines/bladerunner/script/init.cpp b/engines/bladerunner/script/init.cpp index 76f68edee3..ed1140582d 100644 --- a/engines/bladerunner/script/init.cpp +++ b/engines/bladerunner/script/init.cpp @@ -2642,16 +2642,16 @@ void ScriptInit::Init_CDB() { } void ScriptInit::Init_Spinner() { - Spinner_Set_Selectable_Destination_Flag(0, 1); - Spinner_Set_Selectable_Destination_Flag(1, 1); - Spinner_Set_Selectable_Destination_Flag(2, 1); - Spinner_Set_Selectable_Destination_Flag(3, 0); - Spinner_Set_Selectable_Destination_Flag(4, 0); - Spinner_Set_Selectable_Destination_Flag(5, 0); - Spinner_Set_Selectable_Destination_Flag(6, 0); - Spinner_Set_Selectable_Destination_Flag(7, 0); - Spinner_Set_Selectable_Destination_Flag(8, 0); - Spinner_Set_Selectable_Destination_Flag(9, 0); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationPoliceStation, true); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationMcCoysApartment, true); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationRuncitersAnimals, true); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationChinatown, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationAnimoidRow, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationTyrellBuilding, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationDNARow, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationBradburyBuilding, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationNightclubRow, false); + Spinner_Set_Selectable_Destination_Flag(kSpinnerDestinationHysteriaHall, false); } void ScriptInit::Init_Actor_Friendliness() { diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp index c4352706f5..72f4e50a53 100644 --- a/engines/bladerunner/script/script.cpp +++ b/engines/bladerunner/script/script.cpp @@ -22,14 +22,13 @@ #include "bladerunner/script/script.h" -#include "bladerunner/bladerunner.h" - #include "bladerunner/actor.h" #include "bladerunner/actor_combat.h" #include "bladerunner/adq.h" #include "bladerunner/ambient_sounds.h" #include "bladerunner/audio_player.h" #include "bladerunner/audio_speech.h" +#include "bladerunner/bladerunner.h" #include "bladerunner/crimes_database.h" #include "bladerunner/combat.h" #include "bladerunner/dialogue_menu.h" @@ -39,6 +38,8 @@ #include "bladerunner/items.h" #include "bladerunner/item_pickup.h" #include "bladerunner/movement_track.h" +#include "bladerunner/regions.h" +#include "bladerunner/set.h" #include "bladerunner/settings.h" #include "bladerunner/set_effects.h" #include "bladerunner/scene.h" @@ -46,6 +47,7 @@ #include "bladerunner/slice_animations.h" #include "bladerunner/slice_renderer.h" #include "bladerunner/spinner.h" +#include "bladerunner/suspects_database.h" #include "bladerunner/text_resource.h" #include "bladerunner/vector.h" #include "bladerunner/waypoints.h" @@ -499,7 +501,7 @@ bool ScriptBase::Loop_Actor_Walk_To_XYZ(int actorId, float x, float y, float z, //TODO: //PlayerActorIdle = 0; bool isRunning; - bool result = _vm->_actors[actorId]->loopWalkToXYZ(Vector3(x, y, z), destinationOffset, a5, run, 1, &isRunning); + bool result = _vm->_actors[actorId]->loopWalkToXYZ(Vector3(x, y, z), destinationOffset, a5, run, true, &isRunning); // if (PlayerActorIdle == 1) { // result = 1; @@ -829,12 +831,12 @@ void ScriptBase::Scene_Loop_Set_Default(int loopId) { _vm->_scene->loopSetDefault(loopId); } -void ScriptBase::Scene_Loop_Start_Special(int sceneLoopMode, int loopId, int c) { - if (sceneLoopMode == 1) { - c = 1; +void ScriptBase::Scene_Loop_Start_Special(int sceneLoopMode, int loopId, bool immediately) { + if (sceneLoopMode == kSceneLoopModeChangeSet) { + immediately = true; } - _vm->_scene->loopStartSpecial(sceneLoopMode, loopId, c); - if (sceneLoopMode == 1) { + _vm->_scene->loopStartSpecial(sceneLoopMode, loopId, immediately); + if (sceneLoopMode == kSceneLoopModeChangeSet) { _vm->_settings->clearNewSetAndScene(); } } @@ -843,54 +845,44 @@ void ScriptBase::Outtake_Play(int id, int noLocalization, int container) { _vm->outtakePlay(id, noLocalization, container); } -void ScriptBase::Ambient_Sounds_Add_Sound(int id, int time1, int time2, int volume1, int volume2, int pan1begin, int pan1end, int pan2begin, int pan2end, int priority, int unk) { - _vm->_ambientSounds->addSound(id, time1, time2, volume1, volume2, pan1begin, pan1end, pan2begin, pan2end, priority, unk); +void ScriptBase::Ambient_Sounds_Add_Sound(int sfxId, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk) { + _vm->_ambientSounds->addSound(sfxId, timeMin, timeMax, volumeMin, volumeMax, panStartMin, panStartMax, panEndMin, panEndMax, priority, unk); } -void ScriptBase::Ambient_Sounds_Remove_Sound(int id, bool a2) { - //TODO - warning("Ambient_Sounds_Remove_Sound(%d, %d)", id, a2); +void ScriptBase::Ambient_Sounds_Remove_Sound(int sfxId, bool stopPlaying) { + _vm->_ambientSounds->removeNonLoopingSound(sfxId, stopPlaying); } -void ScriptBase::Ambient_Sounds_Add_Speech_Sound(int id, int unk1, int time1, int time2, int volume1, int volume2, int pan1begin, int pan1end, int pan2begin, int pan2end, int priority, int unk2){ - //TODO - warning("Ambient_Sounds_Add_Speech_Sound(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", id, unk1, time1, time2, volume1, volume2, pan1begin, pan1end, pan2begin, pan2end, priority, unk2); +void ScriptBase::Ambient_Sounds_Add_Speech_Sound(int actorId, int sentenceId, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk){ + _vm->_ambientSounds->addSpeech(actorId, sentenceId, timeMin, timeMax, volumeMin, volumeMax, panStartMin, panStartMax, panEndMin, panEndMax, priority, unk); } // ScriptBase::Ambient_Sounds_Remove_Speech_Sound -int ScriptBase::Ambient_Sounds_Play_Sound(int a1, int a2, int a3, int a4, int a5) { - //TODO - warning("Ambient_Sounds_Remove_Sound(%d, %d, %d, %d, %d)", a1, a2, a3, a4, a5); - return -1; +void ScriptBase::Ambient_Sounds_Play_Sound(int sfxId, int volume, int panStart, int panEnd, int priority) { + _vm->_ambientSounds->playSound(sfxId, volume, panStart, panEnd, priority); } // ScriptBase::Ambient_Sounds_Play_Speech_Sound -void ScriptBase::Ambient_Sounds_Remove_All_Non_Looping_Sounds(int time) { - //TODO - warning("Ambient_Sounds_Remove_All_Non_Looping_Sounds(%d)", time); - // _vm->_ambientSounds->removeAllNonLoopingSounds(time); +void ScriptBase::Ambient_Sounds_Remove_All_Non_Looping_Sounds(bool stopPlaying) { + _vm->_ambientSounds->removeAllNonLoopingSounds(stopPlaying); } -void ScriptBase::Ambient_Sounds_Add_Looping_Sound(int id, int volume, int pan, int fadeInTime) { - _vm->_ambientSounds->addLoopingSound(id, volume, pan, fadeInTime); +void ScriptBase::Ambient_Sounds_Add_Looping_Sound(int sfxId, int volume, int pan, int delay) { + _vm->_ambientSounds->addLoopingSound(sfxId, volume, pan, delay); } -void ScriptBase::Ambient_Sounds_Adjust_Looping_Sound(int id, int panBegin, int panEnd, int a4) { - //TODO - warning("Ambient_Sounds_Adjust_Looping_Sound(%d, %d, %d, %d)", id, panBegin, panEnd, a4); +void ScriptBase::Ambient_Sounds_Adjust_Looping_Sound(int sfxId, int volume, int pan, int delay) { + _vm->_ambientSounds->adjustLoopingSound(sfxId, volume, pan, delay); } -void ScriptBase::Ambient_Sounds_Remove_Looping_Sound(int id, bool a2){ - //TODO - warning("Ambient_Sounds_Remove_Looping_Sound(%d, %d)", id, a2); +void ScriptBase::Ambient_Sounds_Remove_Looping_Sound(int sfxId, int delay){ + _vm->_ambientSounds->removeLoopingSound(sfxId, delay); } -void ScriptBase::Ambient_Sounds_Remove_All_Looping_Sounds(int time) { - //TODO - warning("Ambient_Sounds_Remove_All_Looping_Sounds(%d)", time); - // _vm->_ambientSounds->removeAllLoopingSounds(time); +void ScriptBase::Ambient_Sounds_Remove_All_Looping_Sounds(int delay) { + _vm->_ambientSounds->removeAllLoopingSounds(delay); } void ScriptBase::Setup_Scene_Information(float actorX, float actorY, float actorZ, int actorFacing) { @@ -1067,14 +1059,14 @@ bool ScriptBase::SDB_Add_Other_Clue(int suspectId, int clueId) { return _vm->_suspectsDatabase->get(suspectId)->addOtherClue(clueId); } -void ScriptBase::Spinner_Set_Selectable_Destination_Flag(int a1, int a2) { - _vm->_spinner->setSelectableDestinationFlag(a1, a2); +void ScriptBase::Spinner_Set_Selectable_Destination_Flag(int destination, bool selectable) { + _vm->_spinner->setSelectableDestinationFlag(destination, selectable); } // ScriptBase::Spinner_Query_Selectable_Destination_Flag -int ScriptBase::Spinner_Interface_Choose_Dest(int a1, int a2) { - return _vm->_spinner->interfaceChooseDest(a1, a2); +int ScriptBase::Spinner_Interface_Choose_Dest(int loopId, bool immediately) { + return _vm->_spinner->chooseDestination(loopId, immediately); } void ScriptBase::ESPER_Flag_To_Activate() { @@ -1131,7 +1123,7 @@ void ScriptBase::Actor_Retired_Here(int actorId, int width, int height, int reti Vector3 actorPosition; actor->getXYZ(&actorPosition.x, &actorPosition.y, &actorPosition.z); actor->retire(retired, width, height, retiredByActorId); - actor->setAtXYZ(actorPosition, actor->getFacing(), true, 0, true); + actor->setAtXYZ(actorPosition, actor->getFacing(), true, false, true); _vm->_sceneObjects->setRetired(actorId + SCENE_OBJECTS_ACTORS_OFFSET, true); } diff --git a/engines/bladerunner/script/script.h b/engines/bladerunner/script/script.h index 61cee3aa9b..c282bccd1d 100644 --- a/engines/bladerunner/script/script.h +++ b/engines/bladerunner/script/script.h @@ -567,20 +567,20 @@ protected: bool Music_Is_Playing(); void Overlay_Play(const char *overlay, int a2, int a3, int a4, int a5); void Overlay_Remove(const char *overlay); - void Scene_Loop_Set_Default(int); - void Scene_Loop_Start_Special(int, int, int); + void Scene_Loop_Set_Default(int loopId); + void Scene_Loop_Start_Special(int sceneLoopMode, int loopId, bool immediately); void Outtake_Play(int id, int noLocalization = false, int container = -1); - void Ambient_Sounds_Add_Sound(int id, int time1, int time2, int volume1, int volume2, int pan1begin, int pan1end, int pan2begin, int pan2end, int priority, int unk); - void Ambient_Sounds_Remove_Sound(int id, bool a2); - void Ambient_Sounds_Add_Speech_Sound(int id, int unk1, int time1, int time2, int volume1, int volume2, int pan1begin, int pan1end, int pan2begin, int pan2end, int priority, int unk2); + void Ambient_Sounds_Add_Sound(int sfxId, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk); + void Ambient_Sounds_Remove_Sound(int sfxId, bool stopPlaying); + void Ambient_Sounds_Add_Speech_Sound(int actorId, int sentenceId, int timeMin, int timeMax, int volumeMin, int volumeMax, int panStartMin, int panStartMax, int panEndMin, int panEndMax, int priority, int unk); // Ambient_Sounds_Remove_Speech_Sound - int Ambient_Sounds_Play_Sound(int a1, int a2, int a3, int a4, int a5); + void Ambient_Sounds_Play_Sound(int sfxId, int volume, int panStart, int panEnd, int priority); // Ambient_Sounds_Play_Speech_Sound - void Ambient_Sounds_Remove_All_Non_Looping_Sounds(int time); - void Ambient_Sounds_Add_Looping_Sound(int id, int volume, int pan, int fadeInTime); - void Ambient_Sounds_Adjust_Looping_Sound(int id, int panBegin, int panEnd, int a4); - void Ambient_Sounds_Remove_Looping_Sound(int id, bool a2); - void Ambient_Sounds_Remove_All_Looping_Sounds(int time); + void Ambient_Sounds_Remove_All_Non_Looping_Sounds(bool stopPlaying); + void Ambient_Sounds_Add_Looping_Sound(int sfxId, int volume, int pan, int delay); + void Ambient_Sounds_Adjust_Looping_Sound(int sfxId, int volume, int pan, int delay); + void Ambient_Sounds_Remove_Looping_Sound(int sfxId, int delay); + void Ambient_Sounds_Remove_All_Looping_Sounds(int delay); void Setup_Scene_Information(float actorX, float actorY, float actorZ, int actorFacing); bool Dialogue_Menu_Appear(int x, int y); bool Dialogue_Menu_Disappear(); @@ -625,9 +625,9 @@ protected: bool SDB_Add_Replicant_Clue(int suspectId, int clueId); bool SDB_Add_Non_Replicant_Clue(int suspectId, int clueId); bool SDB_Add_Other_Clue(int suspectId, int clueId); - void Spinner_Set_Selectable_Destination_Flag(int a1, int a2); - // Spinner_Query_Selectable_Destination_Flag - int Spinner_Interface_Choose_Dest(int a1, int a2); + void Spinner_Set_Selectable_Destination_Flag(int destination, bool selectable); + // Spinner_Query_Selectable_Destination_Flag(int destination); + int Spinner_Interface_Choose_Dest(int loopId, bool immediately); void ESPER_Flag_To_Activate(); bool Voight_Kampff_Activate(int a1, int a2); int Elevator_Activate(int elevatorId); diff --git a/engines/bladerunner/settings.h b/engines/bladerunner/settings.h index de9846a854..105c8fcdca 100644 --- a/engines/bladerunner/settings.h +++ b/engines/bladerunner/settings.h @@ -27,6 +27,14 @@ namespace BladeRunner { class BladeRunnerEngine; +enum PlayerAgenda { + kPlayerAgendaPolite = 0, + kPlayerAgendaNormal = 1, + kPlayerAgendaSurly = 2, + kPlayerAgendaErratic = 3, + kPlayerAgendaUserChoice = 4, +}; + class Settings { BladeRunnerEngine *_vm; diff --git a/engines/bladerunner/spinner.cpp b/engines/bladerunner/spinner.cpp index 40650cbc22..acee0cb262 100644 --- a/engines/bladerunner/spinner.cpp +++ b/engines/bladerunner/spinner.cpp @@ -38,12 +38,19 @@ namespace BladeRunner { Spinner::Spinner(BladeRunnerEngine *vm) : _vm(vm) { reset(); - _imagePicker = new UIImagePicker(vm, SPINNER_DESTINATIONS); + _imagePicker = new UIImagePicker(vm, kSpinnerDestinations); + _vqaPlayer = nullptr; } Spinner::~Spinner() { delete _imagePicker; + reset(); + + if (_vqaPlayer != nullptr) { + _vqaPlayer->close(); + delete _vqaPlayer; + } } void Spinner::setSelectableDestinationFlag(int destination, bool selectable) { @@ -55,38 +62,39 @@ bool Spinner::querySelectableDestinationFlag(int destination) const { } SpinnerDestination SpinnerDestinationsNear[] = { - { 0, 0x0D2, 0x107, 0x107, 0x14C }, - { 1, 0x133, 0x14A, 0x169, 0x17D }, - { 2, 0x152, 0x089, 0x16A, 0x0A9 }, - { 3, 0x0F8, 0x087, 0x121, 0x0A8 }, - { 4, 0x160, 0x0DE, 0x17B, 0x0EE }, - { -1, -1, -1, -1, -1 } + { 0, { 210, 263, 263, 332 } }, + { 1, { 307, 330, 361, 381 } }, + { 2, { 338, 137, 362, 169 } }, + { 3, { 248, 135, 289, 168 } }, + { 4, { 352, 222, 379, 238 } }, + { -1, { -1,-1,-1,-1 } } + }; SpinnerDestination SpinnerDestinationsMedium[] = { - { 0, 0x0FC, 0x0F2, 0x117, 0x11B }, - { 1, 0x12D, 0x111, 0x148, 0x130 }, - { 2, 0x13F, 0x0B6, 0x150, 0x0C8 }, - { 3, 0x10D, 0x0B5, 0x125, 0x0C8 }, - { 4, 0x145, 0x0E3, 0x159, 0x0F0 }, - { 5, 0x103, 0x04A, 0x17C, 0x077 }, - { 6, 0x0CB, 0x07C, 0x0E0, 0x088 }, - { 7, 0x0C8, 0x093, 0x0DE, 0x0AA }, - { -1, -1, -1, -1, -1 } + { 0, { 252, 242, 279, 283 } }, + { 1, { 301, 273, 328, 304 } }, + { 2, { 319, 182, 336, 200 } }, + { 3, { 269, 181, 293, 200 } }, + { 4, { 325, 227, 345, 240 } }, + { 5, { 259, 74, 380, 119 } }, + { 6, { 203, 124, 224, 136 } }, + { 7, { 200, 147, 222, 170 } }, + { -1, { -1,-1,-1,-1 } } }; SpinnerDestination SpinnerDestinationsFar[] = { - { 0, 0x0DC, 0x0E3, 0x0F6, 0x106 }, - { 1, 0x104, 0x0FC, 0x11E, 0x117 }, - { 2, 0x11E, 0x0B2, 0x12E, 0x0C4 }, - { 3, 0x0F4, 0x0B2, 0x107, 0x0C3 }, - { 4, 0x120, 0x0D8, 0x132, 0x0E4 }, - { 5, 0x0F9, 0x04D, 0x161, 0x07C }, - { 6, 0x0BE, 0x07F, 0x0D0, 0x08A }, - { 7, 0x0B9, 0x095, 0x0CE, 0x0AA }, - { 8, 0x18E, 0x0F9, 0x1A3, 0x10C }, - { 9, 0x186, 0x0DA, 0x1A3, 0x0EC }, - { -1, -1, -1, -1, -1 } + { 0, { 220, 227, 246, 262 } }, + { 1, { 260, 252, 286, 279 } }, + { 2, { 286, 178, 302, 196 } }, + { 3, { 244, 178, 263, 195 } }, + { 4, { 288, 216, 306, 228 } }, + { 5, { 249, 77, 353, 124 } }, + { 6, { 190, 127, 208, 138 } }, + { 7, { 185, 149, 206, 170 } }, + { 8, { 398, 249, 419, 268 } }, + { 9, { 390, 218, 419, 236 } }, + { -1, { -1, -1, -1, -1 } } }; static void spinner_mouseInCallback(int, void*); @@ -94,16 +102,17 @@ static void spinner_mouseOutCallback(int, void*); static void spinner_mouseDownCallback(int, void*); static void spinner_mouseUpCallback(int, void*); -int Spinner::interfaceChooseDest(int loopId, int loopFlag) { +int Spinner::chooseDestination(int loopId, bool immediately) { _selectedDestination = 0; - if (!_vm->openArchive("MODE.MIX")) + if (!_vm->openArchive("MODE.MIX")) { return 0; + } if (loopId < 0) { _isOpen = true; } else { _vm->playerLosesControl(); - _vm->_scene->loopStartSpecial(3, loopId, loopFlag); + _vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately); while (!_isOpen) { _vm->gameTick(); } @@ -119,10 +128,11 @@ int Spinner::interfaceChooseDest(int loopId, int loopFlag) { // Determine which map we need to show to include the active destinations uint8 mapmask = 0; - uint8 mapmaskv[SPINNER_DESTINATIONS] = { 1, 1, 1, 1, 1, 3, 3, 3, 7, 7 }; - for (int i = 0; i != SPINNER_DESTINATIONS; ++i) { - if (_isDestinationSelectable[i]) + 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; @@ -151,8 +161,8 @@ int Spinner::interfaceChooseDest(int loopId, int loopFlag) { return -1; } - _vqaPlayer->setLoop(spinnerLoopId, -1, 2, nullptr, nullptr); - _vqaPlayer->setLoop(spinnerLoopId + 1, -1, 0, nullptr, nullptr); + _vqaPlayer->setLoop(spinnerLoopId, -1, kLoopSetModeImmediate, nullptr, nullptr); + _vqaPlayer->setLoop(spinnerLoopId + 1, -1, kLoopSetModeJustStart, nullptr, nullptr); for (int j = 0; j != shapeCount; ++j) { _shapes.push_back(new Shape(_vm)); @@ -162,17 +172,15 @@ int Spinner::interfaceChooseDest(int loopId, int loopFlag) { _imagePicker->resetImages(); for (SpinnerDestination *dest = _destinations; dest->id != -1; ++dest) { - if (!_isDestinationSelectable[dest->id]) + if (!_isDestinationSelectable[dest->id]) { continue; + } const char *tooltip = _vm->_textSpinnerDestinations->getText(dest->id); _imagePicker->defineImage( dest->id, - dest->left, - dest->top, - dest->right, - dest->bottom, + dest->rect, _shapes[dest->id], _shapes[dest->id + _shapes.size() / 2], _shapes[dest->id + _shapes.size() / 2], @@ -180,7 +188,7 @@ int Spinner::interfaceChooseDest(int loopId, int loopFlag) { ); } - _imagePicker->setCallbacks( + _imagePicker->activate( spinner_mouseInCallback, spinner_mouseOutCallback, spinner_mouseDownCallback, @@ -194,14 +202,26 @@ int Spinner::interfaceChooseDest(int loopId, int loopFlag) { _vm->gameTick(); } while (_selectedDestination == -1); - // TODO: Unfreeze game time - _isOpen = false; - // TODO: _vm->_scene->resume(); + _imagePicker->deactivate(); - for (int i = 0; i != (int)_shapes.size(); ++i) + for (int i = 0; i != (int)_shapes.size(); ++i) { delete _shapes[i]; + } _shapes.clear(); + if (_vqaPlayer != nullptr) { + _vqaPlayer->close(); + delete _vqaPlayer; + _vqaPlayer = nullptr; + } + + _vm->closeArchive("MODE.MIX"); + + _isOpen = false; + + // TODO: Unfreeze game time + // TODO: _vm->_scene->resume(); + return _selectedDestination; } @@ -214,14 +234,13 @@ static void spinner_mouseOutCallback(int, void*) { static void spinner_mouseDownCallback(int, void*) { } -static void spinner_mouseUpCallback(int image, void *data) { +static void spinner_mouseUpCallback(int image, void *self) { if (image >= 0 && image < 10) { - Spinner *spinner = (Spinner *)data; - spinner->setSelectedDestination(image); + ((Spinner *)self)->setSelectedDestination(image); } } -void Spinner::setIsOpen() { +void Spinner::open() { _isOpen = true; } @@ -230,18 +249,19 @@ bool Spinner::isOpen() const { } int Spinner::handleMouseUp(int x, int y) { - _imagePicker->handleMouseAction(x, y, false, true, 0); + _imagePicker->handleMouseAction(x, y, false, true, false); return false; } int Spinner::handleMouseDown(int x, int y) { - _imagePicker->handleMouseAction(x, y, true, false, 0); + _imagePicker->handleMouseAction(x, y, true, false, false); return false; } void Spinner::tick() { - if (!_vm->_gameIsRunning) + if (!_vm->_gameIsRunning) { return; + } int frame = _vqaPlayer->update(); assert(frame >= -1); @@ -249,8 +269,6 @@ void Spinner::tick() { // vqaPlayer renders to _surfaceInterface blit(_vm->_surfaceInterface, _vm->_surfaceGame); - _imagePicker->draw(_vm->_surfaceGame); - Common::Point p = _vm->getMousePos(); _imagePicker->handleMouseAction(p.x, p.y, false, false, false); if (_imagePicker->hasHoveredImage()) { @@ -258,7 +276,9 @@ void Spinner::tick() { } else { _vm->_mouse->setCursor(0); } + _imagePicker->draw(_vm->_surfaceGame); _vm->_mouse->draw(_vm->_surfaceGame, p.x, p.y); + _imagePicker->drawTooltip(_vm->_surfaceGame, p.x, p.y); _vm->blitToScreen(_vm->_surfaceGame); _vm->_system->delayMillis(10); @@ -269,8 +289,8 @@ void Spinner::setSelectedDestination(int destination) { } void Spinner::reset() { - for (int i = 0; i != SPINNER_DESTINATIONS; ++i) { - _isDestinationSelectable[i] = 0; + for (int i = 0; i != kSpinnerDestinations; ++i) { + _isDestinationSelectable[i] = false; } _isOpen = false; @@ -278,13 +298,21 @@ void Spinner::reset() { _selectedDestination = -1; _imagePicker = nullptr; - for (int i = 0; i != (int)_shapes.size(); ++i) + for (int i = 0; i != (int)_shapes.size(); ++i) { delete _shapes[i]; + } _shapes.clear(); } void Spinner::resume() { - // TODO + if(_vqaPlayer == nullptr) { + return; + } + + //_vqa->clear(); + _vqaPlayer->setLoop(0, -1, kLoopSetModeImmediate, nullptr, nullptr); + tick(); + _vqaPlayer->setLoop(1, -1, kLoopSetModeJustStart, nullptr, nullptr); } } // End of namespace BladeRunner diff --git a/engines/bladerunner/spinner.h b/engines/bladerunner/spinner.h index 19021fac1a..cfa593b1fb 100644 --- a/engines/bladerunner/spinner.h +++ b/engines/bladerunner/spinner.h @@ -24,6 +24,7 @@ #define BLADERUNNER_SPINNER_H #include "common/array.h" +#include "common/rect.h" namespace BladeRunner { @@ -32,19 +33,16 @@ class Shape; class VQAPlayer; class UIImagePicker; -#define SPINNER_DESTINATIONS 10 - struct SpinnerDestination { - int id; - int left; - int top; - int right; - int bottom; + int id; + Common::Rect rect; }; class Spinner { + static const int kSpinnerDestinations = 10; + BladeRunnerEngine *_vm; - bool _isDestinationSelectable[SPINNER_DESTINATIONS]; + bool _isDestinationSelectable[kSpinnerDestinations]; bool _isOpen; VQAPlayer *_vqaPlayer; SpinnerDestination *_destinations; @@ -59,9 +57,9 @@ public: void setSelectableDestinationFlag(int destination, bool selectable); bool querySelectableDestinationFlag(int destination) const; - int interfaceChooseDest(int vqaLoopId, int loopFlag); + int chooseDestination(int vqaLoopId, bool immediately); - void setIsOpen(); + void open(); bool isOpen() const; int handleMouseUp(int x, int y); diff --git a/engines/bladerunner/ui_image_picker.cpp b/engines/bladerunner/ui_image_picker.cpp index 654129754c..eb0b57505c 100644 --- a/engines/bladerunner/ui_image_picker.cpp +++ b/engines/bladerunner/ui_image_picker.cpp @@ -23,33 +23,25 @@ #include "bladerunner/ui_image_picker.h" #include "bladerunner/bladerunner.h" - +#include "bladerunner/font.h" +#include "bladerunner/mouse.h" #include "bladerunner/shape.h" #include "common/rect.h" +#include "common/str.h" #include "graphics/surface.h" namespace BladeRunner { -struct UIImagePickerImage { - int active; - Common::Rect rect; - Shape *shapeUp; - Shape *shapeHovered; - Shape *shapeDown; - const char *tooltip; -}; - UIImagePicker::UIImagePicker(BladeRunnerEngine *vm, int imageCount) : _vm(vm) { reset(); - _images = new UIImagePickerImage[imageCount]; + _images.resize(imageCount); _imageCount = imageCount; resetImages(); } UIImagePicker::~UIImagePicker() { - delete[] _images; - _images = nullptr; + _images.clear(); reset(); } @@ -59,28 +51,35 @@ void UIImagePicker::resetImages() { } } -bool UIImagePicker::defineImage(int i, int left, int top, int right, int bottom, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip) { - if (i < 0 || i >= _imageCount || _images[i].active) +bool UIImagePicker::defineImage(int i, Common::Rect rect, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip) { + if (i < 0 || i >= _imageCount || _images[i].active) { return false; + } UIImagePickerImage &img = _images[i]; - img.rect.left = left; - img.rect.top = top; - img.rect.right = right + 1; - img.rect.bottom = bottom + 1; + img.rect = rect; + // for rect to be inclusive + img.rect.right += 1; + img.rect.bottom += 1; img.shapeUp = shapeUp; img.shapeHovered = shapeHovered; img.shapeDown = shapeDown; img.active = true; - img.tooltip = tooltip; + + 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) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } UIImagePickerImage &img = _images[i]; @@ -90,8 +89,9 @@ bool UIImagePicker::setImageTop(int i, int top) { } bool UIImagePicker::setImageLeft(int i, int left) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } UIImagePickerImage &img = _images[i]; @@ -101,8 +101,9 @@ bool UIImagePicker::setImageLeft(int i, int left) { } bool UIImagePicker::setImageShapeUp(int i, Shape *shapeUp) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } _images[i].shapeUp = shapeUp; @@ -110,8 +111,9 @@ bool UIImagePicker::setImageShapeUp(int i, Shape *shapeUp) { } bool UIImagePicker::setImageShapeHovered(int i, Shape *shapeHovered) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } _images[i].shapeHovered = shapeHovered; @@ -119,8 +121,9 @@ bool UIImagePicker::setImageShapeHovered(int i, Shape *shapeHovered) { } bool UIImagePicker::setImageShapeDown(int i, Shape *shapeDown) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } _images[i].shapeDown = shapeDown; @@ -128,8 +131,9 @@ bool UIImagePicker::setImageShapeDown(int i, Shape *shapeDown) { } bool UIImagePicker::setImageTooltip(int i, const char *tooltip) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } _images[i].tooltip = tooltip; @@ -137,18 +141,19 @@ bool UIImagePicker::setImageTooltip(int i, const char *tooltip) { } bool UIImagePicker::resetActiveImage(int i) { - if (i < 0 || i >= _imageCount || !_images[i].active) + if (i < 0 || i >= _imageCount || !_images[i].active) { return false; + } resetImage(i); return true; } -void UIImagePicker::setCallbacks(UIImagePickerCallback *mouseInCallback, - UIImagePickerCallback *mouseOutCallback, - UIImagePickerCallback *mouseDownCallback, - UIImagePickerCallback *mouseUpCallback, - void *callbackData) +void UIImagePicker::activate(UIImagePickerCallback *mouseInCallback, + UIImagePickerCallback *mouseOutCallback, + UIImagePickerCallback *mouseDownCallback, + UIImagePickerCallback *mouseUpCallback, + void *callbackData) { _isButtonDown = false; _mouseInCallback = mouseInCallback; @@ -162,19 +167,29 @@ void UIImagePicker::setCallbacks(UIImagePickerCallback *mouseInCallback, _pressedImageIndex = -1; } -void UIImagePicker::resetCallbacks() {} - -// TODO -void UIImagePicker::drawTooltip() {} +void UIImagePicker::deactivate() { + _isButtonDown = false; + _mouseInCallback = nullptr; + _mouseOutCallback = nullptr; + _mouseDownCallback = nullptr; + _mouseUpCallback = nullptr; + _callbackData = nullptr; + _hoverStartTimestamp = 0; + _isVisible = false; + _hoveredImageIndex = -1; + _pressedImageIndex = -1; +} void UIImagePicker::draw(Graphics::Surface &surface) { - if (!_isVisible) + if (!_isVisible) { return; + } for (int i = 0; i != _imageCount; ++i) { UIImagePickerImage &img = _images[i]; - if (!img.active) + if (!img.active) { continue; + } // TODO: Check interaction with Mouse::isDisabled if (i == _hoveredImageIndex && i == _pressedImageIndex && _isButtonDown) { @@ -193,6 +208,47 @@ void UIImagePicker::draw(Graphics::Surface &surface) { } } +void UIImagePicker::drawTooltip(Graphics::Surface &surface, int x, int y) { + if (!_isVisible) { + return; + } + + if (_hoveredImageIndex == -1 || _vm->_mouse->isDisabled() || !_images[_hoveredImageIndex].active || _vm->getTotalPlayTime() < _hoverStartTimestamp + 1000) { + return; + } + + Common::String &tooltip = _images[_hoveredImageIndex].tooltip; + int width = _vm->_mainFont->getTextWidth(tooltip) + 1; + int height = _vm->_mainFont->getTextHeight(tooltip) + 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 >= 640) { + rect.right = 639; + rect.left = 636 - width; + } + + rect.bottom = height + rect.top + 1; + if (rect.bottom >= 480) { + rect.bottom = 479; + rect.top = 478 - height; + } + + surface.fillRect(rect, 0); + surface.frameRect(rect, 0x7FFF); + _vm->_mainFont->drawColor(tooltip, surface, rect.left + 2, rect.top, 0x7FFF); +} + void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ignore) { if (!_isVisible || ignore) { return; @@ -210,13 +266,16 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign if (hoveredImageIndex != _hoveredImageIndex) { if (!_isButtonDown) { if (hoveredImageIndex == -1) { - if (_mouseOutCallback) + if (_mouseOutCallback) { _mouseOutCallback(hoveredImageIndex, _callbackData); + } } else { - if (_mouseInCallback) + if (_mouseInCallback) { _mouseInCallback(hoveredImageIndex, _callbackData); + } } } + _hoverStartTimestamp = _vm->getTotalPlayTime(); _hoveredImageIndex = hoveredImageIndex; } @@ -224,8 +283,11 @@ void UIImagePicker::handleMouseAction(int x, int y, bool down, bool up, bool ign if (down && !_isButtonDown) { _isButtonDown = true; _pressedImageIndex = _hoveredImageIndex; - if (_mouseDownCallback) - _mouseDownCallback(_hoveredImageIndex, _callbackData); + if (_hoveredImageIndex != 1) { + if (_mouseDownCallback) { + _mouseDownCallback(_hoveredImageIndex, _callbackData); + } + } } // If mouse button changed to released @@ -253,7 +315,7 @@ void UIImagePicker::resetImage(int i) { img.shapeUp = nullptr; img.shapeHovered = nullptr; img.shapeDown = nullptr; - img.tooltip = nullptr; + img.tooltip.clear(); } bool UIImagePicker::hasHoveredImage() { diff --git a/engines/bladerunner/ui_image_picker.h b/engines/bladerunner/ui_image_picker.h index c55aa48a64..c06b556c14 100644 --- a/engines/bladerunner/ui_image_picker.h +++ b/engines/bladerunner/ui_image_picker.h @@ -23,6 +23,10 @@ #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; } @@ -31,10 +35,18 @@ namespace BladeRunner { class BladeRunnerEngine; class Shape; -struct UIImagePickerImage; typedef void UIImagePickerCallback(int, void*); +struct UIImagePickerImage { + int active; + Common::Rect rect; + Shape *shapeUp; + Shape *shapeHovered; + Shape *shapeDown; + Common::String tooltip; +}; + class UIImagePicker { BladeRunnerEngine *_vm; @@ -42,9 +54,9 @@ class UIImagePicker { int _imageCount; int _hoveredImageIndex; int _pressedImageIndex; - int _hoverStartTimestamp; + uint32 _hoverStartTimestamp; int _isButtonDown; - UIImagePickerImage *_images; + Common::Array<UIImagePickerImage> _images; UIImagePickerCallback *_mouseInCallback; UIImagePickerCallback *_mouseOutCallback; @@ -57,7 +69,7 @@ public: ~UIImagePicker(); void resetImages(); - bool defineImage(int i, int left, int top, int right, int bottom, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip); + bool defineImage(int i, Common::Rect rect, Shape *shapeUp, Shape *shapeHovered, Shape *shapeDown, const char *tooltip); bool setImageTop(int i, int top); bool setImageLeft(int i, int left); @@ -68,16 +80,16 @@ public: bool resetActiveImage(int i); - void setCallbacks(UIImagePickerCallback *mouseInCallback, - UIImagePickerCallback *mouseOutCallback, - UIImagePickerCallback *mouseDownCallback, - UIImagePickerCallback *mouseUpCallback, - void *callbackData); + void activate(UIImagePickerCallback *mouseInCallback, + UIImagePickerCallback *mouseOutCallback, + UIImagePickerCallback *mouseDownCallback, + UIImagePickerCallback *mouseUpCallback, + void *callbackData); - void resetCallbacks(); + void deactivate(); - void drawTooltip(); void draw(Graphics::Surface &surface); + void drawTooltip(Graphics::Surface &surface, int x, int y); void handleMouseAction(int x, int y, bool down, bool up, bool ignore = false); diff --git a/engines/bladerunner/view.cpp b/engines/bladerunner/view.cpp index ab9eb3e2e6..ed5cef9e3d 100644 --- a/engines/bladerunner/view.cpp +++ b/engines/bladerunner/view.cpp @@ -72,8 +72,8 @@ void View::calculateCameraPosition() { Matrix4x3 invertedMatrix = invertMatrix(_sliceViewMatrix); _cameraPosition.x = invertedMatrix(0, 3); - _cameraPosition.y = invertedMatrix(1, 3); - _cameraPosition.z = invertedMatrix(2, 3); + _cameraPosition.z = invertedMatrix(1, 3); // this is not a bug, it Z & Y are inverted in original source + _cameraPosition.y = invertedMatrix(2, 3); } Vector3 View::calculateScreenPosition(Vector3 worldPosition) { diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp index f6eecc85ed..12baa26d76 100644 --- a/engines/bladerunner/vqa_player.cpp +++ b/engines/bladerunner/vqa_player.cpp @@ -108,42 +108,40 @@ int VQAPlayer::update() { return -3; } + if (now < _frameNextTime) { + return -1; + } + + int frame = _frameNext; + _decoder.readFrame(_frameNext, 0x2); + _decoder.decodeVideoFrame(); -// TODO: preload audio in better way - int audioPreloadFrames = 3; - - if (now >= _frameNextTime) { - int frame = _frameNext; - _decoder.readFrame(_frameNext, 0x2); - _decoder.decodeVideoFrame(); - - if (_hasAudio) { - if (!_audioStarted) { - for (int i = 0; i < audioPreloadFrames; i++) { - if (_frameNext + i < _frameEnd) { - _decoder.readFrame(_frameNext + i, 0x1); - queueAudioFrame(_decoder.decodeAudioFrame()); - } + int audioPreloadFrames = 14; + + if (_hasAudio) { + if (!_audioStarted) { + for (int i = 0; i < audioPreloadFrames; i++) { + if (_frameNext + i < _frameEnd) { + _decoder.readFrame(_frameNext + i, 0x1); + queueAudioFrame(_decoder.decodeAudioFrame()); } - _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream); - _audioStarted = true; - } - if (_frameNext + audioPreloadFrames < _frameEnd) { - _decoder.readFrame(_frameNext + audioPreloadFrames, 0x1); - queueAudioFrame(_decoder.decodeAudioFrame()); } + _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _audioStream); + _audioStarted = true; } - if (_frameNextTime == 0) { - _frameNextTime = now + 60000 / 15; - } else { - _frameNextTime += 60000 / 15; + if (_frameNext + audioPreloadFrames < _frameEnd) { + _decoder.readFrame(_frameNext + audioPreloadFrames, 0x1); + queueAudioFrame(_decoder.decodeAudioFrame()); } - - _frameNext++; - return frame; + } + if (_frameNextTime == 0) { + _frameNextTime = now + 60000 / 15; + } else { + _frameNextTime += 60000 / 15; } - return -1; + _frameNext++; + return frame; } void VQAPlayer::updateZBuffer(ZBuffer *zbuffer) { @@ -159,7 +157,9 @@ void VQAPlayer::updateLights(Lights *lights) { } bool VQAPlayer::setLoop(int loop, int repeatsCount, int loopSetMode, void (*callback)(void *, int, int), void *callbackData) { +#if 0 debug("VQAPlayer::setBeginAndEndFrameFromLoop(%i, %i, %i), streamLoaded = %i", loop, repeatsCount, loopSetMode, _s != nullptr); +#endif if (_s == nullptr) { _loopInitial = loop; _repeatsCountInitial = repeatsCount; @@ -178,7 +178,9 @@ bool VQAPlayer::setLoop(int loop, int repeatsCount, int loopSetMode, void (*call } bool VQAPlayer::setBeginAndEndFrame(int begin, int end, int repeatsCount, int loopSetMode, void (*callback)(void *, int, int), void *callbackData) { +#if 0 debug("VQAPlayer::setBeginAndEndFrame(%i, %i, %i, %i), streamLoaded = %i", begin, end, repeatsCount, loopSetMode, _s != nullptr); +#endif if (repeatsCount < 0) { repeatsCount = -1; |