diff options
Diffstat (limited to 'engines/tsage')
-rw-r--r-- | engines/tsage/core.cpp | 37 | ||||
-rw-r--r-- | engines/tsage/ringworld2/ringworld2_logic.cpp | 12 | ||||
-rw-r--r-- | engines/tsage/ringworld2/ringworld2_logic.h | 1 | ||||
-rw-r--r-- | engines/tsage/ringworld2/ringworld2_scenes0.cpp | 2 | ||||
-rw-r--r-- | engines/tsage/ringworld2/ringworld2_scenes1.cpp | 10 | ||||
-rw-r--r-- | engines/tsage/ringworld2/ringworld2_scenes1.h | 2 | ||||
-rw-r--r-- | engines/tsage/sound.cpp | 164 | ||||
-rw-r--r-- | engines/tsage/sound.h | 36 |
8 files changed, 246 insertions, 18 deletions
diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp index 488eacceab..3332b12cf6 100644 --- a/engines/tsage/core.cpp +++ b/engines/tsage/core.cpp @@ -1749,9 +1749,22 @@ void SceneItem::display(int resNum, int lineNum, ...) { } g_globals->_sceneText.fixPriority(255); + + // In Return to Ringworld, if in voice mode only, don't show the text + if ((g_vm->getGameID() == GType_Ringworld2) && (R2_GLOBALS._speechSubtitles & SPEECH_VOICE) + && !(R2_GLOBALS._speechSubtitles & SPEECH_TEXT)) + g_globals->_sceneText.hide(); + g_globals->_sceneObjects->draw(); } + // For Return to Ringworld, play the voice overs in sequence + if ((g_vm->getGameID() == GType_Ringworld2) && (R2_GLOBALS._speechSubtitles & SPEECH_VOICE) + && !playList.empty()) { + R2_GLOBALS._playStream.play(*playList.begin(), NULL); + playList.pop_front(); + } + // Unless the flag is set to keep the message on-screen, show it until a mouse or keypress, then remove it if (!keepOnscreen && !msg.empty()) { Event event; @@ -1761,14 +1774,23 @@ void SceneItem::display(int resNum, int lineNum, ...) { EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) { GLOBALS._screenSurface.updateScreen(); g_system->delayMillis(10); - } - // For Return to Ringworld, play the voice overs in sequence - if ((g_vm->getGameID() == GType_Ringworld2) && !playList.empty() && !R2_GLOBALS._playStream.isPlaying()) { - R2_GLOBALS._playStream.play(*playList.begin(), NULL); - playList.pop_front(); + if ((g_vm->getGameID() == GType_Ringworld2) && (R2_GLOBALS._speechSubtitles & SPEECH_VOICE)) { + // Allow the play stream to do processing + R2_GLOBALS._playStream.dispatch(); + if (!R2_GLOBALS._playStream.isPlaying()) { + // Check if there are further voice samples to play + if (!playList.empty()) { + R2_GLOBALS._playStream.play(*playList.begin(), NULL); + playList.pop_front(); + } + } + } } + if (g_vm->getGameID() == GType_Ringworld2) + R2_GLOBALS._playStream.stop(); + g_globals->_sceneText.remove(); } @@ -2814,6 +2836,11 @@ void BackgroundSceneObject::setup2(int visage, int stripFrameNum, int frameNum, void BackgroundSceneObject::copySceneToBackground() { GLOBALS._sceneManager._scene->_backSurface.copyFrom(g_globals->gfxManager().getSurface(), 0, 0); + + // WORKAROUND: Since savegames don't store the active screen data, once we copy the + // foreground objects to the background, we have to prevent the scene being saved. + if (g_vm->getGameID() == GType_Ringworld2) + ((Ringworld2::SceneExt *)GLOBALS._sceneManager._scene)->_preventSaving = true; } /*--------------------------------------------------------------------------*/ diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index fc06b204cd..90df72ab32 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -321,9 +321,11 @@ bool Ringworld2Game::canLoadGameStateCurrently() { * Returns true if it is currently okay to save the game */ bool Ringworld2Game::canSaveGameStateCurrently() { - // Don't allow a game to be saved if a dialog is active or if an animation - // is playing - return g_globals->_gfxManagers.size() == 1 && R2_GLOBALS._animationCtr == 0; + // Don't allow a game to be saved if a dialog is active, or if an animation + // is playing, or if an active scene prevents it + return g_globals->_gfxManagers.size() == 1 && R2_GLOBALS._animationCtr == 0 && + (!R2_GLOBALS._sceneManager._scene || + !((SceneExt *)R2_GLOBALS._sceneManager._scene)->_preventSaving); } /*--------------------------------------------------------------------------*/ @@ -338,6 +340,7 @@ SceneExt::SceneExt(): Scene() { _savedPlayerEnabled = false; _savedUiEnabled = false; _savedCanWalk = false; + _preventSaving = false; // WORKAROUND: In the original, playing animations don't reset the global _animationCtr // counter as scene changes unless the playing animation explicitly finishes. For now, @@ -593,6 +596,9 @@ void SceneExt::loadBlankScene() { void SceneHandlerExt::postInit(SceneObjectList *OwnerList) { SceneHandler::postInit(OwnerList); + + if (!R2_GLOBALS._playStream.setFile("SND4K.RES")) + warning("Could not find SND4K.RES voice file"); } void SceneHandlerExt::process(Event &event) { diff --git a/engines/tsage/ringworld2/ringworld2_logic.h b/engines/tsage/ringworld2/ringworld2_logic.h index aeac2fdd6a..5c8af8d884 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.h +++ b/engines/tsage/ringworld2/ringworld2_logic.h @@ -85,6 +85,7 @@ public: bool _savedPlayerEnabled; bool _savedUiEnabled; bool _savedCanWalk; + bool _preventSaving; Visage _cursorVisage; SynchronizedList<EventHandler *> _sceneAreas; diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp index bdac160163..5e4b4e4191 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp @@ -836,7 +836,7 @@ void Scene125::process(Event &event) { void Scene125::dispatch() { if (_soundCount) - R2_GLOBALS._playStream.proc1(); + R2_GLOBALS._playStream.dispatch(); Scene::dispatch(); } diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.cpp b/engines/tsage/ringworld2/ringworld2_scenes1.cpp index c99550978e..0932c70f04 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes1.cpp @@ -1097,7 +1097,6 @@ void Scene1100::signal() { // Really nothing break; case 13: - _leftImpacts.setup(1113, 2, 1); _trooper.postInit(); R2_GLOBALS._scrollFollower = &_trooper; @@ -1276,6 +1275,15 @@ void Scene1100::signal() { } void Scene1100::dispatch() { + // WORKAROUND: This fixes a problem with an overhang that gets blasted re-appearing + if (_animation._frame > 5 && _sceneMode == 13) { + _animation._endFrame = 9; + if (_animation._frame == 9) + // Use one of the scene's background scene objects to copy the scene to the background. + // This fixes the problem with the cliff overhang still appearing during the cutscene + _rightLandslide.copySceneToBackground(); + } + if ((g_globals->_sceneObjects->contains(&_laserShot)) && (_laserShot._visage == 1102) && (_laserShot._strip == 4) && (_laserShot._frame == 1) && (_laserShot._flags & OBJFLAG_HIDING)) { if (_paletteRefreshStatus == 1) { _paletteRefreshStatus = 2; diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.h b/engines/tsage/ringworld2/ringworld2_scenes1.h index 956328aea9..c0088236b4 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.h +++ b/engines/tsage/ringworld2/ringworld2_scenes1.h @@ -111,7 +111,7 @@ public: SceneActor _shotImpact4; SceneActor _shotImpact5; SceneActor _laserShot; - SceneActor _animation; + SceneActor _animation; // Used for cliff collapse and ship theft SceneActor _leftImpacts; SceneActor _runningGuy1; SceneActor _runningGuy2; diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index b9567cece2..02abc58178 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -22,6 +22,8 @@ #include "audio/decoders/raw.h" #include "common/config-manager.h" +#include "audio/decoders/raw.h" +#include "audio/audiostream.h" #include "tsage/core.h" #include "tsage/globals.h" #include "tsage/debugger.h" @@ -2505,6 +2507,168 @@ void ASoundExt::changeSound(int soundNum) { /*--------------------------------------------------------------------------*/ +void PlayStream::ResFileData::load(Common::SeekableReadStream &stream) { + // Validate that it's the correct data file + char header[4]; + stream.read(&header[0], 4); + if (strncmp(header, "SPAM", 4)) + error("Invalid voice resource data"); + + _fileChunkSize = stream.readUint32LE(); + stream.skip(2); + _indexSize = stream.readUint16LE(); + _chunkSize = stream.readUint16LE(); + + stream.skip(18); +} + +PlayStream::PlayStream(): EventHandler() { + _index = NULL; + _endAction = NULL; +} + +PlayStream::~PlayStream() { + remove(); +} + +bool PlayStream::setFile(const Common::String &filename) { + remove(); + + // Open the resource file for access + if (!_file.open(filename)) + return false; + + // Load header + _resData.load(_file); + + // Load the index + _index = new uint16[_resData._indexSize / 2]; + for (uint i = 0; i < (_resData._indexSize / 2); ++i) + _index[i] = _file.readUint16LE(); + + return true; +} + +bool PlayStream::play(int voiceNum, EventHandler *endAction) { + uint32 offset = getFileOffset(_index, _resData._fileChunkSize, voiceNum); + if (offset) { + _voiceNum = 0; + if (_sound.isPlaying()) + _sound.stop(); + + // Move to the offset for the start of the voice + _file.seek(offset); + + // Read in the header and validate it + char header[4]; + _file.read(&header[0], 4); + if (strncmp(header, "FEED", 4)) + error("Invalid stream data"); + + // Get basic details of first sound chunk + uint chunkSize = _file.readUint16LE() - 16; + _file.skip(4); + int rate = _file.readUint16LE(); + _file.skip(4); + + // Create the stream + Audio::QueuingAudioStream *audioStream = Audio::makeQueuingAudioStream(rate, false); + + // Load in the first chunk + byte *data = (byte *)malloc(chunkSize); + _file.read(data, chunkSize); + audioStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); + + // If necessary, load further chunks of the voice in + while (chunkSize == (_resData._chunkSize - 16)) { + // Ensure the next chunk has the 'MORE' header + _file.read(&header[0], 4); + if (strncmp(header, "MORE", 4)) + error("Invalid stream data"); + + // Get the size of the chunk + chunkSize = _file.readUint16LE() - 16; + _file.skip(10); + + // Read in the data for this next chunk and queue it + data = (byte *)malloc(chunkSize); + _file.read(data, chunkSize); + audioStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); + } + + g_vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, + audioStream, DisposeAfterUse::YES); + + return true; + } + + // If it reaches this point, no valid voice data found + return false; +} + +void PlayStream::stop() { + g_vm->_mixer->stopHandle(_soundHandle); + + _voiceNum = 0; + _endAction = NULL; +} + +bool PlayStream::isPlaying() const { + return _voiceNum != 0 && g_vm->_mixer->isSoundHandleActive(_soundHandle); +} + +void PlayStream::remove() { + stop(); + _file.close(); + + // Free index + delete[] _index; + _index = NULL; + + _endAction = NULL; + _voiceNum = 0; +} + +void PlayStream::dispatch() { + if (_voiceNum != 0 && !isPlaying()) { + // If voice has finished playing, reset fields + EventHandler *endAction = _endAction; + _endAction = NULL; + _voiceNum = 0; + + if (endAction) + // Signal given end action + endAction->signal(); + } +} + +uint32 PlayStream::getFileOffset(const uint16 *data, int count, int voiceNum) { + assert(data); + int bitsIndex = voiceNum & 7; + int byteIndex = voiceNum >> 3; + int shiftAmount = bitsIndex * 2; + int bitMask = 3 << shiftAmount; + int v = (data[byteIndex] & bitMask) >> shiftAmount; + uint32 offset = 0; + + if (!v) + return 0; + + // Loop to figure out offsets from index words skipped over + for (int i = 0; i < (voiceNum >> 3); ++i) { + for (int bit = 0; bit < 16; bit += 2) + offset += ((data[i] >> bit) & 3) * count; + } + + // Bit index loop + for (int i = 0; i < bitsIndex; ++i) + offset += ((data[byteIndex] >> (i * 2)) & 3) * count; + + return offset; +} + +/*--------------------------------------------------------------------------*/ + SoundDriver::SoundDriver() { _driverResID = 0; _minVersion = _maxVersion = 0; diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h index 2f59afb49b..83cd4753d5 100644 --- a/engines/tsage/sound.h +++ b/engines/tsage/sound.h @@ -259,6 +259,7 @@ public: class Sound: public EventHandler { private: void _prime(int soundResID, bool dontQueue); + void _primeBuffer(const byte *soundData); void _unPrime(); public: bool _stoppedAsynchronously; @@ -414,15 +415,36 @@ public: virtual void signal(); }; -class PlayStream { -public: +class PlayStream: public EventHandler { + class ResFileData { + public: + int _fileChunkSize; + uint _indexSize; + uint _chunkSize; + + void load(Common::SeekableReadStream &stream); + }; +private: + Common::File _file; + ResFileData _resData; + Audio::SoundHandle _soundHandle; Sound _sound; + uint16 *_index; + EventHandler *_endAction; + int _voiceNum; - void setFile(const Common::String &filename) {} - bool play(int soundNum, EventHandler *endAction) { return false; } - void stop() {} - void proc1() {} - bool isPlaying() const { return false; } + static uint32 getFileOffset(const uint16 *data, int count, int voiceNum); +public: + PlayStream(); + virtual ~PlayStream(); + + bool setFile(const Common::String &filename); + bool play(int voiceNum, EventHandler *endAction); + void stop(); + bool isPlaying() const; + + virtual void remove(); + virtual void dispatch(); }; #define ADLIB_CHANNEL_COUNT 9 |