diff options
author | Eugene Sandulenko | 2010-10-18 19:17:38 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2010-10-18 19:17:38 +0000 |
commit | 86d650926f9b991b6398e4ad4b0613ac264dfbaa (patch) | |
tree | 5e6791249fa5fce7dd3e1a6406dff4bf720ca085 /engines/lastexpress/game/sound.cpp | |
parent | c92d2bc234f2f73a9629b3622cd5e66c57439cda (diff) | |
download | scummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.tar.gz scummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.tar.bz2 scummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.zip |
LASTEXPRESS: Merge in the engine.
svn-id: r53579
Diffstat (limited to 'engines/lastexpress/game/sound.cpp')
-rw-r--r-- | engines/lastexpress/game/sound.cpp | 1676 |
1 files changed, 1676 insertions, 0 deletions
diff --git a/engines/lastexpress/game/sound.cpp b/engines/lastexpress/game/sound.cpp new file mode 100644 index 0000000000..f8b45bb385 --- /dev/null +++ b/engines/lastexpress/game/sound.cpp @@ -0,0 +1,1676 @@ +/* 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/data/snd.h" +#include "lastexpress/data/subtitle.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/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) { + _soundStream = new StreamedSound(); + + // Initialize unknown data + _data0 = 0; + _data1 = 0; + _data2 = 0; + + memset(&_buffer, 0, sizeof(_buffer)); + memset(&_lastWarning, 0, sizeof(_lastWarning)); +} + +SoundManager::~SoundManager() { + delete _soundStream; + + // Zero passed pointers + _engine = NULL; +} + +////////////////////////////////////////////////////////////////////////// +// Timer +////////////////////////////////////////////////////////////////////////// +void SoundManager::handleTimer() { + + for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) { + SoundEntry *entry = (*i); + if (entry->stream == NULL) { + i = _cache.reverse_erase(i); + continue; + } else if (!entry->isStreamed) { + entry->isStreamed = true; + _soundStream->load(entry->stream); + } + } + + // TODO: stream any sound in the queue after filtering +} + +////////////////////////////////////////////////////////////////////////// +// Sound queue management +////////////////////////////////////////////////////////////////////////// +void SoundManager::updateQueue() { + //warning("Sound::unknownFunction1: not implemented!"); +} + +void SoundManager::resetQueue(SoundType type1, SoundType type2) { + if (!type2) + type2 = type1; + + 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) { + SoundEntry *entry = getEntry(entity); + + if (entry) + resetEntry(entry); +} + +void SoundManager::removeFromQueue(Common::String filename) { + SoundEntry *entry = getEntry(filename); + + if (entry) + resetEntry(entry); +} + +void SoundManager::clearQueue() { + _flag |= 4; + + // Wait a while for a flag to be set + for (int i = 0; i < 3000000; i++) + if (_flag & 8) + break; + + _flag |= 8; + + for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) { + SoundEntry *entry = (*i); + + removeEntry(entry); + + // Delete entry + SAFE_DELETE(entry); + i = _cache.reverse_erase(i); + } + + updateSubtitles(); +} + +bool SoundManager::isBuffered(EntityIndex entity) { + return (getEntry(entity) != NULL); +} + +bool SoundManager::isBuffered(Common::String filename, bool testForEntity) { + 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 & kFlagType7) { + 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::updateCache: not implemented!"); + return true; +} + +void SoundManager::clearStatus() { + 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->isStreamed) + SAFE_DELETE(entry->stream); + + entry->stream = NULL; + } +} + + +void SoundManager::removeEntry(SoundEntry *entry) { + entry->status.status |= kSoundStatusRemoved; + + // Loop until ready + while (!(entry->status.status1 & 4) && !(_flag & 8) && (_flag & 1)); + + // 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) { + drawSubtitles(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) { + SoundEntry *entry = getEntry(entity); + + if (entry) { + updateEntry(entry, 0); + entry->entity = kEntityPlayer; + } +} + +void SoundManager::processEntry(SoundType type) { + SoundEntry *entry = getEntry(type); + + if (entry) + updateEntry(entry, 0); +} + +void SoundManager::setupEntry(SoundType type, EntityIndex index) { + SoundEntry *entry = getEntry(type); + + if (entry) + entry->entity = index; +} + +void SoundManager::processEntry(Common::String filename) { + SoundEntry *entry = getEntry(filename); + + if (entry) { + updateEntry(entry, 0); + entry->entity = kEntityPlayer; + } +} + +void SoundManager::processEntries() { + _state = 0; + + processEntry(kSoundType1); + processEntry(kSoundType2); +} + +////////////////////////////////////////////////////////////////////////// +// Misc +////////////////////////////////////////////////////////////////////////// + +void SoundManager::unknownFunction4() { + 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 &ser) { + error("Sound::saveLoadWithSerializer: not implemented!"); +} + +////////////////////////////////////////////////////////////////////////// +// 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); +} + +SoundManager::SoundType SoundManager::playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4) { + SoundEntry* entry = new SoundEntry(); + 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(); + + showSubtitles(entry, filename); + updateEntryState(entry); + } + + return entry->type; +} + +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) + showSubtitles(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(kEventLocomotiveMilos) || 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() { + //warning("SoundManager::updateSubtitles: not implemented!"); +} + +void SoundManager::showSubtitles(SoundEntry *entry, Common::String filename) { + warning("SoundManager::showSubtitles: not implemented!"); +} + +void SoundManager::drawSubtitles(SubtitleManager *subtitle) { + warning("SoundManager::drawSubtitles: not implemented!"); +} + +////////////////////////////////////////////////////////////////////////// +// Misc +////////////////////////////////////////////////////////////////////////// +void SoundManager::playLoopingSound() { + warning("SoundManager::playLoopingSound: not implemented!"); +} + +void SoundManager::stopAllSound() const { + _soundStream->stop(); +} + +} // End of namespace LastExpress |