/* 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 3 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, see . * * * Based on the original sources * Faery Tale II -- The Halls of the Dead * (c) 1993-1996 The Wyrmkeep Entertainment Co. */ #include "saga2/saga2.h" #include "saga2/idtypes.h" #include "saga2/tile.h" #include "saga2/beegee.h" #include "saga2/player.h" namespace Saga2 { #define AUXTHEMES 2 #define USEAUXTHEME 0xe0 void addAuxTheme(Location loc, uint32 lid); void killAuxTheme(uint32 lid); void killAllAuxThemes(); enum audioTerrains { kAudioTerrainForest = 1, kAudioTerrainSurf, kAudioTerrainCity, kAudioTerrainCavern, kAudioTerrainColdWind, kAudioTerrainJungle, kAudioTerrainLava, kAudioTerrainRiver, kAudioTerrainFire, kAudioTerrainLIMIT }; enum { kCheckGameTime = 1000 }; struct IntermittentAudioRecord { int noSoundOdds; int soundOdds[4]; }; const IntermittentAudioRecord intermittentAudioRecords[kAudioTerrainLIMIT] = { // // none 1 2 3 4 // { 0, { 0, 0, 0, 0 } }, // no record { 1, { 1, 0, 0, 0 } }, // Forest { 0, { 0, 0, 0, 0 } }, // Surf { 0, { 0, 0, 0, 0 } }, // City { 0, { 0, 0, 0, 0 } }, // Cavern { 0, { 0, 0, 0, 0 } }, // ColdWind { 0, { 0, 0, 0, 0 } }, // Jungle { 0, { 0, 0, 0, 0 } }, // Lava { 0, { 0, 0, 0, 0 } }, // River { 0, { 0, 0, 0, 0 } } // Fire }; // Factional music mapping // Built in factions // 0 = suspended // 1 = daytime 1 // 2 = daytime 2 // 3 = underground // 4 = nighttime // 5 = aggressive // Faction based // 6 = faction 0 // 7 = faction 1 // etc inline int8 musicMapping(int16 musicChoice) { if (musicChoice < 1) { return 0; } else if (musicChoice < 6) { return musicChoice; } else if (musicChoice == 6) { return 6; } else if (musicChoice > 11 && musicChoice < 14) { return 7; } else if (musicChoice > 8 && musicChoice < 12) { return 8; } else { return 6; } } const static StaticTilePoint AudibilityVector = { 1, 1, 0 }; /* ===================================================================== * Imports * ===================================================================== */ extern volatile int32 gameTime; extern uint16 rippedRoofID; extern GameObject *getViewCenterObject(); #if DEBUG extern bool debugAudioThemes; #endif /* ===================================================================== * Prototypes * ===================================================================== */ #define musicResID(i) MKTAG('X','M','I',i) inline metaTileNoise getSound(MetaTilePtr mt) { int hmm = mt->HeavyMetaMusic(); return (hmm >= 0 && hmm < kMaxThemes) ? hmm : 0; } inline uint32 metaNoiseID(metaTileNoise mtnID) { return mtnID ? MKTAG('T', 'E', 'R', mtnID) : 0; } void _playLoop(uint32 s); MetaTileID lookupMetaID(TilePoint coords); /* ===================================================================== * Module code * ===================================================================== */ //----------------------------------------------------------------------- // init void initAudioEnvirons() { } /* ===================================================================== * Looped sound engine * ===================================================================== */ void addAuxTheme(Location loc, uint32 lid) { for (int i = 0; i < AUXTHEMES; i++) { if (!g_vm->_grandMasterFTA->_aats[i].active) { g_vm->_grandMasterFTA->_aats[i].l = loc; g_vm->_grandMasterFTA->_aats[i].loopID = lid; g_vm->_grandMasterFTA->_aats[i].active = true; return; } } } void killAuxTheme(uint32 lid) { for (int i = 0; i < AUXTHEMES; i++) { if (g_vm->_grandMasterFTA->_aats[i].active && g_vm->_grandMasterFTA->_aats[i].loopID == lid) { g_vm->_grandMasterFTA->_aats[i].active = false; } } } void killAllAuxThemes() { for (int i = 0; i < AUXTHEMES; i++) { g_vm->_grandMasterFTA->_aats[i].active = false; } } //----------------------------------------------------------------------- // Hooks to allow other loops to play void disableBGLoop(bool s) { g_vm->_grandMasterFTA->_playingExternalLoop = s; } void enableBGLoop() { uint32 cr = g_vm->_grandMasterFTA->_currentTheme; g_vm->_grandMasterFTA->_playingExternalLoop = false; g_vm->_grandMasterFTA->_currentTheme = 0; audioEnvironmentUseSet(cr, g_vm->_grandMasterFTA->_auxTheme, g_vm->_grandMasterFTA->_themeAt); } //----------------------------------------------------------------------- // Main loop selection routine - called from Tile.cpp void setAreaSound(const TilePoint &) { g_vm->_grandMasterFTA->_pct = (g_vm->_grandMasterFTA->_pct + 1) % 8; if (g_vm->_grandMasterFTA->_pct == 0) { if (!g_vm->_grandMasterFTA->_playingExternalLoop) { TilePoint baseCoords = centerActorCoords() >> kTileUVShift; TilePoint mtPos; metaTileNoise loopID = 0; uint32 ss = 0; Point32 themePos; for (int r = 1; r < 5 && loopID == 0 ; r++) { TileRegion regn; TilePoint AudVec = TilePoint(AudibilityVector); regn.max = baseCoords + ((AudVec * r) << kPlatShift) ; ///kTileUVSize; regn.min = baseCoords - ((AudVec * r) << kPlatShift); ///kTileUVSize; MetaTileIterator mIter(g_vm->_currentMapNum, regn); int i = 0; TilePoint dist = AudVec * r << (kPlatShift + 1); dist = dist << 4; themePos.x = dist.u; themePos.y = dist.v; MetaTilePtr mt = mIter.first(&mtPos); while (mt) { i++; if (getSound(mt)) { TilePoint thisDist = mtPos - baseCoords; if (thisDist.magnitude() < dist.magnitude()) { dist = thisDist; loopID = getSound(mt); themePos.x = thisDist.u; themePos.y = thisDist.v; } } mt = mIter.next(&mtPos); } for (i = 0; i < AUXTHEMES; i++) { if (g_vm->_grandMasterFTA->_aats[i].active) { Location loc = getCenterActor()->notGetWorldLocation(); if (g_vm->_grandMasterFTA->_aats[i].l._context == Nothing || loc._context == g_vm->_grandMasterFTA->_aats[i].l._context) { TilePoint tp = (g_vm->_grandMasterFTA->_aats[i].l >> kTileUVShift) - baseCoords; if (tp.magnitude() < dist.magnitude()) { dist = tp; loopID = USEAUXTHEME; ss = g_vm->_grandMasterFTA->_aats[i].loopID; themePos.x = tp.u; themePos.y = tp.v; } } } } } if (rippedRoofID) { loopID = 0; } audioEnvironmentUseSet(loopID, ss, themePos << kPlatShift); } else if (g_vm->_grandMasterFTA->_playingExternalLoop) { audioEnvironmentUseSet(g_vm->_grandMasterFTA->_playingExternalLoop, 0, Point16(0, 0)); //themePos << kPlatShift); } } } //----------------------------------------------------------------------- // Implement a particular loop void audioEnvironmentUseSet(int16 audioSet, int32 auxID, Point32 relPos) { uint32 res; if (audioSet == USEAUXTHEME) res = auxID; else if (audioSet > 0 && audioSet <= 16) res = metaNoiseID(audioSet); else res = 0; if (g_vm->_grandMasterFTA->_currentTheme != (uint16)audioSet || g_vm->_grandMasterFTA->_auxTheme != (uint32)auxID) { g_vm->_grandMasterFTA->_currentTheme = audioSet; g_vm->_grandMasterFTA->_auxTheme = auxID; g_vm->_grandMasterFTA->_themeAt.x = relPos.x; g_vm->_grandMasterFTA->_themeAt.y = relPos.y; _playLoop(0); if (g_vm->_grandMasterFTA->_currentTheme) playLoopAt(res, g_vm->_grandMasterFTA->_themeAt); } else if (g_vm->_grandMasterFTA->_currentTheme && g_vm->_grandMasterFTA->_themeAt != relPos) { #if DEBUG if (debugAudioThemes) { WriteStatusF(9, "Thm: %2.2d (%d,%d) was (%d,%d) ", audioSet, relPos.x, relPos.y, g_vm->_grandMasterFTA->_themeAt.x, g_vm->_grandMasterFTA->_themeAt.y); } #endif g_vm->_grandMasterFTA->_themeAt.x = relPos.x; g_vm->_grandMasterFTA->_themeAt.y = relPos.y; moveLoop(g_vm->_grandMasterFTA->_themeAt); } } //----------------------------------------------------------------------- // Intermittent sound check void audioEnvironmentCheck() { uint32 delta = gameTime - g_vm->_grandMasterFTA->_lastGameTime; g_vm->_grandMasterFTA->_lastGameTime = gameTime; if (g_vm->_grandMasterFTA->_currentTheme > 0 && g_vm->_grandMasterFTA->_currentTheme <= kAudioTerrainLIMIT) { g_vm->_grandMasterFTA->_elapsedGameTime += delta; if (g_vm->_grandMasterFTA->_elapsedGameTime > kCheckGameTime) { int i; g_vm->_grandMasterFTA->_elapsedGameTime = 0; const IntermittentAudioRecord &iar = intermittentAudioRecords[g_vm->_grandMasterFTA->_currentTheme]; int16 totalProb = iar.noSoundOdds; for (i = 0; i < 4; i++) totalProb += iar.soundOdds[i]; if (totalProb <= iar.noSoundOdds) return; int32 pval = g_vm->_rnd->getRandomNumber(totalProb - 1); if (pval < iar.noSoundOdds) return; pval -= iar.noSoundOdds; for (i = 0; i < 4; i++) { if (pval < iar.soundOdds[i]) { //GameObject *go=getViewCenterObject(); //Location cal=Location(TilePoint(g_vm->_grandMasterFTA->_themeAt.x,g_vm->_grandMasterFTA->_themeAt.y,0),go->IDParent()); //playSound(metaNoiseID((g_vm->_grandMasterFTA->_currentTheme*10)+i)); playSoundAt(metaNoiseID((g_vm->_grandMasterFTA->_currentTheme * 10) + i), g_vm->_grandMasterFTA->_themeAt); return; } else pval -= iar.soundOdds[i]; } } } else if (g_vm->_grandMasterFTA->_currentTheme) warning("currentTheme out of range: %d", g_vm->_grandMasterFTA->_currentTheme); } /* ===================================================================== * Music environment engine * ===================================================================== */ //----------------------------------------------------------------------- // Intermittent sound check void Deejay::select() { int choice = 0; #if DEBUG & 0 if (1) choice = 0; else #endif if (_susp) choice = 0; else if (_enemy >= 0) choice = _enemy + 6; else if (_aggr) choice = 5; else if (_ugd) choice = 3; //else if ( !day ) // choice=4; else if (_current != 4 && (_current > 2 || _current < 1)) { choice = 1 + g_vm->_rnd->getRandomNumber(2); if (choice == 3) choice++; } else choice = _current; if (_currentID != musicMapping(choice)) { _currentID = musicMapping(choice); if (_currentID) playMusic(musicResID(_currentID)); else playMusic(0); } _current = choice; #if DEBUG if (debugAudioThemes) { WriteStatusF(8, "Music: %2.2d => %2.2d ", _current, _currentID); } #endif } //----------------------------------------------------------------------- // Faction enumeration routines void clearActiveFactions() { for (int i = 0; i < kMaxFactions; i++) g_vm->_grandMasterFTA->_activeFactions[i] = 0; } void incrementActiveFaction(Actor *a) { g_vm->_grandMasterFTA->_activeFactions[a->_faction]++; } void useActiveFactions() { int highCount = 0; int highFaction = 0; for (int i = 0; i < kMaxFactions; i++) { if (g_vm->_grandMasterFTA->_activeFactions[i] > highCount) { highCount = g_vm->_grandMasterFTA->_activeFactions[i]; highFaction = i; } } if (highCount) g_vm->_grandMasterFTA->setEnemy(highFaction); else g_vm->_grandMasterFTA->setEnemy(kNoEnemy); } //----------------------------------------------------------------------- // Aggresssion & day/night control void audioEnvironmentSetAggression(bool onOff) { g_vm->_grandMasterFTA->setAggression(onOff); } void audioEnvironmentSetDaytime(bool onOff) { g_vm->_grandMasterFTA->setDaytime(onOff); } void audioEnvironmentSuspend(bool onOff) { g_vm->_grandMasterFTA->setSuspend(onOff); } void audioEnvironmentSetWorld(int mapNum) { g_vm->_grandMasterFTA->setWorld(mapNum == 1); } } // end of namespace Saga2