mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-10 12:55:24 +00:00
4c3a68027f
it's Logic::_scriptVars[ID] instead of just ID. Apart from looking cool, it makes it much easier to tell the difference between variables and constants when looking at the code. Of course, this sort of sweeping changes is jolly good for introducing truly weird regressions, which is why I waited until after 0.6.0. svn-id: r13331
416 lines
10 KiB
C++
416 lines
10 KiB
C++
/* Copyright (C) 1994-2004 Revolution Software Ltd
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BROKEN SWORD 2
|
|
//
|
|
// SOUND.CPP Contains the sound engine, fx & music functions
|
|
// Some very 'sound' code in here ;)
|
|
//
|
|
// (16Dec96 JEL)
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "common/stdafx.h"
|
|
#include "common/file.h"
|
|
#include "sword2/sword2.h"
|
|
#include "sword2/defs.h"
|
|
#include "sword2/interpreter.h"
|
|
#include "sword2/logic.h"
|
|
#include "sword2/resman.h"
|
|
#include "sword2/driver/d_sound.h"
|
|
|
|
namespace Sword2 {
|
|
|
|
// initialise the fxq by clearing all the entries
|
|
|
|
void Sword2Engine::initFxQueue(void) {
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
_fxQueue[i].resource = 0; // 0 resource means 'empty' slot
|
|
_fxQueue[i].fetchId = 0; // Not being fetched.
|
|
}
|
|
}
|
|
|
|
// process the fx queue once every game cycle
|
|
|
|
void Sword2Engine::processFxQueue(void) {
|
|
for (int i = 0; i < FXQ_LENGTH; i++) {
|
|
if (!_fxQueue[i].resource)
|
|
continue;
|
|
|
|
switch (_fxQueue[i].type) {
|
|
case FX_RANDOM:
|
|
// 1 in 'delay' chance of this fx occurring
|
|
if (_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
|
|
triggerFx(i);
|
|
break;
|
|
case FX_SPOT:
|
|
if (_fxQueue[i].delay)
|
|
_fxQueue[i].delay--;
|
|
else {
|
|
triggerFx(i);
|
|
_fxQueue[i].type = FX_SPOT2;
|
|
}
|
|
break;
|
|
case FX_SPOT2:
|
|
// Once the Fx has finished remove it from the queue.
|
|
if (!_sound->isFxPlaying(i + 1)) {
|
|
_fxQueue[i].resource = 0;
|
|
_sound->closeFx(i + 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// called from processFxQueue only
|
|
|
|
void Sword2Engine::triggerFx(uint8 j) {
|
|
uint8 *data;
|
|
int32 id;
|
|
uint32 rv;
|
|
|
|
id = (uint32) j + 1; // because 0 is not a valid id
|
|
|
|
if (_fxQueue[j].type == FX_SPOT) {
|
|
// load in the sample
|
|
data = _resman->openResource(_fxQueue[j].resource);
|
|
data += sizeof(StandardHeader);
|
|
// wav data gets copied to sound memory
|
|
rv = _sound->playFx(id, data, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
|
|
// release the sample
|
|
_resman->closeResource(_fxQueue[j].resource);
|
|
} else {
|
|
// random & looped fx are already loaded into sound memory
|
|
// by fnPlayFx()
|
|
// - to be referenced by 'j', so pass NULL data
|
|
|
|
if (_fxQueue[j].type == FX_RANDOM) {
|
|
// Not looped
|
|
rv = _sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXSPOT);
|
|
} else {
|
|
// Looped
|
|
rv = _sound->playFx(id, NULL, _fxQueue[j].volume, _fxQueue[j].pan, RDSE_FXLOOP);
|
|
}
|
|
}
|
|
|
|
if (rv)
|
|
debug(5, "SFX ERROR: playFx() returned %.8x", rv);
|
|
}
|
|
|
|
// Stops all looped & random fx and clears the entire queue
|
|
|
|
void Sword2Engine::clearFxQueue(void) {
|
|
// stop all fx & remove the samples from sound memory
|
|
_sound->clearAllFx();
|
|
|
|
// clean out the queue
|
|
initFxQueue();
|
|
}
|
|
|
|
void Sword2Engine::killMusic(void) {
|
|
_loopingMusicId = 0; // clear the 'looping' flag
|
|
_sound->stopMusic();
|
|
}
|
|
|
|
void Sword2Engine::pauseAllSound(void) {
|
|
_sound->pauseMusic();
|
|
_sound->pauseSpeech();
|
|
_sound->pauseFx();
|
|
}
|
|
|
|
void Sword2Engine::unpauseAllSound(void) {
|
|
_sound->unpauseMusic();
|
|
_sound->unpauseSpeech();
|
|
_sound->unpauseFx();
|
|
}
|
|
|
|
// called from script only
|
|
|
|
int32 Logic::fnPlayFx(int32 *params) {
|
|
// params: 0 sample resource id
|
|
// 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
|
|
// 2 delay (0..65535)
|
|
// 3 volume (0..16)
|
|
// 4 pan (-16..16)
|
|
|
|
// example script:
|
|
// fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
|
|
// // fx_water is just a local script flag
|
|
// fx_water = result;
|
|
// .
|
|
// .
|
|
// .
|
|
// fnStopFx (fx_water);
|
|
|
|
uint8 j = 0;
|
|
uint8 *data;
|
|
uint32 id;
|
|
uint32 rv;
|
|
|
|
#ifdef _SWORD2_DEBUG
|
|
StandardHeader *header;
|
|
#endif
|
|
|
|
if (_vm->_wantSfxDebug) {
|
|
char type[10];
|
|
|
|
switch (params[1]) {
|
|
case FX_SPOT:
|
|
strcpy(type, "SPOT");
|
|
break;
|
|
case FX_LOOP:
|
|
strcpy(type, "LOOPED");
|
|
break;
|
|
case FX_RANDOM:
|
|
strcpy(type, "RANDOM");
|
|
break;
|
|
default:
|
|
strcpy(type, "INVALID");
|
|
break;
|
|
}
|
|
|
|
debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(params[0]), params[3], params[4], params[2], type);
|
|
}
|
|
|
|
while (j < FXQ_LENGTH && _vm->_fxQueue[j].resource != 0)
|
|
j++;
|
|
|
|
if (j == FXQ_LENGTH)
|
|
return IR_CONT;
|
|
|
|
_vm->_fxQueue[j].resource = params[0]; // wav resource id
|
|
_vm->_fxQueue[j].type = params[1]; // FX_SPOT, FX_LOOP or FX_RANDOM
|
|
|
|
if (_vm->_fxQueue[j].type == FX_RANDOM) {
|
|
// 'delay' param is the intended average no. seconds between
|
|
// playing this effect
|
|
_vm->_fxQueue[j].delay = params[2] * 12;
|
|
} else {
|
|
// FX_SPOT or FX_LOOP:
|
|
// 'delay' is no. frames to wait before playing
|
|
_vm->_fxQueue[j].delay = params[2];
|
|
}
|
|
|
|
_vm->_fxQueue[j].volume = params[3]; // 0..16
|
|
_vm->_fxQueue[j].pan = params[4]; // -16..16
|
|
|
|
if (_vm->_fxQueue[j].type == FX_SPOT) {
|
|
// "pre-load" the sample; this gets it into memory
|
|
data = _vm->_resman->openResource(_vm->_fxQueue[j].resource);
|
|
|
|
#ifdef _SWORD2_DEBUG
|
|
header = (StandardHeader *) data;
|
|
if (header->fileType != WAV_FILE)
|
|
error("fnPlayFx given invalid resource");
|
|
#endif
|
|
|
|
// but then releases it to "age" out if the space is needed
|
|
_vm->_resman->closeResource(_vm->_fxQueue[j].resource);
|
|
} else {
|
|
// random & looped fx
|
|
|
|
id = (uint32) j + 1; // because 0 is not a valid id
|
|
|
|
// load in the sample
|
|
data = _vm->_resman->openResource(_vm->_fxQueue[j].resource);
|
|
|
|
#ifdef _SWORD2_DEBUG
|
|
header = (StandardHeader *) data;
|
|
if (header->fileType != WAV_FILE)
|
|
error("fnPlayFx given invalid resource");
|
|
#endif
|
|
|
|
data += sizeof(StandardHeader);
|
|
|
|
// copy it to sound memory, using position in queue as 'id'
|
|
rv = _vm->_sound->openFx(id, data);
|
|
|
|
if (rv)
|
|
debug(5, "SFX ERROR: openFx() returned %.8x", rv);
|
|
|
|
// release the sample
|
|
_vm->_resman->closeResource(_vm->_fxQueue[j].resource);
|
|
}
|
|
|
|
if (_vm->_fxQueue[j].type == FX_LOOP) {
|
|
// play now, rather than in processFxQueue where it was
|
|
// getting played again & again!
|
|
_vm->triggerFx(j);
|
|
}
|
|
|
|
// in case we want to call fnStopFx() later, to kill this fx
|
|
// (mainly for FX_LOOP & FX_RANDOM)
|
|
|
|
_scriptVars[RESULT] = j;
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnSoundFetch(int32 *params) {
|
|
// params: 0 id of sound to fetch [guess]
|
|
return IR_CONT;
|
|
}
|
|
|
|
// to alter the volume and pan of a currently playing fx
|
|
|
|
int32 Logic::fnSetFxVolAndPan(int32 *params) {
|
|
// params: 0 id of fx (ie. the id returned in 'result' from
|
|
// fnPlayFx
|
|
// 1 new volume (0..16)
|
|
// 2 new pan (-16..16)
|
|
|
|
debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
|
|
|
|
// setFxIdVolumePan(int32 id, uint8 vol, uint8 pan);
|
|
// driver fx_id is 1 + <pos in queue>
|
|
_vm->_sound->setFxIdVolumePan(1 + params[0], params[1], params[2]);
|
|
return IR_CONT;
|
|
}
|
|
|
|
// to alter the volume of a currently playing fx
|
|
|
|
int32 Logic::fnSetFxVol(int32 *params) {
|
|
// params: 0 id of fx (ie. the id returned in 'result' from
|
|
// fnPlayFx
|
|
// 1 new volume (0..16)
|
|
|
|
// SetFxIdVolume(int32 id, uint8 vol);
|
|
_vm->_sound->setFxIdVolume(1 + params[0], params[1]);
|
|
return IR_CONT;
|
|
}
|
|
|
|
// called from script only
|
|
|
|
int32 Logic::fnStopFx(int32 *params) {
|
|
// params: 0 position in queue
|
|
|
|
// This will stop looped & random fx instantly, and remove the fx
|
|
// from the queue. So although it doesn't stop spot fx, it will
|
|
// remove them from the queue if they haven't yet played
|
|
|
|
uint8 j = (uint8) params[0];
|
|
uint32 id;
|
|
uint32 rv;
|
|
|
|
if (_vm->_fxQueue[j].type == FX_RANDOM || _vm->_fxQueue[j].type == FX_LOOP) {
|
|
id = (uint32) j + 1; // because 0 is not a valid id
|
|
|
|
// stop fx & remove sample from sound memory
|
|
rv = _vm->_sound->closeFx(id);
|
|
|
|
if (rv)
|
|
debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
|
|
}
|
|
|
|
// remove from queue
|
|
_vm->_fxQueue[j].resource = 0;
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
// called from script only
|
|
|
|
int32 Logic::fnStopAllFx(int32 *params) {
|
|
// Stops all looped & random fx and clears the entire queue
|
|
|
|
// params: none
|
|
|
|
_vm->clearFxQueue();
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnPrepareMusic(int32 *params) {
|
|
// params: 1 id of music to prepare [guess]
|
|
return IR_CONT;
|
|
}
|
|
|
|
// Start a tune playing, to play once or to loop until stopped or next one
|
|
// played
|
|
|
|
int32 Logic::fnPlayMusic(int32 *params) {
|
|
// params: 0 tune id
|
|
// 1 loop flag (0 or 1)
|
|
|
|
char filename[128];
|
|
bool loopFlag;
|
|
uint32 rv;
|
|
|
|
if (params[1] == FX_LOOP) {
|
|
loopFlag = true;
|
|
|
|
// keep a note of the id, for restarting after an
|
|
// interruption to gameplay
|
|
_vm->_loopingMusicId = params[0];
|
|
} else {
|
|
loopFlag = false;
|
|
|
|
// don't need to restart this tune after control panel or
|
|
// restore
|
|
_vm->_loopingMusicId = 0;
|
|
}
|
|
|
|
// add the appropriate file extension & play it
|
|
|
|
if (_scriptVars[DEMO]) {
|
|
// The demo I found didn't come with any music file, but you
|
|
// could use the music from the first CD of the complete game,
|
|
// I suppose...
|
|
strcpy(filename, "music.clu");
|
|
} else {
|
|
File f;
|
|
|
|
sprintf(filename, "music%d.clu", _vm->_resman->whichCd());
|
|
if (f.open(filename))
|
|
f.close();
|
|
else
|
|
strcpy(filename, "music.clu");
|
|
}
|
|
|
|
rv = _vm->_sound->streamCompMusic(filename, params[0], loopFlag);
|
|
|
|
if (rv)
|
|
debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
// called from script only
|
|
|
|
int32 Logic::fnStopMusic(int32 *params) {
|
|
// params: none
|
|
|
|
_vm->_loopingMusicId = 0; // clear the 'looping' flag
|
|
_vm->_sound->stopMusic();
|
|
return IR_CONT;
|
|
}
|
|
|
|
int32 Logic::fnCheckMusicPlaying(int32 *params) {
|
|
// params: none
|
|
|
|
// sets result to no. of seconds of current tune remaining
|
|
// or 0 if no music playing
|
|
|
|
// in seconds, rounded up to the nearest second
|
|
_scriptVars[RESULT] = _vm->_sound->musicTimeRemaining();
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
} // End of namespace Sword2
|