mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 11:40:44 +00:00
0f4abfa4f4
This required taking lots of function prototypes out of functions because they won't get a namespace in there.
2068 lines
49 KiB
C++
2068 lines
49 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 1997, 2005 - 3D Realms Entertainment
|
|
|
|
This file is part of Shadow Warrior version 1.2
|
|
|
|
Shadow Warrior 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.
|
|
|
|
Original Source: 1997 - Frank Maddin and Jim Norwood
|
|
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h"
|
|
#include "compat.h"
|
|
#include "build.h"
|
|
#include "cache1d.h"
|
|
|
|
#include "keys.h"
|
|
|
|
#include "names2.h"
|
|
#include "mytypes.h"
|
|
#include "fx_man.h"
|
|
#include "music.h"
|
|
#include "gamedefs.h"
|
|
#include "config.h"
|
|
|
|
|
|
#include "panel.h"
|
|
#include "game.h"
|
|
#include "sounds.h"
|
|
#include "ai.h"
|
|
#include "network.h"
|
|
|
|
#include "cache.h"
|
|
#include "text.h"
|
|
#include "rts.h"
|
|
#include "menus.h"
|
|
#include "config.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "sdlayer.h"
|
|
#endif
|
|
|
|
BEGIN_SW_NS
|
|
|
|
extern USERp User[MAXSPRITES];
|
|
void DumpSounds(void);
|
|
|
|
// Parentally locked sounds list
|
|
int PLocked_Sounds[] =
|
|
{
|
|
483,328,334,335,338,478,450,454,452,453,456,457,458,455,460,462,
|
|
461,464,466,465,467,463,342,371,254,347,350,432,488,489,490,76,339,
|
|
499,500,506,479,480,481,482,78,600,467,548,547,544,546,545,542,541,540,
|
|
539,536,529,525,522,521,515,516,612,611,589,625,570,569,567,565,
|
|
558,557
|
|
};
|
|
|
|
uint8_t RedBookSong[40] =
|
|
{
|
|
2,4,9,12,10, // Title and ShareWare levels
|
|
5,6,8,11,12,5,10,4,6,9,7,10,8,7,9,10,11,5, // Registered levels
|
|
11,8,7,13,5,6, // Deathmatch levels
|
|
13 // Fight boss
|
|
};
|
|
|
|
// Global vars used by ambient sounds to set spritenum of ambient sounds for later lookups in
|
|
// the sprite array so FAFcansee can know the sound sprite's current sector location
|
|
SWBOOL Use_SoundSpriteNum = FALSE;
|
|
int16_t SoundSpriteNum = -1; // Always set this back to -1 for proper validity checking!
|
|
|
|
SWBOOL MusicInitialized = FALSE;
|
|
SWBOOL FxInitialized = FALSE;
|
|
|
|
void SoundCallBack(unsigned int num);
|
|
SWBOOL LoadSong(const char *track);
|
|
|
|
#define MUSIC_ID -65536
|
|
|
|
#define NUM_SAMPLES 10
|
|
|
|
const char *BitNames[2] =
|
|
{
|
|
"8-bit", "16-bit"
|
|
};
|
|
|
|
const char *ChannelNames[2] =
|
|
{
|
|
"Mono", "Stereo"
|
|
};
|
|
|
|
const char *VoiceNames[8] =
|
|
{
|
|
"1", "2", "3", "4", "5", "6", "7", "8"
|
|
};
|
|
|
|
int music;
|
|
int soundfx;
|
|
int num_voices;
|
|
|
|
int NumSounds = 0;
|
|
|
|
int angle;
|
|
int distance;
|
|
int voice;
|
|
|
|
int loopflag;
|
|
|
|
typedef enum
|
|
{
|
|
SongTypeNone,
|
|
SongTypeMIDI,
|
|
SongTypeWave,
|
|
} SongType_t;
|
|
|
|
char *SongPtr = NULL;
|
|
int SongLength = 0;
|
|
char *SongName = NULL;
|
|
int SongTrack = 0;
|
|
SongType_t SongType = SongTypeNone;
|
|
int SongVoice = -1;
|
|
extern SWBOOL DemoMode;
|
|
|
|
//
|
|
// Includes digi.h to build the table
|
|
//
|
|
|
|
#define DIGI_TABLE
|
|
VOC_INFO voc[] =
|
|
{
|
|
#include "digi.h"
|
|
};
|
|
|
|
#undef DIGI_TABLE
|
|
|
|
//
|
|
// Includes ambient.h to build the table of ambient sounds for game
|
|
//
|
|
|
|
#define AMBIENT_TABLE
|
|
AMB_INFO ambarray[] =
|
|
{
|
|
#include "ambient.h"
|
|
};
|
|
#undef AMBIENT_TABLE
|
|
#define MAX_AMBIENT_SOUNDS 82
|
|
|
|
#define MAXSONGS 10 // This is the max songs per episode
|
|
|
|
SWBOOL OpenSound(VOC_INFOp vp, int *handle, int *length);
|
|
int ReadSound(int handle, VOC_INFOp vp, int length);
|
|
|
|
// 3d sound engine function prototype
|
|
VOC3D_INFOp Insert3DSound(void);
|
|
|
|
#if 0
|
|
// DEBUG
|
|
char *globsndata[DIGI_MAX], *globvpdata[DIGI_MAX];
|
|
int glength[DIGI_MAX];
|
|
#endif
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= My stuff
|
|
=
|
|
===================
|
|
*/
|
|
|
|
int PlayerPainVocs[] =
|
|
{
|
|
DIGI_PLAYERPAIN1,
|
|
DIGI_PLAYERPAIN2,
|
|
DIGI_PLAYERPAIN3,
|
|
DIGI_PLAYERPAIN4,
|
|
DIGI_PLAYERPAIN5
|
|
};
|
|
|
|
// Don't have these sounds yet
|
|
int PlayerLowHealthPainVocs[] =
|
|
{
|
|
DIGI_HURTBAD1,
|
|
DIGI_HURTBAD2,
|
|
DIGI_HURTBAD3,
|
|
DIGI_HURTBAD4,
|
|
DIGI_HURTBAD5
|
|
};
|
|
|
|
int TauntAIVocs[] =
|
|
{
|
|
DIGI_TAUNTAI1,
|
|
DIGI_TAUNTAI2,
|
|
DIGI_TAUNTAI3,
|
|
DIGI_TAUNTAI4,
|
|
DIGI_TAUNTAI5,
|
|
DIGI_TAUNTAI6,
|
|
DIGI_TAUNTAI7,
|
|
DIGI_TAUNTAI8,
|
|
DIGI_TAUNTAI9,
|
|
DIGI_TAUNTAI10,
|
|
DIGI_COWABUNGA,
|
|
DIGI_NOCHARADE,
|
|
DIGI_TIMETODIE,
|
|
DIGI_EATTHIS,
|
|
DIGI_FIRECRACKERUPASS,
|
|
DIGI_HOLYCOW,
|
|
DIGI_HAHA2,
|
|
DIGI_HOLYPEICESOFCOW,
|
|
DIGI_HOLYSHIT,
|
|
DIGI_HOLYPEICESOFSHIT,
|
|
DIGI_PAYINGATTENTION,
|
|
DIGI_EVERYBODYDEAD,
|
|
DIGI_KUNGFU,
|
|
DIGI_HOWYOULIKEMOVE,
|
|
DIGI_HAHA3,
|
|
DIGI_NOMESSWITHWANG,
|
|
DIGI_RAWREVENGE,
|
|
DIGI_YOULOOKSTUPID,
|
|
DIGI_TINYDICK,
|
|
DIGI_NOTOURNAMENT,
|
|
DIGI_WHOWANTSWANG,
|
|
DIGI_MOVELIKEYAK,
|
|
DIGI_ALLINREFLEXES
|
|
};
|
|
|
|
int PlayerGetItemVocs[] =
|
|
{
|
|
DIGI_GOTITEM1,
|
|
DIGI_HAHA1,
|
|
DIGI_BANZAI,
|
|
DIGI_COWABUNGA,
|
|
DIGI_TIMETODIE
|
|
};
|
|
|
|
int PlayerYellVocs[] =
|
|
{
|
|
DIGI_PLAYERYELL1,
|
|
DIGI_PLAYERYELL2,
|
|
DIGI_PLAYERYELL3
|
|
};
|
|
|
|
extern uint8_t lumplockbyte[];
|
|
|
|
#if 0
|
|
// DEBUG
|
|
void CheckSndData(char *file, int line)
|
|
{
|
|
short i;
|
|
|
|
//return;
|
|
|
|
for (i = 0; i<DIGI_MAX; i++)
|
|
{
|
|
if (!globsndata[i] && !globvpdata[i]) continue;
|
|
|
|
if (memcmp(globsndata[i], globvpdata[i], glength[i]) != 0)
|
|
{
|
|
printf("%s %d\n",file,line);
|
|
printf("CheckSndData: Data is not the same! num = %d",i);
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Routine called when a sound is finished playing
|
|
//
|
|
|
|
void
|
|
SoundCallBack(intptr_t num)
|
|
{
|
|
VOC_INFOp vp;
|
|
|
|
if ((int) num == MUSIC_ID)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// RTS sounds are negative
|
|
if ((int)num < 0)
|
|
{
|
|
ASSERT(-num < 11);
|
|
lumplockbyte[-num]--;
|
|
return;
|
|
}
|
|
|
|
vp = &voc[num];
|
|
|
|
// Update counter
|
|
//vp->playing--;
|
|
vp->lock--;
|
|
}
|
|
|
|
//
|
|
|
|
void
|
|
ClearSoundLocks(void)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < SIZ(voc); i++)
|
|
{
|
|
if (voc[i].lock >= 200)
|
|
voc[i].lock = 199;
|
|
}
|
|
|
|
for (i = 0; i < 11; i++)
|
|
{
|
|
if (lumplockbyte[i] >= 200)
|
|
lumplockbyte[i] = 199;
|
|
}
|
|
}
|
|
|
|
void
|
|
UnInitSound(void)
|
|
{
|
|
SoundShutdown();
|
|
MusicShutdown();
|
|
}
|
|
|
|
void
|
|
InitFX(void)
|
|
{
|
|
VOC_INFOp vp;
|
|
short i;
|
|
|
|
#if 0
|
|
// DEBUG
|
|
for (i=0; i<DIGI_MAX; i++)
|
|
{
|
|
globsndata[i] = globvpdata[i] = NULL;
|
|
glength[i] = 0;
|
|
}
|
|
#endif
|
|
|
|
//ExternalSoundMod();
|
|
|
|
// Select which cards to use
|
|
SoundStartup();
|
|
|
|
// Get the current volume of the music and sound fx
|
|
gs.SoundVolume = FX_GetVolume();
|
|
|
|
for (vp = voc; vp < &voc[SIZ(voc)]; vp++)
|
|
{
|
|
vp->playing = 0;
|
|
}
|
|
|
|
// Set up our fx callback so we can display the sounds that are playing
|
|
FX_SetCallBack(SoundCallBack);
|
|
}
|
|
|
|
void
|
|
InitMusic(void)
|
|
{
|
|
// Select which cards to use
|
|
MusicStartup();
|
|
//SendGeneralMidiSysX();
|
|
}
|
|
|
|
void
|
|
ExternalSoundMod(void)
|
|
{
|
|
FILE *fin;
|
|
VOC_INFOp vp;
|
|
char name[40];
|
|
char new_name[40];
|
|
int pri;
|
|
int pitch_lo, pitch_hi;
|
|
int ret;
|
|
|
|
fin = fopen("swextern.snd", "r");
|
|
|
|
if (!fin)
|
|
return;
|
|
|
|
while (TRUE)
|
|
{
|
|
ret = fscanf(fin, "%s %s %d %d", name, new_name, &pitch_lo, &pitch_hi);
|
|
|
|
if (ret == EOF)
|
|
break;
|
|
|
|
for (vp = voc; vp < &voc[SIZ(voc)]; vp++)
|
|
{
|
|
#if 0
|
|
if (!vp->name)
|
|
continue;
|
|
#endif
|
|
|
|
if (!Bstrcasecmp(name, vp->name))
|
|
{
|
|
// vp->priority = pri;
|
|
strcpy(vp->name, new_name);
|
|
vp->pitch_lo = pitch_lo;
|
|
vp->pitch_hi = pitch_hi;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fin);
|
|
}
|
|
|
|
extern short Level;
|
|
|
|
SWBOOL
|
|
PlaySong(char *song_file_name, int cdaudio_track, SWBOOL loop, SWBOOL restart)
|
|
{
|
|
if (!gs.MusicOn)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (DemoMode)
|
|
return FALSE;
|
|
|
|
if (!restart)
|
|
{
|
|
if (SongType == SongTypeWave)
|
|
{
|
|
if (SongTrack > 0 && SongTrack == cdaudio_track)
|
|
{
|
|
// ogg replacement for a CD track
|
|
return TRUE;
|
|
}
|
|
else if (SongName && song_file_name && !strcmp(SongName, song_file_name))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (SongType == SongTypeMIDI)
|
|
{
|
|
if (SongName && song_file_name && !strcmp(SongName, song_file_name))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
StopSong();
|
|
|
|
if (!SW_SHAREWARE)
|
|
{
|
|
if (cdaudio_track >= 0)
|
|
{
|
|
char waveformtrack[MAXWAVEFORMTRACKLENGTH];
|
|
Bstrncpy(waveformtrack, gs.WaveformTrackName, MAXWAVEFORMTRACKLENGTH - 1);
|
|
|
|
char *numPos = Bstrstr(waveformtrack, "??");
|
|
|
|
if (numPos && (numPos-waveformtrack) < MAXWAVEFORMTRACKLENGTH - 2)
|
|
{
|
|
static const char *tracktypes[] = { ".flac", ".ogg" };
|
|
const size_t tracknamebaselen = Bstrlen(waveformtrack);
|
|
size_t i;
|
|
|
|
numPos[0] = '0' + (cdaudio_track / 10) % 10;
|
|
numPos[1] = '0' + cdaudio_track % 10;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tracktypes); ++i)
|
|
{
|
|
waveformtrack[tracknamebaselen] = '\0';
|
|
Bstrncat(waveformtrack, tracktypes[i], MAXWAVEFORMTRACKLENGTH - 1);
|
|
|
|
if (LoadSong(waveformtrack))
|
|
{
|
|
SongVoice = FX_Play(SongPtr, SongLength, 0, 0, 0,
|
|
255, 255, 255, FX_MUSIC_PRIORITY, 1.f, MUSIC_ID);
|
|
if (SongVoice > FX_Ok)
|
|
{
|
|
SongType = SongTypeWave;
|
|
SongTrack = cdaudio_track;
|
|
SongName = Bstrdup(waveformtrack);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
buildprintf("Can't find CD track %i!\n", cdaudio_track);
|
|
}
|
|
else
|
|
{
|
|
buildprintf("Make sure to have \"??\" as a placeholder for the track number in your WaveformTrackName!\n");
|
|
buildprintf(" e.g. WaveformTrackName = \"MUSIC/Track??\"\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!song_file_name || !LoadSong(song_file_name))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!memcmp(SongPtr, "MThd", 4))
|
|
{
|
|
MUSIC_PlaySong(SongPtr, SongLength, MUSIC_LoopSong);
|
|
SongType = SongTypeMIDI;
|
|
SongName = strdup(song_file_name);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
SongVoice = FX_Play(SongPtr, SongLength, 0, 0, 0,
|
|
255, 255, 255, FX_MUSIC_PRIORITY, 1.f, MUSIC_ID);
|
|
if (SongVoice > FX_Ok)
|
|
{
|
|
SongType = SongTypeWave;
|
|
SongName = strdup(song_file_name);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
StopFX(void)
|
|
{
|
|
FX_StopAllSounds();
|
|
}
|
|
|
|
void
|
|
StopSong(void)
|
|
{
|
|
if (DemoMode)
|
|
return;
|
|
|
|
if (SongType == SongTypeWave && SongVoice >= 0)
|
|
{
|
|
FX_StopSound(SongVoice);
|
|
}
|
|
else if (SongType == SongTypeMIDI)
|
|
{
|
|
MUSIC_StopSong();
|
|
}
|
|
SongType = SongTypeNone;
|
|
|
|
DO_FREE_AND_NULL(SongName);
|
|
SongTrack = 0;
|
|
|
|
if (SongPtr)
|
|
{
|
|
FreeMem(SongPtr);
|
|
SongPtr = 0;
|
|
SongLength = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
PauseSong(SWBOOL pauseon)
|
|
{
|
|
if (!gs.MusicOn) return;
|
|
|
|
if (SongType == SongTypeWave && SongVoice >= 0)
|
|
{
|
|
FX_PauseVoice(SongVoice, pauseon);
|
|
}
|
|
}
|
|
|
|
void
|
|
SetSongVolume(int volume)
|
|
{
|
|
}
|
|
|
|
SWBOOL
|
|
SongIsPlaying(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
StopSound(void)
|
|
{
|
|
StopFX();
|
|
StopSong();
|
|
}
|
|
|
|
//
|
|
// Sound Distance Calculation
|
|
//
|
|
|
|
#define MAXLEVLDIST 19000 // The higher the number, the further away you can hear sound
|
|
|
|
short
|
|
SoundDist(int x, int y, int z, int basedist)
|
|
{
|
|
double tx, ty, tz;
|
|
double sqrdist,retval;
|
|
double decay,decayshift;
|
|
extern short screenpeek;
|
|
|
|
#define DECAY_CONST 4000
|
|
|
|
|
|
tx = fabs(Player[screenpeek].posx - x);
|
|
ty = fabs(Player[screenpeek].posy - y);
|
|
tz = fabs((Player[screenpeek].posz - z) >> 4);
|
|
|
|
// Use the Pythagreon Theorem to compute the magnitude of a 3D vector
|
|
sqrdist = fabs(tx*tx + ty*ty + tz*tz);
|
|
retval = sqrt(sqrdist);
|
|
|
|
if (basedist < 0) // if basedist is negative
|
|
{
|
|
short i;
|
|
|
|
decayshift=2;
|
|
decay = labs(basedist) / DECAY_CONST;
|
|
|
|
for (i=0; i<decay; i++)
|
|
decayshift *= 2;
|
|
|
|
if (fabs(double(basedist)/decayshift) >= retval)
|
|
retval = 0;
|
|
else
|
|
retval *= decay;
|
|
}
|
|
else
|
|
{
|
|
if (basedist > retval)
|
|
retval = 0;
|
|
else
|
|
retval -= basedist;
|
|
}
|
|
|
|
retval = retval * 256 / MAXLEVLDIST;
|
|
|
|
if (retval < 0) retval = 0;
|
|
if (retval > 255) retval = 255;
|
|
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// Angle calcuations - may need to be checked to make sure they are right
|
|
//
|
|
|
|
short
|
|
SoundAngle(int x, int y)
|
|
{
|
|
extern short screenpeek;
|
|
|
|
short angle, delta_angle;
|
|
|
|
angle = getangle(x - Player[screenpeek].posx, y - Player[screenpeek].posy);
|
|
|
|
delta_angle = GetDeltaAngle(angle, Player[screenpeek].pang);
|
|
|
|
// convert a delta_angle to a real angle if negative
|
|
if (delta_angle < 0)
|
|
delta_angle = NORM_ANGLE((1024 + delta_angle) + 1024);
|
|
|
|
// convert 2048 degree angle to 32 degree angle
|
|
return delta_angle >> 6;
|
|
}
|
|
|
|
int _PlayerSound(const char *file, int line, int num, int *x, int *y, int *z, Voc3D_Flags flags, PLAYERp pp)
|
|
//PlayerSound(int num, int *x, int *y, int *z, Voc3D_Flags flags, PLAYERp pp)
|
|
{
|
|
int handle;
|
|
VOC_INFOp vp;
|
|
|
|
if (Prediction)
|
|
return 0;
|
|
|
|
if (pp < Player || pp >= Player + MAX_SW_PLAYERS)
|
|
{
|
|
TerminateGame();
|
|
printf("Player Sound invalid player: file %s, line %d\n",file,line);
|
|
exit(0);
|
|
}
|
|
|
|
PRODUCTION_ASSERT(pp >= Player && pp < Player+MAX_SW_PLAYERS);
|
|
PRODUCTION_ASSERT(num >= 0 && num < DIGI_MAX);
|
|
|
|
if (TEST(pp->Flags, PF_DEAD)) return 0; // You're dead, no talking!
|
|
|
|
// If this is a player voice and he's already yacking, forget it.
|
|
vp = &voc[num];
|
|
if (vp == NULL)
|
|
{
|
|
TerminateGame();
|
|
printf("vp == NULL in PlayerSound, num = %d\n",num);
|
|
exit(0);
|
|
}
|
|
|
|
// Not a player voice, bail.
|
|
if (vp->priority != PRI_PLAYERVOICE && vp->priority != PRI_PLAYERDEATH)
|
|
return 0;
|
|
|
|
// He wasn't talking, but he will be now.
|
|
if (!pp->PlayerTalking)
|
|
{
|
|
pp->PlayerTalking = TRUE;
|
|
pp->TalkVocnum = num; // Set the voc number
|
|
pp->TalkVocHandle = PlaySound(num, x, y, z, flags); // Play the sound
|
|
if (pp->TalkVocHandle < 0)
|
|
{
|
|
pp->PlayerTalking = FALSE;
|
|
pp->TalkVocnum = -1;
|
|
pp->TalkVocHandle = -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void LockSound(int num)
|
|
{
|
|
VOC_INFOp vp = &voc[num];
|
|
// if data is not locked
|
|
if (vp->lock <= CACHE_UNLOCK_MAX)
|
|
{
|
|
vp->lock = CACHE_LOCK_START;
|
|
}
|
|
else
|
|
// if data is already locked
|
|
{
|
|
vp->lock++;
|
|
if (vp->lock >= CACHE_LOCK_MAX || vp->lock == 0)
|
|
{
|
|
DumpSounds();
|
|
TerminateGame();
|
|
printf("lock > MAX, num = %d",num);
|
|
exit(0);
|
|
}
|
|
//ASSERT(vp->lock < CACHE_LOCK_MAX);
|
|
//ASSERT(vp->lock != 0);
|
|
}
|
|
}
|
|
|
|
SWBOOL CacheSound(int num, int type)
|
|
{
|
|
VOC_INFOp vp = &voc[num];
|
|
|
|
PRODUCTION_ASSERT(num >= 0 && num < DIGI_MAX);
|
|
|
|
// if no data we need to cache it in
|
|
if (!vp->data)
|
|
{
|
|
int handle;
|
|
int length;
|
|
|
|
if (!OpenSound(vp, &handle, &length))
|
|
{
|
|
sprintf(ds,"Could not open sound %s, num %d, priority %d\n",vp->name,num,vp->priority);
|
|
CON_ConMessage("%s", ds);
|
|
return FALSE;
|
|
}
|
|
|
|
if (vp != NULL)
|
|
{
|
|
//FILE *fp;
|
|
|
|
/*
|
|
if (type == CACHE_SOUND_PLAY)
|
|
// start it out locked at the min
|
|
vp->lock = CACHE_LOCK_START;
|
|
else
|
|
if (type == CACHE_SOUND_PRECACHE)
|
|
// start it out unlocked at the max
|
|
*/
|
|
vp->lock = CACHE_UNLOCK_MAX;
|
|
|
|
cacheAllocateBlock((intptr_t*)&vp->data, length, &vp->lock);
|
|
|
|
#if 0
|
|
// DEBUG
|
|
globsndata[num] = AllocMem(length);
|
|
glength[num] = length;
|
|
|
|
fp = fopen(vp->name, "rb");
|
|
if (fp != NULL)
|
|
{
|
|
fread(globsndata[num], length, 1, fp);
|
|
ASSERT(globsndata[num] != NULL);
|
|
fclose(fp);
|
|
}
|
|
#endif
|
|
///////
|
|
|
|
ASSERT(vp->data);
|
|
ReadSound(handle, vp, length);
|
|
|
|
#if 0
|
|
//DEBUG
|
|
globvpdata[num] = vp->data;
|
|
CheckSndData(__FILE__, __LINE__);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Play a sound
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define SOUND_UNIT MAXLEVLDIST/255
|
|
// NOTE: If v3df_follow == 1, x,y,z are considered literal coordinates
|
|
int
|
|
PlaySound(int num, int *x, int *y, int *z, Voc3D_Flags flags)
|
|
{
|
|
VOC_INFOp vp;
|
|
VOC3D_INFOp v3p;
|
|
int pitch = 0;
|
|
short angle, sound_dist;
|
|
int tx, ty, tz;
|
|
uint8_t priority;
|
|
SPRITEp sp=NULL;
|
|
|
|
// DEBUG
|
|
//extern SWBOOL Pachinko_Win_Cheat;
|
|
|
|
|
|
// Don't play game sounds when in menus
|
|
//if (UsingMenus && (*x!=0 || *y!=0 || *z!=0)) return(-1);
|
|
|
|
// Weed out parental lock sounds if PLock is active
|
|
if (gs.ParentalLock || Global_PLock)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i=0; i<sizeof(PLocked_Sounds); i++)
|
|
{
|
|
if (num == PLocked_Sounds[i])
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (Prediction)
|
|
return -1;
|
|
|
|
if (!gs.FxOn)
|
|
return -1;
|
|
|
|
PRODUCTION_ASSERT(num >= 0 && num < DIGI_MAX);
|
|
|
|
// Reset voice
|
|
voice = -1;
|
|
|
|
// This is used for updating looping sounds in Update3DSounds
|
|
if (Use_SoundSpriteNum && SoundSpriteNum >= 0)
|
|
{
|
|
ASSERT(SoundSpriteNum >= 0 && SoundSpriteNum < MAXSPRITES);
|
|
sp = &sprite[SoundSpriteNum];
|
|
}
|
|
|
|
if (gs.Ambient && TEST(flags,v3df_ambient) && !TEST(flags,v3df_nolookup)) // Look for invalid ambient numbers
|
|
{
|
|
if (num < 0 || num > MAX_AMBIENT_SOUNDS)
|
|
{
|
|
sprintf(ds,"Invalid or out of range ambient sound number %d\n",num);
|
|
PutStringInfo(Player+screenpeek, ds);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
// Call queue management to add sound to play list.
|
|
// 3D sound manager will update playing sound 10x per second until
|
|
// the sound ends, at which time it is removed from both the 3D
|
|
// sound list as well as the actual cache.
|
|
v3p = Insert3DSound();
|
|
|
|
// If the ambient flag is set, do a name conversion to point to actual
|
|
// digital sound entry.
|
|
v3p->num = num;
|
|
v3p->priority = 0;
|
|
v3p->FX_Ok = FALSE; // Hasn't played yet
|
|
|
|
if (gs.Ambient && TEST(flags,v3df_ambient) && !TEST(flags,v3df_nolookup))
|
|
{
|
|
v3p->maxtics = STD_RANDOM_RANGE(ambarray[num].maxtics);
|
|
flags |= ambarray[num].ambient_flags; // Add to flags if any
|
|
num = ambarray[num].diginame;
|
|
}
|
|
|
|
PRODUCTION_ASSERT(num >= 0 && num < DIGI_MAX);
|
|
|
|
|
|
// Assign voc to voc pointer
|
|
vp = &voc[num];
|
|
if (UsingMenus && *x==0 && *y==0 && *z==0) // Menus sound outdo everything
|
|
priority = 100;
|
|
else
|
|
priority = vp->priority;
|
|
v3p->vp = vp;
|
|
|
|
// Assign voc info to 3d struct for future reference
|
|
v3p->x = x;
|
|
v3p->y = y;
|
|
v3p->z = z;
|
|
v3p->fx = *x;
|
|
v3p->fy = *y;
|
|
v3p->fz = *z;
|
|
v3p->flags = flags;
|
|
|
|
if (flags & v3df_follow)
|
|
{
|
|
tx = *x;
|
|
ty = *y;
|
|
if (!z)
|
|
tz = 0; // Some sound calls don't have a z
|
|
// value
|
|
else
|
|
tz = *z;
|
|
}
|
|
else
|
|
{
|
|
// Don't use pointers to coordinate values.
|
|
tx = v3p->fx;
|
|
ty = v3p->fy;
|
|
tz = v3p->fz;
|
|
}
|
|
|
|
// Special case stuff for sounds being played in a level
|
|
if (*x==0 && *y==0 && *z==0)
|
|
tx = ty = tz = 0;
|
|
|
|
if ((vp->voc_flags & vf_loop) && Use_SoundSpriteNum && SoundSpriteNum >= 0 && sp)
|
|
{
|
|
tx=sp->x;
|
|
ty=sp->y;
|
|
tz=sp->z;
|
|
//CON_Message("Using sp to set tx=%ld,ty=%ld,tz=%ld",tx,ty,tz);
|
|
}
|
|
|
|
// Calculate sound angle
|
|
if (flags & v3df_dontpan) // If true, don't do panning
|
|
angle = 0;
|
|
else
|
|
angle = SoundAngle(tx, ty);
|
|
|
|
// Calculate sound distance
|
|
if (tx == 0 && ty == 0 && tz == 0)
|
|
sound_dist = 255; // Special case for menus sounds,etc.
|
|
else
|
|
sound_dist = SoundDist(tx, ty, tz, vp->voc_distance);
|
|
|
|
v3p->doplr_delta = sound_dist; // Save of distance for doppler
|
|
// effect
|
|
|
|
// //DSPRINTF(ds,"sound dist = %d\n",sound_dist);
|
|
// MONO_PRINT(ds);
|
|
|
|
// Can the ambient sound see the player? If not, tone it down some.
|
|
if ((vp->voc_flags & vf_loop) && Use_SoundSpriteNum && SoundSpriteNum >= 0)
|
|
{
|
|
PLAYERp pp = Player+screenpeek;
|
|
|
|
//MONO_PRINT("PlaySound:Checking sound cansee");
|
|
if (!FAFcansee(tx, ty, tz, sp->sectnum,pp->posx, pp->posy, pp->posz, pp->cursectnum))
|
|
{
|
|
//MONO_PRINT("PlaySound:Reducing sound distance");
|
|
sound_dist += ((sound_dist/2)+(sound_dist/4)); // Play more quietly
|
|
if (sound_dist > 255) sound_dist = 255;
|
|
|
|
// Special Cases
|
|
if (num == DIGI_WHIPME) sound_dist = 255;
|
|
}
|
|
}
|
|
|
|
// Assign ambient priorities based on distance
|
|
if (gs.Ambient && TEST(flags, v3df_ambient))
|
|
{
|
|
v3p->priority = v3p->vp->priority - (sound_dist / 26);
|
|
priority = v3p->priority;
|
|
}
|
|
|
|
if (!CacheSound(num, CACHE_SOUND_PLAY))
|
|
{
|
|
v3p->flags = v3df_kill;
|
|
v3p->handle = -1;
|
|
v3p->dist = 0;
|
|
v3p->deleted = TRUE; // Sound init failed, remove it!
|
|
return -1;
|
|
}
|
|
|
|
LockSound(num);
|
|
|
|
if (sound_dist < 5)
|
|
angle = 0;
|
|
|
|
// Check for pitch bending
|
|
if (vp->pitch_lo > vp->pitch_hi)
|
|
ASSERT(vp->pitch_lo <= vp->pitch_hi);
|
|
|
|
if (vp->pitch_hi == vp->pitch_lo)
|
|
pitch = vp->pitch_lo;
|
|
else if (vp->pitch_hi != vp->pitch_lo)
|
|
pitch = vp->pitch_lo + (STD_RANDOM_RANGE(vp->pitch_hi - vp->pitch_lo));
|
|
|
|
#if 0
|
|
// DEBUG
|
|
if (Pachinko_Win_Cheat)
|
|
{
|
|
CheckSndData(__FILE__, __LINE__);
|
|
Pachinko_Win_Cheat = FALSE;
|
|
CON_Message("S O U N D S C H E C K E D");
|
|
}
|
|
#endif
|
|
|
|
// Request playback and play it as a looping sound if flag is set.
|
|
if (vp->voc_flags & vf_loop)
|
|
{
|
|
short loopvol=0;
|
|
|
|
if ((loopvol = 255-sound_dist) <= 0)
|
|
loopvol = 0;
|
|
|
|
if (sound_dist < 255 || (flags & v3df_init))
|
|
{
|
|
voice = FX_Play((char *)vp->data, vp->datalen, 0, 0,
|
|
pitch, loopvol, loopvol, loopvol, priority, 1.f, num); // [JM] Should probably utilize floating point volume. !CHECKME!
|
|
}
|
|
else
|
|
voice = -1;
|
|
|
|
}
|
|
else
|
|
//if(!flags & v3df_init) // If not initing sound, play it
|
|
if (tx==0 && ty==0 && tz==0) // It's a non-inlevel sound
|
|
{
|
|
voice = FX_Play((char *)vp->data, vp->datalen, -1, -1, pitch, 255, 255, 255, priority, 1.f, num); // [JM] And here !CHECKME!
|
|
}
|
|
else // It's a 3d sound
|
|
{
|
|
if (sound_dist < 255)
|
|
{
|
|
voice = FX_Play3D((char *)vp->data, vp->datalen, FX_ONESHOT, pitch, angle, sound_dist, priority, 1.f, num); // [JM] And here !CHECKME!
|
|
}
|
|
else
|
|
voice = -1;
|
|
}
|
|
|
|
// If sound played, update our counter
|
|
if (voice > FX_Ok)
|
|
{
|
|
//vp->playing++;
|
|
v3p->FX_Ok = TRUE;
|
|
}
|
|
else
|
|
{
|
|
vp->lock--;
|
|
}
|
|
|
|
// Assign voc info to 3d struct for future reference
|
|
v3p->handle = voice; // Save the current voc handle in struct
|
|
v3p->dist = sound_dist;
|
|
v3p->tics = 0; // Reset tics
|
|
if (flags & v3df_init)
|
|
v3p->flags ^= v3df_init; // Turn init off now
|
|
|
|
return voice;
|
|
}
|
|
|
|
void PlaySoundRTS(int rts_num)
|
|
{
|
|
char *rtsptr;
|
|
int voice=-1;
|
|
|
|
if (RTS_NumSounds() <= 0 || !gs.FxOn)
|
|
return;
|
|
|
|
rtsptr = (char *)RTS_GetSound(rts_num - 1);
|
|
|
|
ASSERT(rtsptr);
|
|
|
|
voice = FX_Play3D(rtsptr, RTS_SoundLength(rts_num - 1), FX_ONESHOT, 0, 0, 0, 255, 1.f, -rts_num); // [JM] Float volume here too I bet. !CHECKME!
|
|
|
|
if (voice <= FX_Ok)
|
|
{
|
|
lumplockbyte[rts_num]--;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
SWBOOL
|
|
OpenSound(VOC_INFOp vp, int *handle, int *length)
|
|
{
|
|
*handle = kopen4load(vp->name, 0);
|
|
|
|
if (*handle == -1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*length = kfilelength(*handle);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
ReadSound(int handle, VOC_INFOp vp, int length)
|
|
{
|
|
if (kread(handle, vp->data, length) != length)
|
|
{
|
|
TerminateGame();
|
|
printf("Error reading file '%s'.\n", vp->name);
|
|
exit(0);
|
|
}
|
|
|
|
vp->datalen = length;
|
|
|
|
kclose(handle);
|
|
return 0;
|
|
}
|
|
|
|
SWBOOL
|
|
LoadSong(const char *filename)
|
|
{
|
|
int handle;
|
|
int size;
|
|
char *ptr;
|
|
|
|
if ((handle = kopen4load(filename, 0)) == -1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
size = kfilelength(handle);
|
|
|
|
ptr = (char *) AllocMem(size);
|
|
if (ptr == NULL)
|
|
{
|
|
kclose(handle);
|
|
return FALSE;
|
|
}
|
|
|
|
if (kread(handle, ptr, size) != size)
|
|
{
|
|
FreeMem(ptr);
|
|
kclose(handle);
|
|
return FALSE;
|
|
}
|
|
|
|
kclose(handle);
|
|
|
|
SongPtr = ptr;
|
|
SongLength = size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void FlipStereo(void)
|
|
{
|
|
FX_SetReverseStereo(gs.FlipStereo);
|
|
}
|
|
|
|
void
|
|
SoundStartup(void)
|
|
{
|
|
int32_t status;
|
|
void *initdata = 0;
|
|
|
|
// if they chose None lets return
|
|
if (FXDevice < 0)
|
|
{
|
|
gs.FxOn = FALSE;
|
|
return;
|
|
}
|
|
|
|
#ifdef MIXERTYPEWIN
|
|
initdata = (void *) win_gethwnd();
|
|
#endif
|
|
|
|
//gs.FxOn = TRUE;
|
|
|
|
status = FX_Init(NumVoices, NumChannels, MixRate, initdata);
|
|
if (status == FX_Ok)
|
|
{
|
|
FxInitialized = TRUE;
|
|
FX_SetVolume(gs.SoundVolume);
|
|
|
|
if (gs.FlipStereo)
|
|
FX_SetReverseStereo(!FX_GetReverseStereo());
|
|
}
|
|
if (status != FX_Ok)
|
|
{
|
|
buildprintf("Sound error: %s\n",FX_ErrorString(FX_Error));
|
|
}
|
|
|
|
FX_SetCallBack(SoundCallBack);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= SoundShutdown
|
|
=
|
|
===================
|
|
*/
|
|
|
|
void
|
|
SoundShutdown(void)
|
|
{
|
|
int32_t status;
|
|
|
|
// if they chose None lets return
|
|
if (FXDevice < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!FxInitialized)
|
|
return;
|
|
|
|
status = FX_Shutdown();
|
|
if (status != FX_Ok)
|
|
{
|
|
buildprintf("Sound error: %s\n",FX_ErrorString(FX_Error));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= MusicStartup
|
|
=
|
|
===================
|
|
*/
|
|
|
|
#if 0
|
|
void loadtmb(void)
|
|
{
|
|
char tmb[8000];
|
|
int fil, l;
|
|
|
|
fil = kopen4load("swtimbr.tmb",0);
|
|
if (fil == -1)
|
|
return;
|
|
|
|
l = min((size_t)kfilelength(fil), sizeof(tmb));
|
|
kread(fil,tmb,l);
|
|
MUSIC_RegisterTimbreBank(tmb);
|
|
kclose(fil);
|
|
}
|
|
#endif
|
|
|
|
void MusicStartup(void)
|
|
{
|
|
// if they chose None lets return
|
|
if (MusicDevice < 0)
|
|
{
|
|
gs.MusicOn = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (MUSIC_Init(0, 0) == MUSIC_Ok || MUSIC_Init(1, 0) == MUSIC_Ok)
|
|
{
|
|
MusicInitialized = TRUE;
|
|
MUSIC_SetVolume(gs.MusicVolume);
|
|
}
|
|
else
|
|
{
|
|
buildprintf("Music error: %s\n",MUSIC_ErrorString(MUSIC_ErrorCode));
|
|
gs.MusicOn = FALSE;
|
|
}
|
|
|
|
#if 0
|
|
if (MusicInitialized)
|
|
loadtmb();
|
|
#endif
|
|
}
|
|
|
|
void COVER_SetReverb(int amt)
|
|
{
|
|
FX_SetReverb(amt);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= MusicShutdown
|
|
=
|
|
===================
|
|
*/
|
|
|
|
void
|
|
MusicShutdown(void)
|
|
{
|
|
int32_t status;
|
|
|
|
// if they chose None lets return
|
|
if (MusicDevice < 0)
|
|
return;
|
|
|
|
if (!MusicInitialized)
|
|
return;
|
|
|
|
StopSong();
|
|
|
|
status = MUSIC_Shutdown();
|
|
if (status != MUSIC_Ok)
|
|
{
|
|
buildprintf("Music error: %s\n",MUSIC_ErrorString(MUSIC_ErrorCode));
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
//
|
|
// 3D sound engine
|
|
// Sound management routines that keep a list of
|
|
// all sounds being played in a level.
|
|
// Doppler and Panning effects are achieved here.
|
|
//
|
|
///////////////////////////////////////////////
|
|
// Declare and initialize linked list of vocs.
|
|
VOC3D_INFOp voc3dstart = NULL;
|
|
VOC3D_INFOp voc3dend = NULL;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////
|
|
// Initialize new vocs in the 3D sound queue
|
|
///////////////////////////////////////////////
|
|
VOC3D_INFOp
|
|
InitNew3DSound(VOC3D_INFOp v3p)
|
|
{
|
|
v3p->handle = -1; // Initialize handle to new sound
|
|
// value
|
|
v3p->owner = -1;
|
|
v3p->deleted = FALSE; // Used for when sound gets deleted
|
|
|
|
return v3p;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Inserts new vocs in the 3D sound queue
|
|
///////////////////////////////////////////////
|
|
VOC3D_INFOp
|
|
Insert3DSound(void)
|
|
{
|
|
VOC3D_INFOp vp, old;
|
|
|
|
// Allocate memory for new sound
|
|
// You can allocate new sounds as long as memory holds out.
|
|
// If you run out of memory for sounds, you got problems anyway.
|
|
vp = (VOC3D_INFOp) AllocMem(sizeof(VOC3D_INFO));
|
|
ASSERT(vp != NULL);
|
|
memset(vp,0xCC,sizeof(VOC3D_INFO)); // Zero out the memory
|
|
|
|
if (!voc3dend) // First item in list
|
|
{
|
|
vp->next = vp->prev = NULL;
|
|
voc3dend = vp;
|
|
voc3dstart = vp;
|
|
InitNew3DSound(vp);
|
|
return vp;
|
|
}
|
|
|
|
old = voc3dend; // Put it on the end
|
|
old->next = vp;
|
|
vp->next = NULL;
|
|
vp->prev = old;
|
|
voc3dend = vp;
|
|
|
|
InitNew3DSound(vp);
|
|
return vp;
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Deletes vocs in the 3D sound queue with no owners
|
|
/////////////////////////////////////////////////////
|
|
void
|
|
DeleteNoSoundOwner(short spritenum)
|
|
{
|
|
VOC3D_INFOp vp, dp;
|
|
|
|
vp = voc3dstart;
|
|
|
|
while (vp)
|
|
{
|
|
dp = NULL;
|
|
if (vp->owner == spritenum && vp->owner >= 0 && (vp->vp->voc_flags & vf_loop))
|
|
{
|
|
//DSPRINTF(ds,"Deleting owner %d\n",vp->owner);
|
|
//MONO_PRINT(ds);
|
|
|
|
// Make sure to stop active
|
|
// sounds
|
|
if (FX_SoundActive(vp->handle))
|
|
{
|
|
FX_StopSound(vp->handle);
|
|
}
|
|
|
|
#if 0
|
|
// Clean up the sound active counter for cacheing locks
|
|
if (vp->FX_Ok) // Only decrement if sound ever played
|
|
vp->vp->playing--; // Decrement instance of sound playing
|
|
if (vp->vp->playing == 0 && vp->vp->lock > CACHE_UNLOCK_MAX)
|
|
vp->vp->lock = CACHE_UNLOCK_MAX;
|
|
#endif
|
|
|
|
|
|
dp = vp; // Point to sound to be deleted
|
|
|
|
if (vp->prev)
|
|
{
|
|
vp->prev->next = vp->next;
|
|
}
|
|
else
|
|
{
|
|
voc3dstart = vp->next; // New first item
|
|
if (voc3dstart)
|
|
voc3dstart->prev = NULL;
|
|
}
|
|
|
|
if (vp->next)
|
|
{
|
|
vp->next->prev = vp->prev; // Middle element
|
|
}
|
|
else
|
|
{
|
|
voc3dend = vp->prev; // Delete last element
|
|
}
|
|
}
|
|
|
|
vp = vp->next;
|
|
|
|
if (dp != NULL)
|
|
FreeMem(dp); // Return memory to heap
|
|
}
|
|
}
|
|
|
|
// This is called from KillSprite to kill a follow sound with no valid sprite owner
|
|
// Stops and active sound with the follow bit set, even play once sounds.
|
|
void DeleteNoFollowSoundOwner(short spritenum)
|
|
{
|
|
VOC3D_INFOp vp, dp;
|
|
SPRITEp sp = &sprite[spritenum];
|
|
|
|
vp = voc3dstart;
|
|
|
|
while (vp)
|
|
{
|
|
dp = NULL;
|
|
// If the follow flag is set, compare the x and y addresses.
|
|
if ((vp->flags & v3df_follow) && vp->x == &sp->x && vp->y == &sp->y)
|
|
{
|
|
if (FX_SoundActive(vp->handle))
|
|
{
|
|
FX_StopSound(vp->handle);
|
|
}
|
|
|
|
#if 0
|
|
if (vp->FX_Ok) // Only decrement if sound ever played
|
|
vp->vp->playing--; // Decrement instance of sound playing
|
|
if (vp->vp->playing == 0 && vp->vp->lock > CACHE_UNLOCK_MAX)
|
|
vp->vp->lock = CACHE_UNLOCK_MAX;
|
|
#endif
|
|
|
|
dp = vp; // Point to sound to be deleted
|
|
|
|
if (vp->prev)
|
|
{
|
|
vp->prev->next = vp->next;
|
|
}
|
|
else
|
|
{
|
|
voc3dstart = vp->next; // New first item
|
|
if (voc3dstart)
|
|
voc3dstart->prev = NULL;
|
|
}
|
|
|
|
if (vp->next)
|
|
{
|
|
vp->next->prev = vp->prev; // Middle element
|
|
}
|
|
else
|
|
{
|
|
voc3dend = vp->prev; // Delete last element
|
|
}
|
|
}
|
|
|
|
vp = vp->next;
|
|
|
|
if (dp != NULL)
|
|
FreeMem(dp); // Return memory to heap
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Deletes vocs in the 3D sound queue
|
|
///////////////////////////////////////////////
|
|
void
|
|
Delete3DSounds(void)
|
|
{
|
|
VOC3D_INFOp vp, dp;
|
|
PLAYERp pp;
|
|
int cnt=0;
|
|
|
|
|
|
vp = voc3dstart;
|
|
|
|
while (vp)
|
|
{
|
|
dp = NULL;
|
|
if (vp->deleted)
|
|
{
|
|
#if 0
|
|
if (vp->FX_Ok) // Only decrement if sound ever played
|
|
vp->vp->playing--; // Decrement instance of sound playing
|
|
if (vp->vp->playing == 0 && vp->vp->lock > CACHE_UNLOCK_MAX)
|
|
vp->vp->lock = CACHE_UNLOCK_MAX;
|
|
#endif
|
|
|
|
//DSPRINTF(ds,"Delete3DSounds: deleting owner %d digi %d\n",vp->owner,vp->num);
|
|
//MONO_PRINT(ds);
|
|
// Reset Player talking flag if a voice was deleted
|
|
//if(vp->num > DIGI_FIRSTPLAYERVOICE && vp->num < DIGI_LASTPLAYERVOICE)
|
|
if (!vp->vp)
|
|
{
|
|
printf("Delete3DSounds(): NULL vp->vp\n");
|
|
}
|
|
else // JBF: added null check
|
|
if (vp->vp->priority == PRI_PLAYERVOICE || vp->vp->priority == PRI_PLAYERDEATH)
|
|
{
|
|
int16_t pnum;
|
|
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = &Player[pnum];
|
|
|
|
if (vp->num == pp->TalkVocnum)
|
|
{
|
|
pp->PlayerTalking = FALSE;
|
|
pp->TalkVocnum = -1;
|
|
pp->TalkVocHandle = -1;
|
|
//DSPRINTF(ds,"DELETED PLAYER VOICE VOC! NUM=%d\n",vp->num);
|
|
//MONO_PRINT(ds);
|
|
}
|
|
}
|
|
}
|
|
|
|
dp = vp; // Point to sound to be deleted
|
|
if (vp->prev)
|
|
{
|
|
vp->prev->next = vp->next;
|
|
}
|
|
else
|
|
{
|
|
voc3dstart = vp->next; // New first item
|
|
if (voc3dstart)
|
|
voc3dstart->prev = NULL;
|
|
}
|
|
|
|
if (vp->next)
|
|
{
|
|
vp->next->prev = vp->prev; // Middle element
|
|
}
|
|
else
|
|
{
|
|
voc3dend = vp->prev; // Delete last element
|
|
}
|
|
}
|
|
|
|
vp = vp->next;
|
|
|
|
if (dp != NULL)
|
|
{
|
|
FreeMem(dp); // Return memory to heap
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Play a sound
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
RandomizeAmbientSpecials(int handle)
|
|
{
|
|
#define MAXRNDAMB 12
|
|
int ambrand[] =
|
|
{
|
|
56,57,58,59,60,61,62,63,64,65,66,67
|
|
};
|
|
short i;
|
|
|
|
// If ambient sound is found in the array, randomly pick a new sound
|
|
for (i=0; i<MAXRNDAMB; i++)
|
|
{
|
|
if (handle == ambrand[i])
|
|
return ambrand[STD_RANDOM_RANGE(MAXRNDAMB-1)];
|
|
}
|
|
|
|
return handle; // Give back the sound, no new one was found
|
|
}
|
|
|
|
void
|
|
DoTimedSound(VOC3D_INFOp p)
|
|
{
|
|
p->tics += synctics;
|
|
|
|
if (p->tics >= p->maxtics)
|
|
{
|
|
if (!FX_SoundActive(p->handle))
|
|
{
|
|
// Check for special case ambient sounds
|
|
p->num = RandomizeAmbientSpecials(p->num);
|
|
|
|
// Sound was bumped from active sounds list, try to play again.
|
|
// Don't bother if voices are already maxed out.
|
|
if (FX_SoundsPlaying() < NumVoices)
|
|
{
|
|
if (p->flags & v3df_follow)
|
|
{
|
|
PlaySound(p->num, p->x, p->y, p->z, p->flags);
|
|
p->deleted = TRUE; // Mark old sound for deletion
|
|
}
|
|
else
|
|
{
|
|
PlaySound(p->num, &p->fx, &p->fy, &p->fz, p->flags);
|
|
p->deleted = TRUE; // Mark old sound for deletion
|
|
}
|
|
}
|
|
} // !FX_SoundActive
|
|
|
|
p->tics = 0;
|
|
//while (p->tics >= p->maxtics) // Really stupid thing to do!
|
|
// {
|
|
// p->tics -= p->maxtics;
|
|
// }
|
|
}
|
|
}
|
|
|
|
void
|
|
StopAmbientSound(void)
|
|
{
|
|
VOC3D_INFOp p;
|
|
extern SWBOOL InMenuLevel;
|
|
|
|
if (InMenuLevel) return;
|
|
|
|
p = voc3dstart;
|
|
|
|
while (p)
|
|
{
|
|
// kill ambient sounds if Ambient is off
|
|
if (TEST(p->flags,v3df_ambient))
|
|
SET(p->flags, v3df_kill);
|
|
|
|
if (p->flags & v3df_kill)
|
|
{
|
|
if (FX_SoundActive(p->handle))
|
|
FX_StopSound(p->handle); // Make sure to stop active sounds
|
|
|
|
p->deleted = TRUE;
|
|
}
|
|
|
|
p = p->next;
|
|
}
|
|
|
|
Delete3DSounds();
|
|
}
|
|
|
|
void
|
|
StartAmbientSound(void)
|
|
{
|
|
VOC3D_INFOp p;
|
|
short i,nexti;
|
|
extern SWBOOL InMenuLevel;
|
|
|
|
if (InMenuLevel) return; // Don't restart ambience if no level is active! Will crash game.
|
|
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_AMBIENT], i, nexti)
|
|
{
|
|
SPRITEp sp = &sprite[i];
|
|
|
|
PlaySound(sp->lotag, &sp->x, &sp->y, &sp->z, v3df_ambient | v3df_init
|
|
| v3df_doppler | v3df_follow);
|
|
Set3DSoundOwner(i); // Ambient sounds need this to get sectnum for later processing
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Main function to update 3D sound array
|
|
///////////////////////////////////////////////
|
|
typedef struct
|
|
{
|
|
VOC3D_INFOp p;
|
|
short dist;
|
|
uint8_t priority;
|
|
} TVOC_INFO, *TVOC_INFOp;
|
|
|
|
void
|
|
DoUpdateSounds3D(void)
|
|
{
|
|
VOC3D_INFOp p;
|
|
SWBOOL looping;
|
|
int pitch = 0, pitchmax;
|
|
int delta;
|
|
short dist, angle;
|
|
SWBOOL deletesound = FALSE;
|
|
|
|
TVOC_INFO TmpVocArray[32];
|
|
int i;
|
|
static SWBOOL MoveSkip8 = 0;
|
|
|
|
if (UsingMenus) return;
|
|
|
|
// This function is already only call 10x per sec, this widdles it down even more!
|
|
MoveSkip8 = (MoveSkip8 + 1) & 15;
|
|
|
|
//CON_Message("Sounds Playing = %d",FX_SoundsPlaying());
|
|
|
|
// Zero out the temporary array
|
|
//memset(&TmpVocArray[0],0,sizeof(TmpVocArray));
|
|
for (i=0; i<32; i++)
|
|
{
|
|
TmpVocArray[i].p = NULL;
|
|
TmpVocArray[i].dist = 0;
|
|
TmpVocArray[i].priority = 0;
|
|
}
|
|
|
|
p = voc3dstart;
|
|
|
|
while (p)
|
|
{
|
|
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
|
|
|
|
looping = p->vp->voc_flags & vf_loop;
|
|
|
|
// //DSPRINTF(ds,"sound %d FX_SoundActive = %d\n,",p->num,FX_SoundActive(p->handle));
|
|
// MONO_PRINT(ds);
|
|
|
|
// If sprite owner is dead, kill this sound as long as it isn't ambient
|
|
if (looping && p->owner == -1 && !TEST(p->flags,v3df_ambient))
|
|
{
|
|
SET(p->flags, v3df_kill);
|
|
}
|
|
|
|
// Is the sound slated for death? Kill it, otherwise play it.
|
|
if (p->flags & v3df_kill)
|
|
{
|
|
if (FX_SoundActive(p->handle))
|
|
FX_StopSound(p->handle); // Make sure to stop active sounds
|
|
|
|
//DSPRINTF(ds,"%d had v3df_kill.\n",p->num);
|
|
//MONO_PRINT(ds);
|
|
p->deleted = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!FX_SoundActive(p->handle) && !looping)
|
|
{
|
|
if (p->flags & v3df_intermit)
|
|
{
|
|
DoTimedSound(p);
|
|
}
|
|
else
|
|
//if(p->owner == -1 && !TEST(p->flags,v3df_ambient))
|
|
{
|
|
//DSPRINTF(ds,"%d is now inactive.\n",p->num);
|
|
//MONO_PRINT(ds);
|
|
p->deleted = TRUE;
|
|
}
|
|
}
|
|
else if (FX_SoundActive(p->handle))
|
|
{
|
|
if (p->flags & v3df_follow)
|
|
{
|
|
dist = SoundDist(*p->x, *p->y, *p->z, p->vp->voc_distance);
|
|
angle = SoundAngle(*p->x, *p->y);
|
|
}
|
|
else
|
|
{
|
|
if (p->fx == 0 && p->fy == 0 && p->fz == 0)
|
|
dist = 0;
|
|
else
|
|
dist = SoundDist(p->fx, p->fy, p->fz, p->vp->voc_distance);
|
|
angle = SoundAngle(p->fx, p->fy);
|
|
}
|
|
|
|
// Can the ambient sound see the player? If not, tone it down some.
|
|
if ((p->vp->voc_flags & vf_loop) && p->owner != -1)
|
|
{
|
|
PLAYERp pp = Player+screenpeek;
|
|
SPRITEp sp = &sprite[p->owner];
|
|
|
|
//MONO_PRINT("Checking sound cansee");
|
|
if (!FAFcansee(sp->x, sp->y, sp->z, sp->sectnum,pp->posx, pp->posy, pp->posz, pp->cursectnum))
|
|
{
|
|
//MONO_PRINT("Reducing sound distance");
|
|
dist += ((dist/2)+(dist/4)); // Play more quietly
|
|
if (dist > 255) dist = 255;
|
|
|
|
// Special cases
|
|
if (p->num == 76 && TEST(p->flags,v3df_ambient))
|
|
{
|
|
dist = 255; // Cut off whipping sound, it's secret
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (dist >= 255 && p->vp->voc_distance == DIST_NORMAL)
|
|
{
|
|
FX_StopSound(p->handle); // Make sure to stop active
|
|
// sounds
|
|
}
|
|
else
|
|
{
|
|
// Handle Panning Left and Right
|
|
if (!(p->flags & v3df_dontpan))
|
|
FX_Pan3D(p->handle, angle, dist);
|
|
else
|
|
FX_Pan3D(p->handle, 0, dist);
|
|
|
|
// Handle Doppler Effects
|
|
#define DOPPLERMAX 400
|
|
if (!(p->flags & v3df_doppler) && FX_SoundActive(p->handle))
|
|
{
|
|
pitch -= (dist - p->doplr_delta);
|
|
|
|
if (p->vp->pitch_lo != 0 && p->vp->pitch_hi != 0)
|
|
{
|
|
if (abs(p->vp->pitch_lo) > abs(p->vp->pitch_hi))
|
|
pitchmax = abs(p->vp->pitch_lo);
|
|
else
|
|
pitchmax = abs(p->vp->pitch_hi);
|
|
|
|
}
|
|
else
|
|
pitchmax = DOPPLERMAX;
|
|
|
|
if (pitch > pitchmax)
|
|
pitch = pitchmax;
|
|
if (pitch < -pitchmax)
|
|
pitch = -pitchmax;
|
|
|
|
p->doplr_delta = dist; // Save new distance to
|
|
// struct
|
|
FX_SetPitch(p->handle, pitch);
|
|
}
|
|
}
|
|
}
|
|
else if (!FX_SoundActive(p->handle) && looping)
|
|
{
|
|
if (p->flags & v3df_follow)
|
|
{
|
|
dist = SoundDist(*p->x, *p->y, *p->z, p->vp->voc_distance);
|
|
angle = SoundAngle(*p->x, *p->y);
|
|
}
|
|
else
|
|
{
|
|
dist = SoundDist(p->fx, p->fy, p->fz, p->vp->voc_distance);
|
|
angle = SoundAngle(p->fx, p->fy);
|
|
}
|
|
|
|
// Sound was bumped from active sounds list, try to play
|
|
// again.
|
|
// Don't bother if voices are already maxed out.
|
|
// Sort looping vocs in order of priority and distance
|
|
//if (FX_SoundsPlaying() < NumVoices && dist <= 255)
|
|
if (dist <= 255)
|
|
{
|
|
for (i=0; i<min((int)SIZ(TmpVocArray), NumVoices); i++)
|
|
{
|
|
if (p->priority >= TmpVocArray[i].priority)
|
|
{
|
|
if (!TmpVocArray[i].p || dist < TmpVocArray[i].dist)
|
|
{
|
|
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
|
|
TmpVocArray[i].p = p;
|
|
TmpVocArray[i].dist = dist;
|
|
TmpVocArray[i].priority = p->priority;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // !FX_SoundActive
|
|
} // if(p->flags & v3df_kill)
|
|
|
|
p = p->next;
|
|
} // while(p)
|
|
|
|
// //DSPRINTF(ds,"Num vocs in list: %d, Sounds playing: %d\n",numelems,FX_SoundsPlaying());
|
|
// MONO_PRINT(ds);
|
|
|
|
// Process all the looping sounds that said they wanted to get back in
|
|
// Only update these sounds 5x per second! Woo hoo!, aren't we optimized now?
|
|
//if(MoveSkip8==0)
|
|
// {
|
|
for (i=0; i<min((int)SIZ(TmpVocArray), NumVoices); i++)
|
|
{
|
|
int handle;
|
|
|
|
p = TmpVocArray[i].p;
|
|
|
|
//if (FX_SoundsPlaying() >= NumVoices || !p) break;
|
|
if (!p) break;
|
|
|
|
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
|
|
|
|
if (p->flags & v3df_follow)
|
|
{
|
|
if (p->owner == -1)
|
|
{
|
|
int enumber;
|
|
enumber = p->num;
|
|
TerminateGame();
|
|
printf("Owner == -1 on looping sound with follow flag set!\n");
|
|
printf("p->num = %d\n",enumber);
|
|
exit(0);
|
|
}
|
|
|
|
Use_SoundSpriteNum = TRUE;
|
|
SoundSpriteNum = p->owner;
|
|
|
|
handle = PlaySound(p->num, p->x, p->y, p->z, p->flags);
|
|
//if(handle >= 0 || TEST(p->flags,v3df_ambient)) // After a valid PlaySound, it's ok to use voc3dend
|
|
voc3dend->owner = p->owner; // Transfer the owner
|
|
p->deleted = TRUE;
|
|
|
|
Use_SoundSpriteNum = FALSE;
|
|
SoundSpriteNum = -1;
|
|
|
|
//MONO_PRINT("TmpVocArray playing a follow sound");
|
|
}
|
|
else
|
|
{
|
|
if (p->owner == -1)
|
|
{
|
|
int enumber;
|
|
enumber = p->num;
|
|
TerminateGame();
|
|
printf("Owner == -1 on looping sound, no follow flag.\n");
|
|
printf("p->num = %d\n",enumber);
|
|
exit(0);
|
|
}
|
|
|
|
Use_SoundSpriteNum = TRUE;
|
|
SoundSpriteNum = p->owner;
|
|
|
|
handle = PlaySound(p->num, &p->fx, &p->fy, &p->fz, p->flags);
|
|
//if(handle >= 0 || TEST(p->flags,v3df_ambient))
|
|
voc3dend->owner = p->owner; // Transfer the owner
|
|
p->deleted = TRUE;
|
|
|
|
Use_SoundSpriteNum = FALSE;
|
|
SoundSpriteNum = -1;
|
|
|
|
//MONO_PRINT("TmpVocArray playing a non-follow sound");
|
|
}
|
|
}
|
|
// } // MoveSkip8
|
|
|
|
// Clean out any deleted sounds now
|
|
Delete3DSounds();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Terminate the sounds list
|
|
//////////////////////////////////////////////////
|
|
void
|
|
Terminate3DSounds(void)
|
|
{
|
|
VOC3D_INFOp vp;
|
|
|
|
vp = voc3dstart;
|
|
|
|
while (vp)
|
|
{
|
|
FX_StopSound(vp->handle); // Make sure to stop active sounds
|
|
vp->deleted = TRUE;
|
|
vp = vp->next;
|
|
}
|
|
|
|
Delete3DSounds(); // Now delete all remaining sounds
|
|
ClearSoundLocks();
|
|
}
|
|
|
|
void
|
|
DumpSounds(void)
|
|
{
|
|
VOC3D_INFOp vp;
|
|
|
|
vp = voc3dstart;
|
|
|
|
while (vp)
|
|
{
|
|
if (TEST(vp->flags,v3df_ambient))
|
|
sprintf(ds,"vp->num=%d, vp->owner=%d, vp->vp->lock=%d",ambarray[vp->num].diginame,vp->owner,vp->vp->lock);
|
|
else
|
|
sprintf(ds,"vp->num=%d, vp->owner=%d, vp->vp->lock=%d",vp->num,vp->owner,vp->vp->lock);
|
|
DebugWriteString(ds);
|
|
if (vp->owner >= 0)
|
|
{
|
|
SPRITEp sp = &sprite[vp->owner];
|
|
sprintf(ds,"sp->picnum=%d, sp->hitag=%d, sp->lotag=%d, sp->owner=%d\n",TrackerCast(sp->picnum), TrackerCast(sp->hitag), TrackerCast(sp->lotag), TrackerCast(sp->owner));
|
|
DebugWriteString(ds);
|
|
}
|
|
vp = vp->next;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// Set owner to check when to kill looping sounds
|
|
// Must be called immediately after PlaySound call
|
|
// since this only assigns value to last sound
|
|
// on voc list
|
|
//////////////////////////////////////////////////
|
|
void
|
|
Set3DSoundOwner(short spritenum)
|
|
{
|
|
VOC3D_INFOp p;
|
|
|
|
// ASSERT(p->handle != -1); // Check for bogus sounds
|
|
|
|
p = voc3dend;
|
|
if (!p) return;
|
|
|
|
// Queue up sounds with ambient flag even if they didn't play right away!
|
|
if (p->handle != -1 || TEST(p->flags,v3df_ambient))
|
|
{
|
|
p->owner = spritenum;
|
|
}
|
|
else
|
|
{
|
|
p->deleted = TRUE;
|
|
p->flags = v3df_kill;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Play a sound using special sprite setup
|
|
//////////////////////////////////////////////////
|
|
void
|
|
PlaySpriteSound(short spritenum, int attrib_ndx, Voc3D_Flags flags)
|
|
{
|
|
SPRITEp sp = &sprite[spritenum];
|
|
USERp u = User[spritenum];
|
|
|
|
ASSERT(u);
|
|
|
|
// //DSPRINTF(ds,"index = %d, digi num = %d\n",attrib_ndx,u->Attrib->Sounds[attrib_ndx]);
|
|
// MONO_PRINT(ds);
|
|
PlaySound(u->Attrib->Sounds[attrib_ndx], &sp->x, &sp->y, &sp->z, flags);
|
|
}
|
|
|
|
// vim:ts=4:sw=4:expandtab:
|
|
END_SW_NS
|