// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // HEADER FILES ------------------------------------------------------------ #include #include "m_random.h" #include "h2def.h" #include "i_system.h" #include "i_sound.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ #define SS_MAX_SCRIPTS 64 #define SS_TEMPBUFFER_SIZE 1024 #define SS_SEQUENCE_NAME_LENGTH 32 #define SS_SCRIPT_NAME "SNDSEQ" #define SS_STRING_PLAY "play" #define SS_STRING_PLAYUNTILDONE "playuntildone" #define SS_STRING_PLAYTIME "playtime" #define SS_STRING_PLAYREPEAT "playrepeat" #define SS_STRING_DELAY "delay" #define SS_STRING_DELAYRAND "delayrand" #define SS_STRING_VOLUME "volume" #define SS_STRING_END "end" #define SS_STRING_STOPSOUND "stopsound" // TYPES ------------------------------------------------------------------- typedef enum { SS_CMD_NONE, SS_CMD_PLAY, SS_CMD_WAITUNTILDONE, // used by PLAYUNTILDONE SS_CMD_PLAYTIME, SS_CMD_PLAYREPEAT, SS_CMD_DELAY, SS_CMD_DELAYRAND, SS_CMD_VOLUME, SS_CMD_STOPSOUND, SS_CMD_END } sscmds_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void VerifySequencePtr(int *base, int *ptr); static int GetSoundOffset(char *name); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern sfxinfo_t S_sfx[]; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static struct { char name[SS_SEQUENCE_NAME_LENGTH]; int scriptNum; int stopSound; } SequenceTranslate[SEQ_NUMSEQ] = { { "Platform", 0, 0}, { "Platform", 0, 0}, // a 'heavy' platform is just a platform { "PlatformMetal", 0, 0}, { "Platform", 0, 0}, // same with a 'creak' platform { "Silence", 0, 0}, { "Lava", 0, 0}, { "Water", 0, 0}, { "Ice", 0, 0}, { "Earth", 0, 0}, { "PlatformMetal2", 0, 0}, { "DoorNormal", 0, 0}, { "DoorHeavy", 0, 0}, { "DoorMetal", 0, 0}, { "DoorCreak", 0, 0}, { "Silence", 0, 0}, { "Lava", 0, 0}, { "Water", 0, 0}, { "Ice", 0, 0}, { "Earth", 0, 0}, { "DoorMetal2", 0, 0}, { "Wind", 0, 0} }; static int *SequenceData[SS_MAX_SCRIPTS]; int ActiveSequences; seqnode_t *SequenceListHead; // CODE -------------------------------------------------------------------- //========================================================================== // // VerifySequencePtr // // Verifies the integrity of the temporary ptr, and ensures that the ptr // isn't exceeding the size of the temporary buffer //========================================================================== static void VerifySequencePtr(int *base, int *ptr) { if (ptr - base > SS_TEMPBUFFER_SIZE) { I_Error("VerifySequencePtr: tempPtr >= %d\n", SS_TEMPBUFFER_SIZE); } } //========================================================================== // // GetSoundOffset // //========================================================================== static int GetSoundOffset(char *name) { int i; for (i = 0; i < NUMSFX; i++) { if (!strcasecmp(name, S_sfx[i].tagname)) { return i; } } SC_ScriptError("GetSoundOffset: Unknown sound name\n"); return 0; } //========================================================================== // // SN_InitSequenceScript // //========================================================================== void SN_InitSequenceScript(void) { int i, j; int inSequence; int *tempDataStart = NULL; int *tempDataPtr = NULL; inSequence = -1; ActiveSequences = 0; for (i = 0; i < SS_MAX_SCRIPTS; i++) { SequenceData[i] = NULL; } SC_Open(SS_SCRIPT_NAME); while (SC_GetString()) { if (*sc_String == ':') { if (inSequence != -1) { SC_ScriptError("SN_InitSequenceScript: Nested Script Error"); } tempDataStart = (int *) Z_Malloc(SS_TEMPBUFFER_SIZE, PU_STATIC, NULL); memset(tempDataStart, 0, SS_TEMPBUFFER_SIZE); tempDataPtr = tempDataStart; for (i = 0; i < SS_MAX_SCRIPTS; i++) { if (SequenceData[i] == NULL) { break; } } if (i == SS_MAX_SCRIPTS) { I_Error("Number of SS Scripts >= SS_MAX_SCRIPTS"); } for (j = 0; j < SEQ_NUMSEQ; j++) { if (!strcasecmp(SequenceTranslate[j].name, sc_String + 1)) { SequenceTranslate[j].scriptNum = i; inSequence = j; break; } } continue; // parse the next command } if (inSequence == -1) { continue; } if (SC_Compare(SS_STRING_PLAYUNTILDONE)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); *tempDataPtr++ = SS_CMD_WAITUNTILDONE; } else if (SC_Compare(SS_STRING_PLAY)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); } else if (SC_Compare(SS_STRING_PLAYTIME)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); SC_MustGetNumber(); *tempDataPtr++ = SS_CMD_DELAY; *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_PLAYREPEAT)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAYREPEAT; *tempDataPtr++ = GetSoundOffset(sc_String); } else if (SC_Compare(SS_STRING_DELAY)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_DELAY; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_DELAYRAND)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_DELAYRAND; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_VOLUME)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_VOLUME; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_END)) { int dataSize; *tempDataPtr++ = SS_CMD_END; dataSize = (tempDataPtr - tempDataStart) * sizeof(int); SequenceData[i] = (int *) Z_Malloc(dataSize, PU_STATIC, NULL); memcpy(SequenceData[i], tempDataStart, dataSize); Z_Free(tempDataStart); inSequence = -1; } else if (SC_Compare(SS_STRING_STOPSOUND)) { SC_MustGetString(); SequenceTranslate[inSequence].stopSound = GetSoundOffset(sc_String); *tempDataPtr++ = SS_CMD_STOPSOUND; } else { SC_ScriptError("SN_InitSequenceScript: Unknown commmand.\n"); } } } //========================================================================== // // SN_StartSequence // //========================================================================== void SN_StartSequence(mobj_t * mobj, int sequence) { seqnode_t *node; SN_StopSequence(mobj); // Stop any previous sequence node = (seqnode_t *) Z_Malloc(sizeof(seqnode_t), PU_STATIC, NULL); node->sequencePtr = SequenceData[SequenceTranslate[sequence].scriptNum]; node->sequence = sequence; node->mobj = mobj; node->delayTics = 0; node->stopSound = SequenceTranslate[sequence].stopSound; node->volume = 127; // Start at max volume if (!SequenceListHead) { SequenceListHead = node; node->next = node->prev = NULL; } else { SequenceListHead->prev = node; node->next = SequenceListHead; node->prev = NULL; SequenceListHead = node; } ActiveSequences++; return; } //========================================================================== // // SN_StartSequenceName // //========================================================================== void SN_StartSequenceName(mobj_t * mobj, char *name) { int i; for (i = 0; i < SEQ_NUMSEQ; i++) { if (!strcmp(name, SequenceTranslate[i].name)) { SN_StartSequence(mobj, i); return; } } } //========================================================================== // // SN_StopSequence // //========================================================================== void SN_StopSequence(mobj_t * mobj) { seqnode_t *node; for (node = SequenceListHead; node; node = node->next) { if (node->mobj == mobj) { S_StopSound(mobj); if (node->stopSound) { S_StartSoundAtVolume(mobj, node->stopSound, node->volume); } if (SequenceListHead == node) { SequenceListHead = node->next; } if (node->prev) { node->prev->next = node->next; } if (node->next) { node->next->prev = node->prev; } Z_Free(node); ActiveSequences--; } } } //========================================================================== // // SN_UpdateActiveSequences // //========================================================================== void SN_UpdateActiveSequences(void) { seqnode_t *node; boolean sndPlaying; if (!ActiveSequences || paused) { // No sequences currently playing/game is paused return; } for (node = SequenceListHead; node; node = node->next) { if (node->delayTics) { node->delayTics--; continue; } sndPlaying = S_GetSoundPlayingInfo(node->mobj, node->currentSoundID); switch (*node->sequencePtr) { case SS_CMD_PLAY: if (!sndPlaying) { node->currentSoundID = *(node->sequencePtr + 1); S_StartSoundAtVolume(node->mobj, node->currentSoundID, node->volume); } node->sequencePtr += 2; break; case SS_CMD_WAITUNTILDONE: if (!sndPlaying) { node->sequencePtr++; node->currentSoundID = 0; } break; case SS_CMD_PLAYREPEAT: if (!sndPlaying) { node->currentSoundID = *(node->sequencePtr + 1); S_StartSoundAtVolume(node->mobj, node->currentSoundID, node->volume); } break; case SS_CMD_DELAY: node->delayTics = *(node->sequencePtr + 1); node->sequencePtr += 2; node->currentSoundID = 0; break; case SS_CMD_DELAYRAND: node->delayTics = *(node->sequencePtr + 1) + M_Random() % (*(node->sequencePtr + 2) - *(node->sequencePtr + 1)); node->sequencePtr += 2; node->currentSoundID = 0; break; case SS_CMD_VOLUME: node->volume = (127 * (*(node->sequencePtr + 1))) / 100; node->sequencePtr += 2; break; case SS_CMD_STOPSOUND: // Wait until something else stops the sequence break; case SS_CMD_END: SN_StopSequence(node->mobj); break; default: break; } } } //========================================================================== // // SN_StopAllSequences // //========================================================================== void SN_StopAllSequences(void) { seqnode_t *node; for (node = SequenceListHead; node; node = node->next) { node->stopSound = 0; // don't play any stop sounds SN_StopSequence(node->mobj); } } //========================================================================== // // SN_GetSequenceOffset // //========================================================================== int SN_GetSequenceOffset(int sequence, int *sequencePtr) { return (sequencePtr - SequenceData[SequenceTranslate[sequence].scriptNum]); } //========================================================================== // // SN_ChangeNodeData // // nodeNum zero is the first node //========================================================================== void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume, int currentSoundID) { int i; seqnode_t *node; i = 0; node = SequenceListHead; while (node && i < nodeNum) { node = node->next; i++; } if (!node) { // reach the end of the list before finding the nodeNum-th node return; } node->delayTics = delayTics; node->volume = volume; node->sequencePtr += seqOffset; node->currentSoundID = currentSoundID; }