aboutsummaryrefslogtreecommitdiff
path: root/engines/tsage
diff options
context:
space:
mode:
Diffstat (limited to 'engines/tsage')
-rw-r--r--engines/tsage/core.cpp37
-rw-r--r--engines/tsage/ringworld2/ringworld2_logic.cpp12
-rw-r--r--engines/tsage/ringworld2/ringworld2_logic.h1
-rw-r--r--engines/tsage/ringworld2/ringworld2_scenes0.cpp2
-rw-r--r--engines/tsage/ringworld2/ringworld2_scenes1.cpp10
-rw-r--r--engines/tsage/ringworld2/ringworld2_scenes1.h2
-rw-r--r--engines/tsage/sound.cpp164
-rw-r--r--engines/tsage/sound.h36
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