[Scummvm-git-logs] scummvm master -> 3598e4b50c61c053f7c0d0bf1bacc852e51a7488

elasota noreply at scummvm.org
Thu Mar 9 07:10:47 UTC 2023


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
889bc12ae8 VCRUISE: Move completion checks to script termination.
ca75ba28dc VCRUISE: Fix missing return to idle state after operating a gyro.
3598e4b50c VCRUISE: Add sound playlists.


Commit: 889bc12ae8cb447a8315dccd152d4677103719d5
    https://github.com/scummvm/scummvm/commit/889bc12ae8cb447a8315dccd152d4677103719d5
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-09T02:10:00-05:00

Commit Message:
VCRUISE: Move completion checks to script termination.

Changed paths:
    engines/vcruise/runtime.cpp
    engines/vcruise/runtime.h


diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 2e38839469e..0bca982a4b5 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -142,8 +142,8 @@ void Runtime::GyroState::reset() {
 
 Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID)
 	: _system(system), _mixer(mixer), _roomNumber(1), _screenNumber(0), _direction(0), _havePanAnimations(0), _loadedRoomNumber(0), _activeScreenNumber(0),
-	  _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _havePendingReturnToIdleState(false), _scriptNextInstruction(0),
-	  _escOn(false), _debugMode(false), _panoramaDirectionFlags(0),
+	  _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
+	  _scriptNextInstruction(0), _escOn(false), _debugMode(false), _panoramaDirectionFlags(0),
 	  _loadedAnimation(0), _animPendingDecodeFrame(0), _animDisplayingFrame(0), _animFirstFrame(0), _animLastFrame(0), _animStopFrame(0),
 	  _animFrameRateLock(0), _animStartTime(0), _animFramesDecoded(0), _animDecoderState(kAnimDecoderStateStopped),
 	  _animPlayWhileIdle(false), _idleIsOnInteraction(false), _idleHaveClickInteraction(false), _idleHaveDragInteraction(false), _idleInteractionID(0),
@@ -511,6 +511,7 @@ bool Runtime::runGyroIdle() {
 		gyro.logState();
 		gyro.currentState--;
 		_gameState = kGameStateGyroAnimation;
+		_havePendingCompletionCheck = true;
 		return true;
 	} else if (targetState > gyro.currentState) {
 		AnimationDef animDef = _gyros.posAnim;
@@ -523,6 +524,7 @@ bool Runtime::runGyroIdle() {
 		gyro.logState();
 		gyro.currentState++;
 		_gameState = kGameStateGyroAnimation;
+		_havePendingCompletionCheck = true;
 		return true;
 	}
 
@@ -552,56 +554,11 @@ bool Runtime::runGyroAnimation() {
 }
 
 void Runtime::exitGyroIdle() {
-	bool succeeded = true;
-	for (uint i = 0; i < GyroState::kNumGyros; i++) {
-		const Gyro &gyro = _gyros.gyros[i];
-		if (gyro.requireState && gyro.currentState != gyro.requiredState) {
-			succeeded = false;
-			break;
-		}
-
-		if (gyro.numPreviousStates != gyro.numPreviousStatesRequired) {
-			succeeded = false;
-			break;
-		}
-
-		bool prevStatesMatch = true;
-		for (uint j = 0; j < gyro.numPreviousStates; j++) {
-			if (gyro.previousStates[j] != gyro.requiredPreviousStates[j]) {
-				prevStatesMatch = false;
-				break;
-			}
-		}
-
-		if (!prevStatesMatch) {
-			succeeded = false;
-			break;
-		}
-	}
-
-	// Activate the corresponding failure or success interaction if present
-	if (_scriptSet) {
-		RoomScriptSetMap_t::const_iterator roomScriptIt = _scriptSet->roomScripts.find(_roomNumber);
-		if (roomScriptIt != _scriptSet->roomScripts.end()) {
-			const ScreenScriptSetMap_t &screenScriptsMap = roomScriptIt->_value->screenScripts;
-			ScreenScriptSetMap_t::const_iterator screenScriptIt = screenScriptsMap.find(_screenNumber);
-			if (screenScriptIt != screenScriptsMap.end()) {
-				const ScreenScriptSet &screenScriptSet = *screenScriptIt->_value;
-
-				ScriptMap_t::const_iterator interactionScriptIt = screenScriptSet.interactionScripts.find(succeeded ? _gyros.completeInteraction : _gyros.failureInteraction);
-				if (interactionScriptIt != screenScriptSet.interactionScripts.end()) {
-					const Common::SharedPtr<Script> &script = interactionScriptIt->_value;
-					if (script) {
-						activateScript(script, ScriptEnvironmentVars());
-						return;
-					}
-				}
-			}
-		}
-	}
+	_gameState = kGameStateScript;
 
-	_havePendingReturnToIdleState = true;
-	_gameState = kGameStateIdle;
+	// In Reah, gyro interactions stop the script.
+	if (_gameID == GID_REAH)
+		terminateScript();
 }
 
 void Runtime::continuePlayingAnimation(bool loop, bool useStopFrame, bool &outAnimationEnded) {
@@ -852,10 +809,69 @@ void Runtime::terminateScript() {
 	if (_gameState == kGameStateScript)
 		_gameState = kGameStateIdle;
 
+	if (_havePendingCompletionCheck) {
+		_havePendingCompletionCheck = false;
+
+		if (checkCompletionConditions())
+			return;
+	}
+
 	if (_havePendingScreenChange)
 		changeToScreen(_roomNumber, _screenNumber);
 }
 
+bool Runtime::checkCompletionConditions() {
+	bool succeeded = true;
+	for (uint i = 0; i < GyroState::kNumGyros; i++) {
+		const Gyro &gyro = _gyros.gyros[i];
+		if (gyro.requireState && gyro.currentState != gyro.requiredState) {
+			succeeded = false;
+			break;
+		}
+
+		if (gyro.numPreviousStates != gyro.numPreviousStatesRequired) {
+			succeeded = false;
+			break;
+		}
+
+		bool prevStatesMatch = true;
+		for (uint j = 0; j < gyro.numPreviousStates; j++) {
+			if (gyro.previousStates[j] != gyro.requiredPreviousStates[j]) {
+				prevStatesMatch = false;
+				break;
+			}
+		}
+
+		if (!prevStatesMatch) {
+			succeeded = false;
+			break;
+		}
+	}
+
+	// Activate the corresponding failure or success interaction if present
+	if (_scriptSet) {
+		RoomScriptSetMap_t::const_iterator roomScriptIt = _scriptSet->roomScripts.find(_roomNumber);
+		if (roomScriptIt != _scriptSet->roomScripts.end()) {
+			const ScreenScriptSetMap_t &screenScriptsMap = roomScriptIt->_value->screenScripts;
+			ScreenScriptSetMap_t::const_iterator screenScriptIt = screenScriptsMap.find(_screenNumber);
+			if (screenScriptIt != screenScriptsMap.end()) {
+				const ScreenScriptSet &screenScriptSet = *screenScriptIt->_value;
+
+				ScriptMap_t::const_iterator interactionScriptIt = screenScriptSet.interactionScripts.find(succeeded ? _gyros.completeInteraction : _gyros.failureInteraction);
+				if (interactionScriptIt != screenScriptSet.interactionScripts.end()) {
+					const Common::SharedPtr<Script> &script = interactionScriptIt->_value;
+					if (script) {
+						activateScript(script, ScriptEnvironmentVars());
+						return true;
+					}
+				}
+			}
+		}
+	}
+
+	return false;
+}
+
 void Runtime::startTerminatingHorizontalPan(bool isRight) {
 	// Figure out what slice this is.  The last frame is 1 less than usual.
 	uint slice = (_animDisplayingFrame - _animFirstFrame) * kNumDirections / (_animLastFrame - _animFirstFrame + 1);
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 3d594cc6bae..7385b84406a 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -285,6 +285,7 @@ private:
 	void drawSectionToScreen(const RenderSection &section, const Common::Rect &rect);
 	void commitSectionToScreen(const RenderSection &section, const Common::Rect &rect);
 	void terminateScript();
+	bool checkCompletionConditions();
 
 	void startTerminatingHorizontalPan(bool isRight);
 
@@ -470,6 +471,7 @@ private:
 	uint _activeScreenNumber;
 	bool _havePendingScreenChange;
 	bool _havePendingReturnToIdleState;
+	bool _havePendingCompletionCheck;
 	GameState _gameState;
 
 	bool _escOn;


Commit: ca75ba28dc58d759ba31918ffbfab5735f0a9096
    https://github.com/scummvm/scummvm/commit/ca75ba28dc58d759ba31918ffbfab5735f0a9096
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-09T02:10:00-05:00

Commit Message:
VCRUISE: Fix missing return to idle state after operating a gyro.

Changed paths:
    engines/vcruise/runtime.cpp


diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 0bca982a4b5..6078433395d 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -555,6 +555,7 @@ bool Runtime::runGyroAnimation() {
 
 void Runtime::exitGyroIdle() {
 	_gameState = kGameStateScript;
+	_havePendingReturnToIdleState = true;
 
 	// In Reah, gyro interactions stop the script.
 	if (_gameID == GID_REAH)


Commit: 3598e4b50c61c053f7c0d0bf1bacc852e51a7488
    https://github.com/scummvm/scummvm/commit/3598e4b50c61c053f7c0d0bf1bacc852e51a7488
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-09T02:10:01-05:00

Commit Message:
VCRUISE: Add sound playlists.

Changed paths:
    engines/vcruise/audio_player.cpp
    engines/vcruise/audio_player.h
    engines/vcruise/runtime.cpp
    engines/vcruise/runtime.h
    engines/vcruise/vcruise.cpp


diff --git a/engines/vcruise/audio_player.cpp b/engines/vcruise/audio_player.cpp
index 7b648902a3f..876a1dc019e 100644
--- a/engines/vcruise/audio_player.cpp
+++ b/engines/vcruise/audio_player.cpp
@@ -23,10 +23,8 @@
 
 namespace VCruise {
 
-AudioPlayer::AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, byte volume, int8 balance)
-	: _exhausted(false), _mixer(nullptr), _baseStream(baseStream) {
-	_mixer = mixer;
-	mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+AudioPlayer::AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream)
+	: _exhausted(false), _isPlaying(false), _mixer(mixer), _baseStream(baseStream) {
 }
 
 AudioPlayer::~AudioPlayer() {
@@ -60,16 +58,22 @@ bool AudioPlayer::endOfData() const {
 	return _exhausted;
 }
 
-void AudioPlayer::sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance) {
-	mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+void AudioPlayer::play(byte volume, int8 balance) {
+	if (!_isPlaying) {
+		_isPlaying = true;
+		_exhausted = false;
+		_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+	}
+
 }
 
 void AudioPlayer::stop() {
-	if (_mixer)
+	if (_isPlaying) {
 		_mixer->stopHandle(_handle);
+		_isPlaying = false;
+	}
 
 	_exhausted = true;
-	_mixer = nullptr;
 }
 
 } // End of namespace VCruise
diff --git a/engines/vcruise/audio_player.h b/engines/vcruise/audio_player.h
index f6597e2d79c..4447bcd1eea 100644
--- a/engines/vcruise/audio_player.h
+++ b/engines/vcruise/audio_player.h
@@ -34,7 +34,7 @@ class CachedAudio;
 
 class AudioPlayer : public Audio::AudioStream {
 public:
-	AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, byte volume, int8 balance);
+	AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream);
 	~AudioPlayer();
 
 	int readBuffer(int16 *buffer, const int numSamples) override;
@@ -42,7 +42,7 @@ public:
 	int getRate() const override;
 	bool endOfData() const override;
 
-	void sendToMixer(Audio::Mixer *mixer, byte volume, int8 balance);
+	void play(byte volume, int8 balance);
 	void stop();
 
 private:
@@ -50,6 +50,7 @@ private:
 
 	Audio::SoundHandle _handle;
 	bool _isLooping;
+	bool _isPlaying;
 	bool _exhausted;
 	Audio::Mixer *_mixer;
 	Common::SharedPtr<Audio::AudioStream> _baseStream;
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 6078433395d..45baac7bcda 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -140,6 +140,147 @@ void Runtime::GyroState::reset() {
 	isWaitingForAnimation = false;
 }
 
+
+SfxPlaylistEntry::SfxPlaylistEntry() : frame(0), balance(0), volume(0) {
+}
+
+SfxPlaylist::SfxPlaylist() {
+}
+
+SfxData::SfxData() {
+}
+
+void SfxData::reset() {
+	playlists.clear();
+	sounds.clear();
+}
+
+void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
+	Common::INIFile iniFile;
+
+	iniFile.allowNonEnglishCharacters();
+	if (!iniFile.loadFromStream(stream))
+		warning("SfxData::load failed to parse INI file");
+
+	const Common::INIFile::Section *samplesSection = nullptr;
+	const Common::INIFile::Section *playlistsSection = nullptr;
+
+	Common::INIFile::SectionList sections = iniFile.getSections();	// Why does this require a copy??
+
+	for (const Common::INIFile::Section &section : sections) {
+		if (section.name == "samples")
+			samplesSection = §ion;
+		else if (section.name == "playlists")
+			playlistsSection = §ion;
+	}
+
+	if (samplesSection) {
+		for (const Common::INIFile::KeyValue &keyValue : samplesSection->keys) {
+			Common::SharedPtr<SfxSound> sample(new SfxSound());
+
+			// Fix up the path delimiter
+			Common::String sfxPath = keyValue.value;
+			for (char &c : sfxPath) {
+				if (c == '\\')
+					c = '/';
+			}
+
+			sfxPath = Common::String("Sfx/") + sfxPath;
+
+			Common::File f;
+			if (!f.open(sfxPath))
+				warning("SfxData::load: Could not open sample file '%s'", sfxPath.c_str());
+
+			int64 size = f.size();
+			if (size <= 0 || size > 0x1fffffffu) {
+				warning("SfxData::load: File is oversized for some reason");
+				continue;
+			}
+
+			sample->soundData.resize(static_cast<uint>(size));
+			if (f.read(&sample->soundData[0], static_cast<uint32>(size)) != size) {
+				warning("SfxData::load: Couldn't read file");
+				continue;
+			}
+
+			sample->memoryStream.reset(new Common::MemoryReadStream(&sample->soundData[0], static_cast<uint32>(size)));
+			sample->audioStream.reset(Audio::makeWAVStream(sample->memoryStream.get(), DisposeAfterUse::NO));
+			sample->audioPlayer.reset(new AudioPlayer(mixer, sample->audioStream));
+
+			this->sounds[keyValue.key] = sample;
+		}
+	}
+
+	if (playlistsSection) {
+		Common::SharedPtr<SfxPlaylist> playlist;
+
+		for (const Common::INIFile::KeyValue &keyValue : playlistsSection->keys) {
+			const Common::String &key = keyValue.key;
+
+			if (key.size() == 0)
+				continue;
+
+			if (key.size() >= 2 && key.firstChar() == '\"' && key.lastChar() == '\"') {
+				if (!playlist) {
+					warning("Found playlist entry outside of a playlist");
+					continue;
+				}
+
+				Common::String workKey = key.substr(1, key.size() - 2);
+
+				Common::Array<Common::String> tokens;
+				for (;;) {
+					uint32 spaceSpanStart = workKey.find(' ');
+
+					if (spaceSpanStart == Common::String::npos) {
+						tokens.push_back(workKey);
+						break;
+					}
+
+					uint32 spaceSpanEnd = spaceSpanStart;
+
+					while (spaceSpanEnd < workKey.size() && workKey[spaceSpanEnd] == ' ')
+						spaceSpanEnd++;
+
+					tokens.push_back(workKey.substr(0, spaceSpanStart));
+					workKey = workKey.substr(spaceSpanEnd, workKey.size() - spaceSpanEnd);
+				}
+
+				if (tokens.size() != 4) {
+					warning("Found unusual playlist entry: %s", key.c_str());
+					continue;
+				}
+
+				unsigned int frameNum = 0;
+				int balance = 0;
+				unsigned int volume = 0;
+
+				if (!sscanf(tokens[0].c_str(), "%u", &frameNum) || !sscanf(tokens[2].c_str(), "%i", &balance) || !sscanf(tokens[3].c_str(), "%u", &volume)) {
+					warning("Malformed playlist entry: %s", key.c_str());
+					continue;
+				}
+
+				SoundMap_t::const_iterator soundIt = this->sounds.find(tokens[1]);
+				if (soundIt == this->sounds.end()) {
+					warning("Playlist entry referenced non-existent sound: %s", tokens[1].c_str());
+					continue;
+				}
+
+				SfxPlaylistEntry plEntry;
+				plEntry.balance = balance;
+				plEntry.frame = frameNum;
+				plEntry.volume = volume;
+				plEntry.sample = soundIt->_value;
+
+				playlist->entries.push_back(plEntry);
+			} else {
+				playlist.reset(new SfxPlaylist());
+				this->playlists[key] = playlist;
+			}
+		}
+	}
+}
+
 Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID)
 	: _system(system), _mixer(mixer), _roomNumber(1), _screenNumber(0), _direction(0), _havePanAnimations(0), _loadedRoomNumber(0), _activeScreenNumber(0),
 	  _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
@@ -638,6 +779,21 @@ void Runtime::continuePlayingAnimation(bool loop, bool useStopFrame, bool &outAn
 		_animPendingDecodeFrame++;
 		_animFramesDecoded++;
 
+		if (_animPlaylist) {
+			uint decodeFrameInPlaylist = _animDisplayingFrame - _animFirstFrame;
+			for (const SfxPlaylistEntry &playlistEntry : _animPlaylist->entries) {
+				if (playlistEntry.frame == decodeFrameInPlaylist) {
+					VCruise::AudioPlayer &audioPlayer = *playlistEntry.sample->audioPlayer;
+
+					audioPlayer.stop();
+					playlistEntry.sample->audioStream->seek(0);
+					audioPlayer.play(playlistEntry.volume, playlistEntry.frame);
+
+					// No break, it's possible for there to be multiple sounds in the same frame
+				}
+			}
+		}
+
 		Common::Rect copyRect = Common::Rect(0, 0, surface->w, surface->h);
 
 		if (!_animConstraintRect.isEmpty())
@@ -1258,7 +1414,8 @@ void Runtime::changeMusicTrack(int track) {
 		if (Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(wavFile, DisposeAfterUse::YES)) {
 			Common::SharedPtr<Audio::AudioStream> loopingStream(Audio::makeLoopingAudioStream(audioStream, 0));
 
-			_musicPlayer.reset(new AudioPlayer(_mixer, loopingStream, 255, 0));
+			_musicPlayer.reset(new AudioPlayer(_mixer, loopingStream));
+			_musicPlayer->play(255, 0);
 		}
 	} else {
 		warning("Music file '%s' is missing", wavFileName.c_str());
@@ -1273,6 +1430,8 @@ void Runtime::changeAnimation(const AnimationDef &animDef, bool consumeFPSOverri
 void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride) {
 	debug("changeAnimation: %u -> %u  Initial %u", animDef.firstFrame, animDef.lastFrame, initialFrame);
 
+	_animPlaylist.reset();
+
 	int animFile = animDef.animNum;
 	if (animFile < 0)
 		animFile = -animFile;
@@ -1295,6 +1454,14 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
 			warning("Animation file %i is missing", animFile);
 			delete aviFile;
 		}
+
+		Common::String sfxFileName = Common::String::format("Sfx/Anim%04i.sfx", animFile);
+		Common::File sfxFile;
+
+		_sfxData.reset();
+
+		if (sfxFile.open(sfxFileName))
+			_sfxData.load(sfxFile, _mixer);
 	}
 
 	if (_animDecoderState == kAnimDecoderStatePlaying) {
@@ -1311,6 +1478,11 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
 	_animConstraintRect = animDef.constraintRect;
 	_animFrameRateLock = 0;
 
+	SfxData::PlaylistMap_t::const_iterator playlistIt = _sfxData.playlists.find(animDef.animName);
+
+	if (playlistIt != _sfxData.playlists.end())
+		_animPlaylist = playlistIt->_value;
+
 	if (consumeFPSOverride) {
 		_animFrameRateLock = _scriptEnv.fpsOverride;
 		_animFramesDecoded = 0;
@@ -1332,14 +1504,12 @@ AnimationDef Runtime::stackArgsToAnimDef(const StackValue_t *args) const {
 	def.constraintRect.right = args[5];
 	def.constraintRect.bottom = args[6];
 
+	def.animName = _animDefNames[args[7]];
+
 	return def;
 }
 
 void Runtime::pushAnimDef(const AnimationDef &animDef) {
-	// Going from Schizm's scripts it looks like this IS pushed on to the stack, but encoded as:
-	// Bits 0..11:  Last frame
-	// Bits 12..23: First frame
-	// Bits 24..31: Number
 	_scriptStack.push_back(animDef.animNum);
 	_scriptStack.push_back(animDef.firstFrame);
 	_scriptStack.push_back(animDef.lastFrame);
@@ -1348,6 +1518,17 @@ void Runtime::pushAnimDef(const AnimationDef &animDef) {
 	_scriptStack.push_back(animDef.constraintRect.top);
 	_scriptStack.push_back(animDef.constraintRect.right);
 	_scriptStack.push_back(animDef.constraintRect.bottom);
+
+	uint animNameIndex = 0;
+	Common::HashMap<Common::String, uint>::const_iterator nameIt = _animDefNameToIndex.find(animDef.animName);
+	if (nameIt == _animDefNameToIndex.end()) {
+		animNameIndex = _animDefNames.size();
+		_animDefNameToIndex[animDef.animName] = animNameIndex;
+		_animDefNames.push_back(animDef.animName);
+	} else
+		animNameIndex = nameIt->_value;
+
+	_scriptStack.push_back(animNameIndex);
 }
 
 void Runtime::activateScript(const Common::SharedPtr<Script> &script, const ScriptEnvironmentVars &envVars) {
@@ -1382,6 +1563,7 @@ bool Runtime::parseIndexDef(IndexParseType parseType, uint roomNumber, const Com
 		animDef.animNum = animNum;
 		animDef.firstFrame = firstFrame;
 		animDef.lastFrame = lastFrame;
+		animDef.animName = key;
 	} break;
 	case kIndexParseTypeRRoom: {
 		Common::String name;
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 7385b84406a..36e11dbb67a 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -31,12 +31,19 @@ class OSystem;
 
 namespace Common {
 
+class MemoryReadStream;
 class RandomSource;
 class ReadStream;
 class WriteStream;
 
 } // End of namespace Commom
 
+namespace Audio {
+
+class SeekableAudioStream;
+
+} // End of namespace Audio
+
 namespace Graphics {
 
 struct PixelFormat;
@@ -83,6 +90,8 @@ struct AnimationDef {
 	uint lastFrame;	// Inclusive
 
 	Common::Rect constraintRect;
+
+	Common::String animName;
 };
 
 struct RoomDef {
@@ -126,6 +135,40 @@ struct ScriptEnvironmentVars {
 	uint fpsOverride;
 };
 
+struct SfxSound {
+	Common::Array<byte> soundData;
+	Common::SharedPtr<Common::MemoryReadStream> memoryStream;
+	Common::SharedPtr<Audio::SeekableAudioStream> audioStream;
+	Common::SharedPtr<AudioPlayer> audioPlayer;
+};
+
+struct SfxPlaylistEntry {
+	SfxPlaylistEntry();
+
+	uint frame;
+	Common::SharedPtr<SfxSound> sample;
+	int8 balance;
+	uint8 volume;
+};
+
+struct SfxPlaylist {
+	SfxPlaylist();
+
+	Common::Array<SfxPlaylistEntry> entries;
+};
+
+struct SfxData {
+	SfxData();
+
+	void reset();
+	void load(Common::SeekableReadStream &stream, Audio::Mixer *mixer);
+
+	typedef Common::HashMap<Common::String, Common::SharedPtr<SfxPlaylist> > PlaylistMap_t;
+	typedef Common::HashMap<Common::String, Common::SharedPtr<SfxSound> > SoundMap_t;
+	PlaylistMap_t playlists;
+	SoundMap_t sounds;
+};
+
 class Runtime {
 public:
 	Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID);
@@ -490,8 +533,10 @@ private:
 	Common::SharedPtr<Common::RandomSource> _rng;
 
 	Common::SharedPtr<AudioPlayer> _musicPlayer;
+	SfxData _sfxData;
 
 	Common::SharedPtr<Video::AVIDecoder> _animDecoder;
+	Common::SharedPtr<SfxPlaylist> _animPlaylist;
 	AnimDecoderState _animDecoderState;
 	uint _animPendingDecodeFrame;
 	uint _animDisplayingFrame;
@@ -505,6 +550,9 @@ private:
 	uint _loadedAnimation;
 	bool _animPlayWhileIdle;
 
+	Common::Array<Common::String> _animDefNames;
+	Common::HashMap<Common::String, uint> _animDefNameToIndex;
+
 	bool _idleIsOnInteraction;
 	bool _idleHaveClickInteraction;
 	bool _idleHaveDragInteraction;
@@ -535,7 +583,7 @@ private:
 
 	Common::Array<OSEvent> _pendingEvents;
 
-	static const uint kAnimDefStackArgs = 7;
+	static const uint kAnimDefStackArgs = 8;
 
 	static const uint kCursorArrow = 0;
 
diff --git a/engines/vcruise/vcruise.cpp b/engines/vcruise/vcruise.cpp
index b2bfb51d325..a1154b42187 100644
--- a/engines/vcruise/vcruise.cpp
+++ b/engines/vcruise/vcruise.cpp
@@ -38,7 +38,7 @@ namespace VCruise {
 VCruiseEngine::VCruiseEngine(OSystem *syst, const VCruiseGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 
-	SearchMan.addDirectory(gameDataDir.getPath(), gameDataDir, 0, 2);
+	SearchMan.addDirectory(gameDataDir.getPath(), gameDataDir, 0, 3);
 }
 
 VCruiseEngine::~VCruiseEngine() {




More information about the Scummvm-git-logs mailing list