mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-05 02:17:05 +00:00
04f971370c
- Add some const modifiers where applicable - Add some missing casts svn-id: r55541
1897 lines
44 KiB
C++
1897 lines
44 KiB
C++
/* 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "lastexpress/game/sound.h"
|
|
|
|
#include "lastexpress/game/action.h"
|
|
#include "lastexpress/game/entities.h"
|
|
#include "lastexpress/game/inventory.h"
|
|
#include "lastexpress/game/logic.h"
|
|
#include "lastexpress/game/savepoint.h"
|
|
#include "lastexpress/game/state.h"
|
|
|
|
#include "lastexpress/helpers.h"
|
|
#include "lastexpress/graphics.h"
|
|
#include "lastexpress/lastexpress.h"
|
|
#include "lastexpress/resource.h"
|
|
|
|
namespace LastExpress {
|
|
|
|
// Letters & messages
|
|
const char *messages[24] = {
|
|
"",
|
|
"TXT1001", // 1
|
|
"TXT1001A", // 2
|
|
"TXT1011", // 3
|
|
"TXT1012", // 4
|
|
"TXT1013", // 5
|
|
"TXT1014", // 6
|
|
"TXT1020", // 7
|
|
"TXT1030", // 8
|
|
"END1009B", // 50
|
|
"END1046", // 51
|
|
"END1047", // 52
|
|
"END1112", // 53
|
|
"END1112A", // 54
|
|
"END1503", // 55
|
|
"END1505A", // 56
|
|
"END1505B", // 57
|
|
"END1610", // 58
|
|
"END1612A", // 59
|
|
"END1612C", // 61
|
|
"END1612D", // 62
|
|
"ENDALRM1", // 63
|
|
"ENDALRM2", // 64
|
|
"ENDALRM3" // 65
|
|
};
|
|
|
|
const char *cities[17] = {
|
|
"EPERNAY",
|
|
"CHALONS",
|
|
"BARLEDUC",
|
|
"NANCY",
|
|
"LUNEVILL",
|
|
"AVRICOUR",
|
|
"DEUTSCHA",
|
|
"STRASBOU",
|
|
"BADENOOS",
|
|
"SALZBURG",
|
|
"ATTNANG",
|
|
"WELS",
|
|
"LINZ",
|
|
"VIENNA",
|
|
"POZSONY",
|
|
"GALANTA",
|
|
"POLICE"
|
|
};
|
|
|
|
const char *locomotiveSounds[5] = {
|
|
"ZFX1005",
|
|
"ZFX1006",
|
|
"ZFX1007",
|
|
"ZFX1007A",
|
|
"ZFX1007B"
|
|
};
|
|
|
|
static const SoundManager::FlagType soundFlags[32] = {
|
|
SoundManager::kFlagDefault, SoundManager::kFlag15, SoundManager::kFlag14, SoundManager::kFlag13, SoundManager::kFlag12,
|
|
SoundManager::kFlag11, SoundManager::kFlag11, SoundManager::kFlag10, SoundManager::kFlag10, SoundManager::kFlag9, SoundManager::kFlag9, SoundManager::kFlag8, SoundManager::kFlag8,
|
|
SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag6, SoundManager::kFlag6, SoundManager::kFlag6,
|
|
SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4,
|
|
SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3
|
|
};
|
|
|
|
SoundManager::SoundManager(LastExpressEngine *engine) : _engine(engine), _state(0), _currentType(kSoundType16), _flag(0) {
|
|
// Initialize unknown data
|
|
_data0 = 0;
|
|
_data1 = 0;
|
|
_data2 = 0;
|
|
|
|
memset(&_buffer, 0, sizeof(_buffer));
|
|
memset(&_lastWarning, 0, sizeof(_lastWarning));
|
|
|
|
_drawSubtitles = 0;
|
|
_currentSubtitle = NULL;
|
|
}
|
|
|
|
SoundManager::~SoundManager() {
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
|
|
SAFE_DELETE(*i);
|
|
_cache.clear();
|
|
|
|
for (Common::List<SubtitleEntry *>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i)
|
|
SAFE_DELETE(*i);
|
|
_subtitles.clear();
|
|
|
|
_currentSubtitle = NULL;
|
|
|
|
// Zero passed pointers
|
|
_engine = NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Timer
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::handleTimer() {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
SoundEntry *entry = (*i);
|
|
if (entry->stream == NULL) {
|
|
SAFE_DELETE(*i);
|
|
i = _cache.reverse_erase(i);
|
|
continue;
|
|
} else if (!entry->soundStream) {
|
|
entry->soundStream = new StreamedSound();
|
|
|
|
// TODO: stream any sound in the queue after filtering
|
|
entry->soundStream->load(entry->stream);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Sound queue management
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::updateQueue() {
|
|
// TODO add mutex lock!
|
|
//warning("Sound::unknownFunction1: not implemented!");
|
|
}
|
|
|
|
void SoundManager::resetQueue(SoundType type1, SoundType type2) {
|
|
if (!type2)
|
|
type2 = type1;
|
|
|
|
Common::StackLock locker(_mutex);
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
if ((*i)->type != type1 && (*i)->type != type2)
|
|
resetEntry(*i);
|
|
}
|
|
}
|
|
|
|
void SoundManager::removeFromQueue(EntityIndex entity) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(entity);
|
|
if (entry)
|
|
resetEntry(entry);
|
|
}
|
|
|
|
void SoundManager::removeFromQueue(Common::String filename) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(filename);
|
|
if (entry)
|
|
resetEntry(entry);
|
|
}
|
|
|
|
void SoundManager::clearQueue() {
|
|
_flag |= 4;
|
|
|
|
// FIXME: Wait a while for a flag to be set
|
|
//for (int i = 0; i < 3000000; i++)
|
|
// if (_flag & 8)
|
|
// break;
|
|
|
|
_flag |= 8;
|
|
|
|
Common::StackLock locker(_mutex);
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
SoundEntry *entry = (*i);
|
|
|
|
// Delete entry
|
|
removeEntry(entry);
|
|
SAFE_DELETE(entry);
|
|
|
|
i = _cache.reverse_erase(i);
|
|
}
|
|
|
|
updateSubtitles();
|
|
}
|
|
|
|
bool SoundManager::isBuffered(EntityIndex entity) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
return (getEntry(entity) != NULL);
|
|
}
|
|
|
|
bool SoundManager::isBuffered(Common::String filename, bool testForEntity) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(filename);
|
|
|
|
if (testForEntity)
|
|
return entry != NULL && !entry->entity;
|
|
|
|
return (entry != NULL);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Entry
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4) {
|
|
if (!entry)
|
|
error("SoundManager::setupEntry: Invalid entry!");
|
|
|
|
entry->field_4C = a4;
|
|
setEntryType(entry, flag);
|
|
setEntryStatus(entry, flag);
|
|
|
|
// Add entry to cache
|
|
_cache.push_back(entry);
|
|
|
|
setupCache(entry);
|
|
loadSoundData(entry, name);
|
|
}
|
|
|
|
void SoundManager::setEntryType(SoundEntry *entry, FlagType flag) {
|
|
switch (flag & kFlagType9) {
|
|
default:
|
|
case kFlagNone:
|
|
entry->type = _currentType;
|
|
_currentType = (SoundType)(_currentType + 1);
|
|
break;
|
|
|
|
case kFlagType1_2: {
|
|
SoundEntry *previous2 = getEntry(kSoundType2);
|
|
if (previous2)
|
|
updateEntry(previous2, 0);
|
|
|
|
SoundEntry *previous = getEntry(kSoundType1);
|
|
if (previous) {
|
|
previous->type = kSoundType2;
|
|
updateEntry(previous, 0);
|
|
}
|
|
|
|
entry->type = kSoundType1;
|
|
}
|
|
break;
|
|
|
|
case kFlagType3: {
|
|
SoundEntry *previous = getEntry(kSoundType3);
|
|
if (previous) {
|
|
previous->type = kSoundType4;
|
|
updateEntry(previous, 0);
|
|
}
|
|
|
|
entry->type = kSoundType11;
|
|
}
|
|
break;
|
|
|
|
case kFlagType7: {
|
|
SoundEntry *previous = getEntry(kSoundType7);
|
|
if (previous)
|
|
previous->type = kSoundType8;
|
|
|
|
entry->type = kSoundType7;
|
|
}
|
|
break;
|
|
|
|
case kFlagType9: {
|
|
SoundEntry *previous = getEntry(kSoundType9);
|
|
if (previous)
|
|
previous->type = kSoundType10;
|
|
|
|
entry->type = kSoundType9;
|
|
}
|
|
break;
|
|
|
|
case kFlagType11: {
|
|
SoundEntry *previous = getEntry(kSoundType11);
|
|
if (previous)
|
|
previous->type = kSoundType14;
|
|
|
|
entry->type = kSoundType11;
|
|
}
|
|
break;
|
|
|
|
case kFlagType13: {
|
|
SoundEntry *previous = getEntry(kSoundType13);
|
|
if (previous)
|
|
previous->type = kSoundType14;
|
|
|
|
entry->type = kSoundType13;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SoundManager::setEntryStatus(SoundEntry *entry, FlagType flag) const {
|
|
SoundStatus status = (SoundStatus)flag;
|
|
if (!((status & 0xFF) & kSoundStatusClear1))
|
|
status = (SoundStatus)(status | kSoundStatusClear2);
|
|
|
|
if (((status & 0xFF00) >> 8) & kSoundStatusClear0)
|
|
entry->status.status = (uint32)status;
|
|
else
|
|
entry->status.status = (status | kSoundStatusClear4);
|
|
}
|
|
|
|
bool SoundManager::setupCache(SoundEntry *entry) {
|
|
warning("Sound::setupCache: not implemented!");
|
|
return true;
|
|
}
|
|
|
|
void SoundManager::clearStatus() {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
|
|
(*i)->status.status |= kSoundStatusClear3;
|
|
}
|
|
|
|
void SoundManager::loadSoundData(SoundEntry *entry, Common::String name) {
|
|
entry->name2 = name;
|
|
|
|
// Load sound data
|
|
entry->stream = getArchive(name);
|
|
|
|
if (!entry->stream)
|
|
entry->stream = getArchive("DEFAULT.SND");
|
|
|
|
if (entry->stream) {
|
|
warning("Sound::loadSoundData: not implemented!");
|
|
} else {
|
|
entry->status.status = kSoundStatusRemoved;
|
|
}
|
|
}
|
|
|
|
void SoundManager::resetEntry(SoundEntry *entry) const {
|
|
entry->status.status |= kSoundStatusRemoved;
|
|
entry->entity = kEntityPlayer;
|
|
|
|
if (entry->stream) {
|
|
if (!entry->soundStream) {
|
|
SAFE_DELETE(entry->stream);
|
|
} else {
|
|
entry->soundStream->stop();
|
|
SAFE_DELETE(entry->soundStream);
|
|
}
|
|
|
|
entry->stream = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void SoundManager::removeEntry(SoundEntry *entry) {
|
|
entry->status.status |= kSoundStatusRemoved;
|
|
|
|
// Loop until ready
|
|
while (!(entry->status.status1 & 4) && !(_flag & 8) && (_flag & 1))
|
|
; // empty loop body
|
|
|
|
// The original game remove the entry from the cache here,
|
|
// but since we are called from within an iterator loop
|
|
// we will remove the entry there
|
|
// removeFromCache(entry);
|
|
|
|
if (entry->subtitle) {
|
|
drawSubtitle(entry->subtitle);
|
|
SAFE_DELETE(entry->subtitle);
|
|
}
|
|
|
|
if (entry->entity) {
|
|
if (entry->entity == kEntitySteam)
|
|
playLoopingSound();
|
|
else if (entry->entity != kEntityTrain)
|
|
getSavePoints()->push(kEntityPlayer, entry->entity, kActionEndSound);
|
|
}
|
|
}
|
|
|
|
void SoundManager::updateEntry(SoundEntry *entry, uint value) const {
|
|
if (!(entry->status.status3 & 64)) {
|
|
int value2 = value;
|
|
|
|
entry->status.status |= kSoundStatus_100000;
|
|
|
|
if (value) {
|
|
if (_flag & 32) {
|
|
entry->field_40 = value;
|
|
value2 = value * 2 + 1;
|
|
}
|
|
|
|
entry->field_3C = value2;
|
|
} else {
|
|
entry->field_3C = 0;
|
|
entry->status.status |= kSoundStatus_40000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SoundManager::updateEntryState(SoundEntry *entry) const {
|
|
if (_flag & 32) {
|
|
if (entry->type != kSoundType9 && entry->type != kSoundType7 && entry->type != kSoundType5) {
|
|
uint32 status = entry->status.status & kSoundStatusClear1;
|
|
|
|
entry->status.status &= kSoundStatusClearAll;
|
|
|
|
entry->field_40 = status;
|
|
entry->status.status |= status * 2 + 1;
|
|
}
|
|
}
|
|
|
|
entry->status.status |= kSoundStatus_20;
|
|
}
|
|
|
|
void SoundManager::processEntry(EntityIndex entity) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(entity);
|
|
if (entry) {
|
|
updateEntry(entry, 0);
|
|
entry->entity = kEntityPlayer;
|
|
}
|
|
}
|
|
|
|
void SoundManager::processEntry(SoundType type) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(type);
|
|
if (entry)
|
|
updateEntry(entry, 0);
|
|
}
|
|
|
|
void SoundManager::setupEntry(SoundType type, EntityIndex index) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(type);
|
|
if (entry)
|
|
entry->entity = index;
|
|
}
|
|
|
|
void SoundManager::processEntry(Common::String filename) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(filename);
|
|
if (entry) {
|
|
updateEntry(entry, 0);
|
|
entry->entity = kEntityPlayer;
|
|
}
|
|
}
|
|
|
|
void SoundManager::processEntries() {
|
|
_state = 0;
|
|
|
|
processEntry(kSoundType1);
|
|
processEntry(kSoundType2);
|
|
}
|
|
|
|
uint32 SoundManager::getEntryTime(EntityIndex index) {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
SoundEntry *entry = getEntry(index);
|
|
if (entry)
|
|
return entry->time;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Misc
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void SoundManager::unknownFunction4() {
|
|
// TODO: Add mutex ?
|
|
warning("Sound::unknownFunction4: not implemented!");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Entry search
|
|
//////////////////////////////////////////////////////////////////////////
|
|
SoundManager::SoundEntry *SoundManager::getEntry(EntityIndex index) {
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
if ((*i)->entity == index)
|
|
return *i;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
SoundManager::SoundEntry *SoundManager::getEntry(Common::String name) {
|
|
if (!name.contains('.'))
|
|
name += ".SND";
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
if ((*i)->name2 == name)
|
|
return *i;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
SoundManager::SoundEntry *SoundManager::getEntry(SoundType type) {
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
if ((*i)->type == type)
|
|
return *i;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Savegame
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::saveLoadWithSerializer(Common::Serializer &s) {
|
|
s.syncAsUint32LE(_state);
|
|
s.syncAsUint32LE(_currentType);
|
|
|
|
// Compute the number of entries to save
|
|
uint32 numEntries = count();
|
|
s.syncAsUint32LE(numEntries);
|
|
|
|
Common::StackLock locker(_mutex);
|
|
|
|
// Save or load each entry data
|
|
if (s.isSaving()) {
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
|
|
SoundEntry *entry = *i;
|
|
if (entry->name2.matchString("NISSND?") && (entry->status.status & kFlagType7) != kFlag3) {
|
|
s.syncAsUint32LE(entry->status.status); // status;
|
|
s.syncAsUint32LE(entry->type); // type;
|
|
s.syncAsUint32LE(entry->field_1C); // field_8;
|
|
s.syncAsUint32LE(entry->time); // time;
|
|
s.syncAsUint32LE(entry->field_34); // field_10;
|
|
s.syncAsUint32LE(entry->field_38); // field_14;
|
|
s.syncAsUint32LE(entry->entity); // entity;
|
|
|
|
uint32 field_1C = (uint32)entry->field_48 - _data2;
|
|
if (field_1C > kFlag8)
|
|
field_1C = 0;
|
|
s.syncAsUint32LE(field_1C); // field_1C;
|
|
|
|
s.syncAsUint32LE(entry->field_4C); // field_20;
|
|
|
|
char name1[16];
|
|
strcpy((char *)&name1, entry->name1.c_str());
|
|
s.syncBytes((byte *)&name1, 16);
|
|
|
|
char name2[16];
|
|
strcpy((char *)&name2, entry->name2.c_str());
|
|
s.syncBytes((byte *)&name2, 16);
|
|
}
|
|
}
|
|
} else {
|
|
warning("Sound::saveLoadWithSerializer: not implemented!");
|
|
s.skip(numEntries * 64);
|
|
}
|
|
}
|
|
|
|
|
|
// FIXME: We probably need another mutex here to protect during the whole savegame process
|
|
// as we could have removed an entry between the time we check the count and the time we
|
|
// save the entries
|
|
uint32 SoundManager::count() {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
uint32 numEntries = 0;
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
|
|
if ((*i)->name2.matchString("NISSND?"))
|
|
++numEntries;
|
|
|
|
return numEntries;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Game-related functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::playSound(EntityIndex entity, Common::String filename, FlagType flag, byte a4) {
|
|
if (isBuffered(entity) && entity)
|
|
removeFromQueue(entity);
|
|
|
|
FlagType currentFlag = (flag == -1) ? getSoundFlag(entity) : (FlagType)(flag | 0x80000);
|
|
|
|
// Add .SND at the end of the filename if needed
|
|
if (!filename.contains('.'))
|
|
filename += ".SND";
|
|
|
|
if (!playSoundWithSubtitles(filename, currentFlag, entity, a4))
|
|
if (entity)
|
|
getSavePoints()->push(kEntityPlayer, entity, kActionEndSound);
|
|
}
|
|
|
|
bool SoundManager::playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4) {
|
|
SoundEntry *entry = new SoundEntry();
|
|
|
|
Common::StackLock locker(_mutex);
|
|
|
|
setupEntry(entry, filename, flag, 30);
|
|
entry->entity = entity;
|
|
|
|
if (a4) {
|
|
entry->field_48 = _data2 + 2 * a4;
|
|
entry->status.status |= kSoundStatus_8000;
|
|
} else {
|
|
// Get subtitles name
|
|
while (filename.size() > 4)
|
|
filename.deleteLastChar();
|
|
|
|
showSubtitle(entry, filename);
|
|
updateEntryState(entry);
|
|
}
|
|
|
|
return (entry->type != kSoundTypeNone);
|
|
}
|
|
|
|
void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) {
|
|
char filename[12];
|
|
int values[5];
|
|
|
|
if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
|
|
return;
|
|
|
|
if (getEntities()->isInSalon(entity) != getEntities()->isInSalon(kEntityPlayer))
|
|
return;
|
|
|
|
int _action = (int)action;
|
|
FlagType flag = getSoundFlag(entity);
|
|
|
|
switch (action) {
|
|
case 36: {
|
|
int _param3 = (flag <= 9) ? flag + 7 : 16;
|
|
|
|
if (_param3 > 7) {
|
|
_data0 = (uint)_param3;
|
|
_data1 = _data2 + 2 * a3;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 37:
|
|
_data0 = 7;
|
|
_data1 = _data2 + 2 * a3;
|
|
break;
|
|
|
|
case 150:
|
|
case 156:
|
|
case 162:
|
|
case 168:
|
|
case 188:
|
|
case 198:
|
|
_action += 1 + (int)rnd(5);
|
|
break;
|
|
|
|
case 174:
|
|
case 184:
|
|
case 194:
|
|
_action += 1 + (int)rnd(3);
|
|
break;
|
|
|
|
case 180:
|
|
_action += 1 + (int)rnd(4);
|
|
break;
|
|
|
|
case 246:
|
|
values[0] = 0;
|
|
values[1] = 104;
|
|
values[2] = 105;
|
|
values[3] = 106;
|
|
values[4] = 116;
|
|
_action = values[rnd(5)];
|
|
break;
|
|
|
|
case 247:
|
|
values[0] = 11;
|
|
values[1] = 123;
|
|
values[2] = 124;
|
|
_action = values[rnd(3)];
|
|
break;
|
|
|
|
case 248:
|
|
values[0] = 0;
|
|
values[1] = 103;
|
|
values[2] = 108;
|
|
values[3] = 109;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 249:
|
|
values[0] = 0;
|
|
values[1] = 56;
|
|
values[2] = 112;
|
|
values[3] = 113;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 250:
|
|
values[0] = 0;
|
|
values[1] = 107;
|
|
values[2] = 115;
|
|
values[3] = 117;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 251:
|
|
values[0] = 0;
|
|
values[1] = 11;
|
|
values[2] = 56;
|
|
values[3] = 113;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 252:
|
|
values[0] = 0;
|
|
values[1] = 6;
|
|
values[2] = 109;
|
|
values[3] = 121;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 254:
|
|
values[0] = 0;
|
|
values[1] = 104;
|
|
values[2] = 120;
|
|
values[3] = 121;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 255:
|
|
values[0] = 0;
|
|
values[1] = 106;
|
|
values[2] = 115;
|
|
_action = values[rnd(3)];
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (_action) {
|
|
sprintf((char *)&filename, "LIB%03d.SND", _action);
|
|
|
|
if (flag)
|
|
playSoundWithSubtitles((char*)&filename, flag, kEntityPlayer, a3);
|
|
}
|
|
}
|
|
|
|
void SoundManager::playSteam(CityIndex index) {
|
|
if (index >= ARRAYSIZE(cities))
|
|
error("SoundManager::playSteam: invalid city index (was %d, max %d)", index, ARRAYSIZE(cities));
|
|
|
|
_state |= kSoundState2;
|
|
|
|
if (!getEntry(kSoundType1))
|
|
playSoundWithSubtitles("STEAM.SND", kFlagSteam, kEntitySteam);
|
|
|
|
// Get the new sound entry and show subtitles
|
|
SoundEntry *entry = getEntry(kSoundType1);
|
|
if (entry)
|
|
showSubtitle(entry, cities[index]);
|
|
}
|
|
|
|
void SoundManager::playFightSound(byte action, byte a4) {
|
|
int _action = (int)action;
|
|
char filename[12];
|
|
int values[5];
|
|
|
|
switch (action) {
|
|
default:
|
|
break;
|
|
|
|
case 174:
|
|
case 184:
|
|
case 194:
|
|
values[0] = action + 1;
|
|
values[1] = action + 2;
|
|
values[2] = action + 3;
|
|
_action = values[rnd(3)];
|
|
break;
|
|
|
|
case 180:
|
|
values[0] = action + 1;
|
|
values[1] = action + 2;
|
|
values[2] = action + 3;
|
|
values[3] = action + 4;
|
|
_action = values[rnd(4)];
|
|
break;
|
|
|
|
case 150:
|
|
case 156:
|
|
case 162:
|
|
case 168:
|
|
case 188:
|
|
case 198:
|
|
values[0] = action + 1;
|
|
values[1] = action + 2;
|
|
values[2] = action + 3;
|
|
values[3] = action + 4;
|
|
values[4] = action + 5;
|
|
_action = values[rnd(5)];
|
|
break;
|
|
}
|
|
|
|
if (_action) {
|
|
sprintf((char *)&filename, "LIB%03d.SND", _action);
|
|
playSound(kEntityTrain, (char*)&filename, kFlagDefault, a4);
|
|
}
|
|
}
|
|
|
|
void SoundManager::playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4) {
|
|
if (isBuffered(getDialogName(entityDialog)))
|
|
removeFromQueue(getDialogName(entityDialog));
|
|
|
|
playSound(entity, getDialogName(entityDialog), flag, a4);
|
|
}
|
|
|
|
void SoundManager::playLocomotiveSound() {
|
|
playSound(kEntityPlayer, locomotiveSounds[rnd(5)], (FlagType)(rnd(15) + 2));
|
|
}
|
|
|
|
const char *SoundManager::getDialogName(EntityIndex entity) const {
|
|
switch (entity) {
|
|
case kEntityAnna:
|
|
if (getEvent(kEventAnnaDialogGoToJerusalem))
|
|
return "XANN12";
|
|
|
|
if (getEvent(kEventLocomotiveRestartTrain))
|
|
return "XANN11";
|
|
|
|
if (getEvent(kEventAnnaBaggageTies) || getEvent(kEventAnnaBaggageTies2) || getEvent(kEventAnnaBaggageTies3) || getEvent(kEventAnnaBaggageTies4))
|
|
return "XANN10";
|
|
|
|
if (getEvent(kEventAnnaTired) || getEvent(kEventAnnaTiredKiss))
|
|
return "XANN9";
|
|
|
|
if (getEvent(kEventAnnaBaggageArgument))
|
|
return "XANN8";
|
|
|
|
if (getEvent(kEventKronosVisit))
|
|
return "XANN7";
|
|
|
|
if (getEvent(kEventAbbotIntroduction))
|
|
return "XANN6A";
|
|
|
|
if (getEvent(kEventVassiliSeizure))
|
|
return "XANN6";
|
|
|
|
if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction))
|
|
return "XANN5";
|
|
|
|
if (getProgress().field_60)
|
|
return "XANN4";
|
|
|
|
if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
|
|
|| getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram))
|
|
return "XANN3";
|
|
|
|
if (getEvent(kEventDinerMindJoin))
|
|
return "XANN2";
|
|
|
|
if (getEvent(kEventGotALight) || getEvent(kEventGotALightD))
|
|
return "XANN1";
|
|
|
|
break;
|
|
|
|
case kEntityAugust:
|
|
if (getEvent(kEventAugustTalkCigar))
|
|
return "XAUG6";
|
|
|
|
if (getEvent(kEventAugustBringBriefcase))
|
|
return "XAUG5";
|
|
|
|
// Getting closer to Vienna...
|
|
if (getState()->time > kTime2200500 && !getEvent(kEventAugustMerchandise))
|
|
return "XAUG4A";
|
|
|
|
if (getEvent(kEventAugustMerchandise))
|
|
return "XAUG4";
|
|
|
|
if (getEvent(kEventDinerAugust) || getEvent(kEventDinerAugustAlexeiBackground) || getEvent(kEventMeetAugustTylerCompartment)
|
|
|| getEvent(kEventMeetAugustTylerCompartmentBed) || getEvent(kEventMeetAugustHisCompartment) || getEvent(kEventMeetAugustHisCompartmentBed))
|
|
return "XAUG3";
|
|
|
|
if (getEvent(kEventAugustPresentAnnaFirstIntroduction))
|
|
return "XAUG2";
|
|
|
|
if (getProgress().eventMertensAugustWaiting)
|
|
return "XAUG1";
|
|
|
|
break;
|
|
|
|
case kEntityTatiana:
|
|
if (getEvent(kEventTatianaTylerCompartment))
|
|
return "XTAT6";
|
|
|
|
if (getEvent(kEventTatianaCompartmentStealEgg))
|
|
return "XTAT5";
|
|
|
|
if (getEvent(kEventTatianaGivePoem))
|
|
return "XTAT3";
|
|
|
|
if (getProgress().field_64)
|
|
return "XTAT1";
|
|
|
|
break;
|
|
|
|
case kEntityVassili:
|
|
if (getEvent(kEventCathFreePassengers))
|
|
return "XVAS4";
|
|
|
|
if (getEvent(kEventVassiliCompartmentStealEgg))
|
|
return "XVAS3";
|
|
|
|
if (getEvent(kEventAbbotIntroduction))
|
|
return "XVAS2";
|
|
|
|
if (getEvent(kEventVassiliSeizure))
|
|
return "XVAS1A";
|
|
|
|
if (getProgress().field_64)
|
|
return "XVAS1";
|
|
|
|
break;
|
|
|
|
case kEntityAlexei:
|
|
if (getProgress().field_88)
|
|
return "XALX6";
|
|
|
|
if (getProgress().field_8C)
|
|
return "XALX5";
|
|
|
|
if (getProgress().field_90)
|
|
return "XALX4A";
|
|
|
|
if (getProgress().field_68)
|
|
return "XALX4";
|
|
|
|
if (getEvent(kEventAlexeiSalonPoem))
|
|
return "XALX3";
|
|
|
|
if (getEvent(kEventAlexeiSalonVassili))
|
|
return "XALX2";
|
|
|
|
if (getEvent(kEventAlexeiDiner) || getEvent(kEventAlexeiDinerOriginalJacket))
|
|
return "XALX1";
|
|
|
|
break;
|
|
|
|
case kEntityAbbot:
|
|
if (getEvent(kEventAbbotDrinkDefuse))
|
|
return "XABB4";
|
|
|
|
if (getEvent(kEventAbbotInvitationDrink) || getEvent(kEventDefuseBomb))
|
|
return "XABB3";
|
|
|
|
if (getEvent(kEventAbbotWrongCompartment) || getEvent(kEventAbbotWrongCompartmentBed))
|
|
return "XABB2";
|
|
|
|
if (getEvent(kEventAbbotIntroduction))
|
|
return "XABB1";
|
|
|
|
break;
|
|
|
|
case kEntityMilos:
|
|
if (getEvent(kEventLocomotiveMilosDay) || getEvent(kEventLocomotiveMilosNight))
|
|
return "XMIL5";
|
|
|
|
if (getEvent(kEventMilosCompartmentVisitTyler) && (getProgress().chapter == kChapter3 || getProgress().chapter == kChapter4))
|
|
return "XMIL4";
|
|
|
|
if (getEvent(kEventMilosCorridorThanks) || getProgress().chapter == kChapter5)
|
|
return "XMIL3";
|
|
|
|
if (getEvent(kEventMilosCompartmentVisitAugust))
|
|
return "XMIL2";
|
|
|
|
if (getEvent(kEventMilosTylerCompartmentDefeat))
|
|
return "XMIL1";
|
|
|
|
break;
|
|
|
|
case kEntityVesna:
|
|
if (getProgress().field_94)
|
|
return "XVES2";
|
|
|
|
if (getProgress().field_98)
|
|
return "XVES1";
|
|
|
|
break;
|
|
|
|
case kEntityKronos:
|
|
if (getEvent(kEventKronosReturnBriefcase))
|
|
return "XKRO6";
|
|
|
|
if (getEvent(kEventKronosBringEggCeiling) || getEvent(kEventKronosBringEgg))
|
|
return "XKRO5";
|
|
|
|
if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
|
|
ObjectLocation location = getInventory()->get(kItemFirebird)->location;
|
|
if (location != kObjectLocation6 && location != kObjectLocation5 && location != kObjectLocation2 && location != kObjectLocation1)
|
|
return "XKRO4A";
|
|
}
|
|
|
|
if (getEvent(kEventKronosConversationFirebird))
|
|
return "XKRO4";
|
|
|
|
if (getEvent(kEventKronosConversation)) {
|
|
if (!getEvent(kEventMilosCompartmentVisitAugust))
|
|
return "XKRO3";
|
|
else
|
|
return "XKRO2";
|
|
}
|
|
|
|
if (getProgress().eventMertensKronosInvitation)
|
|
return "XKRO1";
|
|
|
|
break;
|
|
|
|
case kEntityFrancois:
|
|
if (getProgress().field_9C)
|
|
return "XFRA3";
|
|
|
|
if (getProgress().field_A0
|
|
|| getEvent(kEventFrancoisWhistle) || getEvent(kEventFrancoisWhistleD)
|
|
|| getEvent(kEventFrancoisWhistleNight) || getEvent(kEventFrancoisWhistleNightD))
|
|
return "XFRA2";
|
|
|
|
if (getState()->time > kTimeParisEpernay) // Between Paris and Epernay
|
|
return "XFRA1";
|
|
|
|
break;
|
|
|
|
case kEntityMmeBoutarel:
|
|
if (getProgress().field_A4)
|
|
return "XMME4";
|
|
|
|
if (getProgress().field_A8)
|
|
return "XMME3";
|
|
|
|
if (getProgress().field_A0)
|
|
return "XMME2";
|
|
|
|
if (getProgress().field_AC)
|
|
return "XMME1";
|
|
|
|
break;
|
|
|
|
case kEntityBoutarel:
|
|
if (getProgress().eventMetBoutarel)
|
|
return "XMRB1";
|
|
|
|
break;
|
|
|
|
case kEntityRebecca:
|
|
if (getProgress().field_B4)
|
|
return "XREB1A";
|
|
|
|
if (getProgress().field_B8)
|
|
return "XREB1";
|
|
|
|
break;
|
|
|
|
case kEntitySophie:
|
|
if (getProgress().field_B0)
|
|
return "XSOP2";
|
|
|
|
if (getProgress().field_BC)
|
|
return "XSOP1B";
|
|
|
|
if (getProgress().field_B4)
|
|
return "XSOP1A";
|
|
|
|
if (getProgress().field_B8)
|
|
return "XSOP1";
|
|
|
|
break;
|
|
|
|
case kEntityMahmud:
|
|
if (getProgress().field_C4)
|
|
return "XMAH1";
|
|
|
|
break;
|
|
|
|
case kEntityYasmin:
|
|
if (getProgress().eventMetYasmin)
|
|
return "XHAR2";
|
|
|
|
break;
|
|
|
|
case kEntityHadija:
|
|
if (getProgress().eventMetHadija)
|
|
return "XHAR1";
|
|
|
|
break;
|
|
|
|
case kEntityAlouan:
|
|
if (getProgress().field_DC)
|
|
return "XHAR3";
|
|
|
|
break;
|
|
|
|
case kEntityGendarmes:
|
|
if (getProgress().field_E0)
|
|
return "XHAR4";
|
|
|
|
break;
|
|
|
|
case kEntityChapters:
|
|
if (getEvent(kEventCathDream) || getEvent(kEventCathWakingUp))
|
|
return "XTYL3";
|
|
|
|
return "XTYL1";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Letters & Messages
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::readText(int id){
|
|
if (!isBuffered(kEntityTables4))
|
|
return;
|
|
|
|
if (id < 0 || (id > 8 && id < 50) || id > 64)
|
|
error("Sound::readText - attempting to use invalid id. Valid values [1;8] - [50;64], was %d", id);
|
|
|
|
// Get proper message file (names are stored in sequence in the array but id is [1;8] - [50;64])
|
|
const char *text = messages[id <= 8 ? id : id - 41];
|
|
|
|
// Check if file is in cache for id [1;8]
|
|
if (id <= 8)
|
|
if (isBuffered(text))
|
|
removeFromQueue(text);
|
|
|
|
playSound(kEntityTables4, text, kFlagDefault);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Sound bites
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::playWarningCompartment(EntityIndex entity, ObjectIndex compartment) {
|
|
|
|
#define PLAY_WARNING(index, sound1, sound2, sound3, sound4, sound5, sound6) { \
|
|
if (_lastWarning[index] + 450 >= getState()->timeTicks) { \
|
|
if (rnd(2)) \
|
|
playSound(kEntityMertens, sound1, kFlagDefault); \
|
|
else \
|
|
playSound(kEntityMertens, rnd(2) ? sound2 : sound3, kFlagDefault); \
|
|
} else { \
|
|
if (rnd(2)) \
|
|
playSound(kEntityMertens, sound4, kFlagDefault); \
|
|
else \
|
|
playSound(kEntityMertens, rnd(2) ? sound5 : sound6, kFlagDefault); \
|
|
} \
|
|
_lastWarning[index] = getState()->timeTicks; \
|
|
}
|
|
|
|
if (entity != kEntityMertens && entity != kEntityCoudert)
|
|
return;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Mertens
|
|
if (entity == kEntityMertens) {
|
|
|
|
switch (compartment) {
|
|
default:
|
|
break;
|
|
|
|
case kObjectCompartment2:
|
|
PLAY_WARNING(0, "Con1502A", "Con1500B", "Con1500C", "Con1502", "Con1500", "Con1500A");
|
|
break;
|
|
|
|
case kObjectCompartment3:
|
|
PLAY_WARNING(1, "Con1501A", "Con1500B", "Con1500C", "Con1501", "Con1500", "Con1500A");
|
|
break;
|
|
|
|
case kObjectCompartment4:
|
|
PLAY_WARNING(2, "Con1503", "Con1500B", "Con1500C", "Con1503", "Con1500", "Con1500A");
|
|
break;
|
|
|
|
case kObjectCompartment5:
|
|
case kObjectCompartment6:
|
|
case kObjectCompartment7:
|
|
case kObjectCompartment8:
|
|
++_lastWarning[3];
|
|
|
|
switch (_lastWarning[3]) {
|
|
default:
|
|
break;
|
|
|
|
case 1:
|
|
getSound()->playSound(kEntityMertens, "Con1503C", kFlagDefault);
|
|
break;
|
|
|
|
case 2:
|
|
getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503E" : "Con1503A", kFlagDefault);
|
|
break;
|
|
|
|
case 3:
|
|
getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503B" : "Con1503D", kFlagDefault);
|
|
_lastWarning[3] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Coudert
|
|
switch (compartment) {
|
|
default:
|
|
break;
|
|
|
|
case kObjectCompartmentA:
|
|
if (_lastWarning[4] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1508" : "Jac1508A", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentB:
|
|
if (_lastWarning[5] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getProgress().field_40 || (getState()->time > kTimeCityLinz && getState()->time < kTime2133000))
|
|
getSound()->playSound(kEntityCoudert, "Jac1507A", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, "Jac1507", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentC:
|
|
if (_lastWarning[6] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getProgress().chapter < kChapter3)
|
|
getSound()->playSound(kEntityCoudert, "Jac1506", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1506A" : "Jac1506B", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentD:
|
|
if (_lastWarning[7] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
getSound()->playSound(kEntityCoudert, "Jac1505", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentE:
|
|
if (_lastWarning[8] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getProgress().field_40 || (getState()->time > kTime2115000 && getState()->time < kTime2133000)) {
|
|
getSound()->playSound(kEntityCoudert, "Jac1504B", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840))
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1504" : "Jac1504A", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentF:
|
|
if (_lastWarning[9] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getProgress().field_40 || (getState()->time > kTime2083500 && getState()->time < kTime2133000)) {
|
|
getSound()->playSound(kEntityCoudert, "Jac1503B", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (rnd(2) || getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070))
|
|
getSound()->playSound(kEntityCoudert, "Jac1503", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, "Jac1503A", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentG:
|
|
if (_lastWarning[10] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (rnd(2) || getEntities()->isInsideCompartment(kEntityMilos, kCarRedSleeping, kPosition_3050))
|
|
getSound()->playSound(kEntityCoudert, "Jac1502", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, "Jac1502A", kFlagDefault);
|
|
break;
|
|
|
|
case kObjectCompartmentH:
|
|
if (_lastWarning[11] + 450 >= getState()->timeTicks) {
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
if (getEntities()->isInsideCompartment(kEntityIvo, kCarRedSleeping, kPosition_2740))
|
|
getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
|
|
else
|
|
getSound()->playSound(kEntityCoudert, "Jac1501", kFlagDefault);
|
|
break;
|
|
}
|
|
|
|
// Update ticks (Compartments A - H are indexes 4 - 11)
|
|
_lastWarning[compartment - 28] = getState()->timeTicks;
|
|
}
|
|
|
|
void SoundManager::excuseMe(EntityIndex entity, EntityIndex entity2, FlagType flag) {
|
|
if (isBuffered(entity) && entity != kEntityPlayer && entity != kEntityChapters && entity != kEntityTrain)
|
|
return;
|
|
|
|
if (entity2 == kEntityFrancois || entity2 == kEntityMax)
|
|
return;
|
|
|
|
if (entity == kEntityFrancois && getEntityData(kEntityFrancois)->field_4A3 != 30)
|
|
return;
|
|
|
|
if (flag == kFlagNone)
|
|
flag = getSoundFlag(entity);
|
|
|
|
switch (entity) {
|
|
default:
|
|
break;
|
|
|
|
case kEntityAnna:
|
|
playSound(kEntityPlayer, "ANN1107A", flag);
|
|
break;
|
|
|
|
case kEntityAugust:
|
|
switch(rnd(4)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "AUG1100A", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "AUG1100B", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "AUG1100C", flag);
|
|
break;
|
|
|
|
case 3:
|
|
playSound(kEntityPlayer, "AUG1100D", flag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case kEntityMertens:
|
|
if (Entities::isFemale(entity2)) {
|
|
playSound(kEntityPlayer, (rnd(2) ? "CON1111" : "CON1111A"), flag);
|
|
} else {
|
|
if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "CON1110A", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "CON1110C", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "CON1110", flag);
|
|
break;
|
|
}
|
|
} else {
|
|
if (isNight()) {
|
|
playSound(kEntityPlayer, (getProgress().field_18 == 2 ? "CON1110F" : "CON1110E"));
|
|
} else {
|
|
playSound(kEntityPlayer, "CON1110D");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kEntityCoudert:
|
|
if (Entities::isFemale(entity2)) {
|
|
playSound(kEntityPlayer, "JAC1111D", flag);
|
|
} else {
|
|
if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
|
|
switch(rnd(4)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "JAC1111", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "JAC1111A", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "JAC1111B", flag);
|
|
break;
|
|
|
|
case 3:
|
|
playSound(kEntityPlayer, "JAC1111C", flag);
|
|
break;
|
|
}
|
|
} else {
|
|
playSound(kEntityPlayer, "JAC1113B", flag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kEntityPascale:
|
|
playSound(kEntityPlayer, (rnd(2) ? "HDE1002" : "HED1002A"), flag);
|
|
break;
|
|
|
|
case kEntityServers0:
|
|
case kEntityServers1:
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002" : "WAT1003", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002A" : "WAT1003A", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002B" : "WAT1003B", flag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case kEntityVerges:
|
|
if (Entities::isFemale(entity2)) {
|
|
playSound(kEntityPlayer, (rnd(2) ? "TRA1113A" : "TRA1113B"));
|
|
} else {
|
|
playSound(kEntityPlayer, "TRA1112", flag);
|
|
}
|
|
break;
|
|
|
|
case kEntityTatiana:
|
|
playSound(kEntityPlayer, (rnd(2) ? "TAT1102A" : "TAT1102B"), flag);
|
|
break;
|
|
|
|
case kEntityAlexei:
|
|
playSound(kEntityPlayer, (rnd(2) ? "ALX1099C" : "ALX1099D"), flag);
|
|
break;
|
|
|
|
case kEntityAbbot:
|
|
if (Entities::isFemale(entity2)) {
|
|
playSound(kEntityPlayer, "ABB3002C", flag);
|
|
} else {
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "ABB3002", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "ABB3002A", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "ABB3002B", flag);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kEntityVesna:
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "VES1109A", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "VES1109B", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "VES1109C", flag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case kEntityKahina:
|
|
playSound(kEntityPlayer, (rnd(2) ? "KAH1001" : "KAH1001A"), flag);
|
|
break;
|
|
|
|
case kEntityFrancois:
|
|
case kEntityMmeBoutarel:
|
|
switch(rnd(4)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001" : "MME1103A", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001A" : "MME1103B", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001B" : "MME1103C", flag);
|
|
break;
|
|
|
|
case 3:
|
|
playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001C" : "MME1103D", flag);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case kEntityBoutarel:
|
|
playSound(kEntityPlayer, "MRB1104", flag);
|
|
if (flag > 2)
|
|
getProgress().eventMetBoutarel = true;
|
|
break;
|
|
|
|
case kEntityRebecca:
|
|
playSound(kEntityPlayer, (rnd(2) ? "REB1106" : "REB110A"), flag);
|
|
break;
|
|
|
|
case kEntitySophie: {
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
playSound(kEntityPlayer, "SOP1105", flag);
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105C" : "SOP1105A", flag);
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105D" : "SOP1105B", flag);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kEntityMahmud:
|
|
playSound(kEntityPlayer, "MAH1101", flag);
|
|
break;
|
|
|
|
case kEntityYasmin:
|
|
playSound(kEntityPlayer, "HAR1002", flag);
|
|
if (flag > 2)
|
|
getProgress().eventMetYasmin = true;
|
|
break;
|
|
|
|
case kEntityHadija:
|
|
playSound(kEntityPlayer, (rnd(2) ? "HAR1001" : "HAR1001A"), flag);
|
|
if (flag > 2)
|
|
getProgress().eventMetHadija = true;
|
|
break;
|
|
|
|
case kEntityAlouan:
|
|
playSound(kEntityPlayer, "HAR1004", flag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SoundManager::excuseMeCath() {
|
|
switch(rnd(3)) {
|
|
default:
|
|
playSound(kEntityPlayer, "CAT1126B");
|
|
break;
|
|
|
|
case 1:
|
|
playSound(kEntityPlayer, "CAT1126C");
|
|
break;
|
|
|
|
case 2:
|
|
playSound(kEntityPlayer, "CAT1126D");
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *SoundManager::justCheckingCath() const {
|
|
switch(rnd(4)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
return "CAT5001";
|
|
|
|
case 1:
|
|
return "CAT5001A";
|
|
|
|
case 2:
|
|
return "CAT5001B";
|
|
|
|
case 3:
|
|
return "CAT5001C";
|
|
}
|
|
|
|
return "CAT5001";
|
|
}
|
|
|
|
const char *SoundManager::wrongDoorCath() const {
|
|
switch(rnd(5)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
return "CAT1125";
|
|
|
|
case 1:
|
|
return "CAT1125A";
|
|
|
|
case 2:
|
|
return "CAT1125B";
|
|
|
|
case 3:
|
|
return "CAT1125C";
|
|
|
|
case 4:
|
|
return "CAT1125D";
|
|
}
|
|
|
|
return "CAT1125";
|
|
}
|
|
|
|
const char *SoundManager::justAMinuteCath() const {
|
|
switch(rnd(3)) {
|
|
default:
|
|
break;
|
|
|
|
case 0:
|
|
return "CAT1520";
|
|
|
|
case 1:
|
|
return "CAT1521";
|
|
|
|
case 2:
|
|
return "CAT1125"; // ?? is this a bug in the original?
|
|
}
|
|
|
|
return "CAT1520";
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Sound flags
|
|
//////////////////////////////////////////////////////////////////////////
|
|
SoundManager::FlagType SoundManager::getSoundFlag(EntityIndex entity) const {
|
|
if (entity == kEntityPlayer)
|
|
return kFlagDefault;
|
|
|
|
if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
|
|
return kFlagNone;
|
|
|
|
// Compute sound value
|
|
FlagType ret = kFlag2;
|
|
|
|
// Get default value if valid
|
|
int index = ABS(getEntityData(entity)->entityPosition - getEntityData(kEntityPlayer)->entityPosition) / 230;
|
|
if (index < 32)
|
|
ret = soundFlags[index];
|
|
|
|
if (getEntityData(entity)->location == kLocationOutsideTrain) {
|
|
if (getEntityData(entity)->car != kCarKronos
|
|
&& !getEntities()->isOutsideAlexeiWindow()
|
|
&& !getEntities()->isOutsideAnnaWindow())
|
|
return kFlagNone;
|
|
|
|
return (FlagType)(ret / 6);
|
|
}
|
|
|
|
switch (getEntityData(entity)->car) {
|
|
default:
|
|
break;
|
|
|
|
case kCarKronos:
|
|
if (getEntities()->isInKronosSalon(entity) != getEntities()->isInKronosSalon(kEntityPlayer))
|
|
ret = (FlagType)(ret * 2);
|
|
break;
|
|
|
|
case kCarGreenSleeping:
|
|
case kCarRedSleeping:
|
|
if (getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInKronosSalon(entity))
|
|
ret = (FlagType)(ret * 2);
|
|
|
|
if (getEntityData(kEntityPlayer)->location
|
|
&& (getEntityData(entity)->entityPosition != kPosition_1 || !getEntities()->isDistanceBetweenEntities(kEntityPlayer, entity, 400)))
|
|
ret = (FlagType)(ret * 2);
|
|
break;
|
|
|
|
case kCarRestaurant:
|
|
if (getEntities()->isInSalon(entity) == getEntities()->isInSalon(kEntityPlayer)
|
|
&& (getEntities()->isInRestaurant(entity) != getEntities()->isInRestaurant(kEntityPlayer)))
|
|
ret = (FlagType)(ret * 2);
|
|
else
|
|
ret = (FlagType)(ret * 4);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Subtitles
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::updateSubtitles() {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
uint32 index = 0;
|
|
SubtitleEntry *subtitle = NULL;
|
|
|
|
for (Common::List<SubtitleEntry *>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
|
|
uint32 current_index = 0;
|
|
SoundEntry *soundEntry = (*i)->sound;
|
|
SoundStatus status = (SoundStatus)soundEntry->status.status;
|
|
|
|
if (!(status & kSoundStatus_40)
|
|
|| status & 0x180
|
|
|| soundEntry->time == 0
|
|
|| (status & 0x1F) < 6
|
|
|| ((getFlags()->nis & 0x8000) && soundEntry->field_4C < 90)) {
|
|
current_index = 0;
|
|
} else {
|
|
current_index = soundEntry->field_4C + (status & 0x1F);
|
|
|
|
if (_currentSubtitle == (*i))
|
|
current_index += 4;
|
|
}
|
|
|
|
if (index < current_index) {
|
|
index = current_index;
|
|
subtitle = (*i);
|
|
}
|
|
}
|
|
|
|
if (_currentSubtitle == subtitle) {
|
|
if (subtitle)
|
|
setupSubtitleAndDraw(subtitle);
|
|
|
|
return;
|
|
}
|
|
|
|
if (_drawSubtitles & 1)
|
|
drawSubtitleOnScreen(subtitle);
|
|
|
|
if (subtitle) {
|
|
loadSubtitleData(subtitle);
|
|
setupSubtitleAndDraw(subtitle);
|
|
}
|
|
}
|
|
|
|
void SoundManager::showSubtitle(SoundEntry *entry, Common::String filename) {
|
|
entry->subtitle = loadSubtitle(filename, entry);
|
|
|
|
if (entry->subtitle->status.status2 & 4) {
|
|
drawSubtitle(entry->subtitle);
|
|
SAFE_DELETE(entry->subtitle);
|
|
} else {
|
|
entry->status.status |= kSoundStatus_20000;
|
|
}
|
|
}
|
|
|
|
SoundManager::SubtitleEntry *SoundManager::loadSubtitle(Common::String filename, SoundEntry *soundEntry) {
|
|
SubtitleEntry *entry = new SubtitleEntry();
|
|
_subtitles.push_back(entry);
|
|
|
|
// Set sound entry and filename
|
|
entry->filename = filename + ".SBE";
|
|
entry->sound = soundEntry;
|
|
|
|
// Load subtitle data
|
|
if (_engine->getResourceManager()->hasFile(filename)) {
|
|
if (_drawSubtitles & 2)
|
|
return entry;
|
|
|
|
loadSubtitleData(entry);
|
|
} else {
|
|
entry->status.status = kSoundStatus_400;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
void SoundManager::loadSubtitleData(SubtitleEntry * entry) {
|
|
entry->data = new SubtitleManager(_engine->getFont());
|
|
entry->data->load(getArchive(entry->filename));
|
|
|
|
_drawSubtitles |= 2;
|
|
_currentSubtitle = entry;
|
|
}
|
|
|
|
void SoundManager::setupSubtitleAndDraw(SubtitleEntry *subtitle) {
|
|
if (!subtitle->data) {
|
|
subtitle->data = new SubtitleManager(_engine->getFont());
|
|
subtitle->data->load(getArchive(subtitle->filename));
|
|
}
|
|
|
|
if (subtitle->data->getMaxTime() > subtitle->sound->time) {
|
|
subtitle->status.status = kSoundStatus_400;
|
|
} else {
|
|
subtitle->data->setTime((uint16)subtitle->sound->time);
|
|
|
|
if (_drawSubtitles & 1)
|
|
drawSubtitleOnScreen(subtitle);
|
|
}
|
|
|
|
_currentSubtitle = subtitle;
|
|
}
|
|
|
|
void SoundManager::drawSubtitle(SubtitleEntry *subtitle) {
|
|
// Remove subtitle from queue
|
|
_subtitles.remove(subtitle);
|
|
|
|
if (subtitle == _currentSubtitle) {
|
|
drawSubtitleOnScreen(subtitle);
|
|
|
|
_currentSubtitle = NULL;
|
|
_drawSubtitles = 0;
|
|
}
|
|
}
|
|
|
|
void SoundManager::drawSubtitleOnScreen(SubtitleEntry *subtitle) {
|
|
if (!subtitle)
|
|
error("SoundManager::drawSubtitleOnScreen: Invalid subtitle entry!");
|
|
|
|
_drawSubtitles &= ~1;
|
|
|
|
if (subtitle->data == NULL)
|
|
return;
|
|
|
|
if (_drawSubtitles & 1)
|
|
_engine->getGraphicsManager()->draw(subtitle->data, GraphicsManager::kBackgroundOverlay);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Misc
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SoundManager::playLoopingSound() {
|
|
warning("SoundManager::playLoopingSound: not implemented!");
|
|
}
|
|
|
|
void SoundManager::stopAllSound() {
|
|
Common::StackLock locker(_mutex);
|
|
|
|
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
|
|
(*i)->soundStream->stop();
|
|
}
|
|
|
|
} // End of namespace LastExpress
|