mirror of
https://github.com/ZDoom/Raze.git
synced 2025-01-10 02:50:49 +00:00
809 lines
22 KiB
C++
809 lines
22 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
|
Copyright (C) 2019 Nuke.YKT, sirlemonhead
|
|
This file is part of PCExhumed.
|
|
PCExhumed is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
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.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h"
|
|
#include "build.h"
|
|
#include "engine.h"
|
|
#include "exhumed.h"
|
|
#include "sound.h"
|
|
#include "aistuff.h"
|
|
#include "player.h"
|
|
#include "sequence.h"
|
|
#include "raze_sound.h"
|
|
#include "mapinfo.h"
|
|
|
|
BEGIN_PS_NS
|
|
|
|
const char *SoundFiles[kMaxSoundFiles] =
|
|
{
|
|
"spl_big",
|
|
"spl_smal",
|
|
"bubble_l",
|
|
"grn_drop",
|
|
"p_click",
|
|
"grn_roll",
|
|
"cosprite",
|
|
"m_chant0",
|
|
"anu_icu",
|
|
"item_reg",
|
|
"item_spe", // 10
|
|
"item_key",
|
|
"torch_on", // 12
|
|
"jon_bnst",
|
|
"jon_gasp",
|
|
"jon_land",
|
|
"jon_gags",
|
|
"jon_fall",
|
|
"jon_drwn",
|
|
"jon_air1",
|
|
"jon_glp1", // 20
|
|
"jon_bbwl",
|
|
"jon_pois",
|
|
"amb_ston",
|
|
"cat_icu",
|
|
"bubble_h",
|
|
"set_land",
|
|
"jon_hlnd",
|
|
"jon_laf2",
|
|
"spi_jump",
|
|
"jon_scub", // 30
|
|
"item_use",
|
|
"tr_arrow",
|
|
"swi_foot",
|
|
"swi_ston",
|
|
"swi_wtr1",
|
|
"tr_fire",
|
|
"m_skull5",
|
|
"spi_atak",
|
|
"anu_hit",
|
|
"fishdies", // 40
|
|
"scrp_icu",
|
|
"jon_wade",
|
|
"amb_watr",
|
|
"tele_1",
|
|
"wasp_stg",
|
|
"res",
|
|
"drum4",
|
|
"rex_icu",
|
|
"m_hits_u",
|
|
"q_tail", // 50
|
|
"vatr_mov",
|
|
"jon_hit3",
|
|
"jon_t_2", // 53
|
|
"jon_t_1",
|
|
"jon_t_5",
|
|
"jon_t_6",
|
|
"jon_t_8",
|
|
"jon_t_4",
|
|
"rasprit1",
|
|
"jon_fdie", // 60
|
|
"wijaf1",
|
|
"ship_1",
|
|
"saw_on",
|
|
"ra_on",
|
|
"amb_ston", // 65
|
|
"vatr_stp", // 66
|
|
"mana1",
|
|
"mana2",
|
|
"ammo",
|
|
"pot_pc1", // 70?
|
|
"pot_pc2",
|
|
"weapon",
|
|
"alarm",
|
|
"tick1",
|
|
"scrp_zap", // 75
|
|
"jon_t_3",
|
|
"jon_laf1",
|
|
"blasted",
|
|
"jon_air2" // 79
|
|
};
|
|
|
|
int nStopSound;
|
|
int nStoneSound;
|
|
int nSwitchSound;
|
|
int nElevSound;
|
|
int nCreepyTimer;
|
|
|
|
bool looped[kMaxSounds];
|
|
|
|
int16_t StaticSound[kMaxSounds];
|
|
int fakesources[] = { 0, 1, 2, 3 };
|
|
int swirlysources[4]= { 0, 1, 2, 3 };
|
|
FVector3 amb, creepy;
|
|
|
|
int nLocalChan = 0;
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
class EXSoundEngine : public RazeSoundEngine
|
|
{
|
|
// client specific parts of the sound engine go in this class.
|
|
void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override;
|
|
|
|
public:
|
|
EXSoundEngine()
|
|
{
|
|
S_Rolloff.RolloffType = ROLLOFF_Doom;
|
|
S_Rolloff.MinDistance = 0;
|
|
S_Rolloff.MaxDistance = 1536;
|
|
}
|
|
|
|
void StopChannel(FSoundChan* chan) override
|
|
{
|
|
if (chan && chan->SysChannel != NULL && !(chan->ChanFlags & CHANF_EVICTED) && chan->SourceType == SOURCE_Actor)
|
|
{
|
|
chan->Source = NULL;
|
|
chan->SourceType = SOURCE_Unattached;
|
|
}
|
|
SoundEngine::StopChannel(chan);
|
|
}
|
|
|
|
};
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
int LoadSound(const char* name)
|
|
{
|
|
char nname[9]{};
|
|
for (int i = 0; i < 8 && name[i]; i++) nname[i] = name[i];
|
|
FSoundID sndid = soundEngine->FindSoundNoHash(nname);
|
|
if (sndid.isvalid()) return sndid.index() - 1;
|
|
|
|
FStringf filename("%s.voc", nname);
|
|
auto lump = S_LookupSound(filename);
|
|
if (lump > 0)
|
|
{
|
|
auto check = fileSystem.ReadFile(lump);
|
|
bool loops = false;
|
|
if (check.GetSize() > 26 && check.GetString()[26] == 6 && !memcmp("Creative Voice File", check.GetString(), 19))
|
|
{
|
|
// This game uses the actual loop point information in the sound data as its only means to check if a sound is looped.
|
|
loops = true;
|
|
}
|
|
FSoundID retval = soundEngine->AddSoundLump(nname, lump, 0, -1, 6);
|
|
soundEngine->CacheSound(retval);
|
|
looped[retval.index() - 1] = loops;
|
|
return retval.index() - 1;
|
|
}
|
|
else if (!isShareware()) // demo tries to load sound files it doesn't have
|
|
{
|
|
Printf("Unable to open sound '%s'!\n", filename.GetChars());
|
|
}
|
|
return -1;
|
|
}
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GameInterface::StartSoundEngine()
|
|
{
|
|
soundEngine = new EXSoundEngine;
|
|
}
|
|
|
|
void InitFX(void)
|
|
{
|
|
auto& S_sfx = soundEngine->GetSounds();
|
|
S_sfx.Resize(1);
|
|
for (size_t i = 0; i < kMaxSoundFiles; i++)
|
|
{
|
|
StaticSound[i] = LoadSound(SoundFiles[i]);
|
|
}
|
|
soundEngine->HashSounds();
|
|
nCreepyTimer = kCreepyCount;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void GetSpriteSoundPitch(int* pVolume, int* pPitch)
|
|
{
|
|
auto pSoundSect = getPlayer(nLocalPlayer)->pPlayerViewSect;
|
|
int nLocalSectFlags = pSoundSect->Flag;
|
|
if (nLocalSectFlags & kSectUnderwater)
|
|
{
|
|
if (*pVolume == 255)
|
|
{
|
|
*pVolume >>= 1;
|
|
*pPitch -= 1200;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*pVolume < 255)
|
|
{
|
|
*pVolume = 255;
|
|
*pPitch += 1200;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void BendAmbientSound(void)
|
|
{
|
|
soundEngine->EnumerateChannels([](FSoundChan* chan)
|
|
{
|
|
if (chan->SourceType == SOURCE_Ambient && chan->Source == &amb)
|
|
{
|
|
soundEngine->SetPitch(chan, (nDronePitch + 11800) / 11025.f);
|
|
}
|
|
return 1;
|
|
});
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PlayLocalSound(int nSound, int nRate, bool unattached, EChanFlags cflags)
|
|
{
|
|
if (!SoundEnabled()) return;
|
|
auto soundid = FSoundID::fromInt(nSound + 1);
|
|
if (!soundEngine->isValidSoundId(soundid))
|
|
{
|
|
Printf("PlayLocalSound: Invalid sound nSound == %i, nRate == %i\n", nSound, nRate);
|
|
return;
|
|
}
|
|
if (looped[nSound]) cflags |= CHANF_LOOP;
|
|
|
|
FSoundChan* chan;
|
|
if (!unattached)
|
|
{
|
|
if (!(cflags & CHANF_UI) && soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_BODY, soundid)) return;
|
|
soundEngine->StopSound(SOURCE_None, nullptr, CHAN_BODY);
|
|
chan = soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_BODY, cflags, soundid, 1.f, ATTN_NONE, nullptr);
|
|
}
|
|
else
|
|
{
|
|
chan = soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_VOICE, CHANF_OVERLAP|cflags, soundid, 1.f, ATTN_NONE, nullptr);
|
|
}
|
|
|
|
if (nRate && chan)
|
|
{
|
|
float ratefac = (11025 + nRate) / 11025.f;
|
|
soundEngine->SetPitch(chan, ratefac);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
int LocalSoundPlaying(void)
|
|
{
|
|
return soundEngine->EnumerateChannels([](FSoundChan* chan)
|
|
{
|
|
return chan->SourceType == SOURCE_None;
|
|
});
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void StopLocalSound(void)
|
|
{
|
|
soundEngine->StopSound(SOURCE_None, nullptr, -1);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
int nNextFreq;
|
|
int nSwirlyFrames;
|
|
|
|
void StartSwirly(int nActiveSound)
|
|
{
|
|
if (!SoundEnabled()) return;
|
|
auto &swirly = swirlysources[nActiveSound];
|
|
|
|
int nPitch = nNextFreq - RandomSize(9);
|
|
nNextFreq = 25000 - RandomSize(10) * 6;
|
|
if (nNextFreq > 32000)
|
|
nNextFreq = 32000;
|
|
|
|
int nVolume = nSwirlyFrames + 1;
|
|
if (nVolume >= 220)
|
|
nVolume = 220;
|
|
|
|
soundEngine->StopSound(SOURCE_Swirly, &swirly, -1);
|
|
soundEngine->StartSound(SOURCE_Swirly, &swirly, nullptr, CHAN_BODY, CHANF_TRANSIENT, FSoundID::fromInt(StaticSound[kSoundMana1]+1), nVolume / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void StartSwirlies()
|
|
{
|
|
StopAllSounds();
|
|
|
|
nNextFreq = 19000;
|
|
nSwirlyFrames = 0;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
StartSwirly(i);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void UpdateSwirlies()
|
|
{
|
|
nSwirlyFrames++;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (!soundEngine->IsSourcePlayingSomething(SOURCE_Swirly, &swirlysources[i], -1))
|
|
StartSwirly(i);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void SoundBigEntrance(void)
|
|
{
|
|
if (!SoundEnabled()) return;
|
|
StopAllSounds();
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int nPitch = 11025 + (i * 512 - 1200);
|
|
//pASound->snd_pitch = nPitch;
|
|
soundEngine->StopSound(SOURCE_EXBoss, &fakesources[i], -1);
|
|
soundEngine->StartSound(SOURCE_EXBoss, &fakesources[i], nullptr, CHAN_BODY, CHANF_TRANSIENT, FSoundID::fromInt(StaticSound[kSoundTorchOn]+1), 200 / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void EXSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan)
|
|
{
|
|
if (pos != nullptr)
|
|
{
|
|
DVector3 campos;
|
|
if (nSnakeCam > -1)
|
|
{
|
|
Snake* pSnake = &SnakeList[nSnakeCam];
|
|
campos = pSnake->pSprites[0]->spr.pos;
|
|
}
|
|
else if (const auto pActor = getPlayer(nLocalPlayer)->GetActor())
|
|
{
|
|
campos = pActor->spr.pos;
|
|
}
|
|
auto fcampos = GetSoundPos(campos);
|
|
|
|
if (vel) vel->Zero();
|
|
|
|
if (type == SOURCE_Ambient)
|
|
{
|
|
*pos = *(FVector3*)source;
|
|
}
|
|
else if (type == SOURCE_Unattached)
|
|
{
|
|
pos->X = pt[0];
|
|
pos->Y = pt[1];
|
|
pos->Z = pt[2];
|
|
}
|
|
// Do some angular magic. The original was just 2D panning which in a 3D sound field is not sufficient.
|
|
else if (type == SOURCE_Swirly)
|
|
{
|
|
int which = *(int*)source;
|
|
double phase = PlayClock << (4 + which);
|
|
pos->X = fcampos.X + float(256 * BobVal(phase + 512));
|
|
pos->Z = fcampos.Z + float(256 * BobVal(phase));
|
|
}
|
|
else if (type == SOURCE_EXBoss)
|
|
{
|
|
int which = *(int*)source;
|
|
*pos = fcampos;
|
|
// Should be positioned in 90° intervals.
|
|
switch (which)
|
|
{
|
|
default:
|
|
case 0: pos->X -= 256; break;
|
|
case 1: pos->Z -= 256; break;
|
|
case 2: pos->X += 256; break;
|
|
case 3: pos->Z += 256; break;
|
|
}
|
|
}
|
|
else if (type == SOURCE_Actor)
|
|
{
|
|
auto actor = (DExhumedActor*)source;
|
|
assert(actor != nullptr);
|
|
if (actor != nullptr)
|
|
{
|
|
*pos = GetSoundPos(actor->spr.pos);
|
|
}
|
|
}
|
|
if ((chanflags & CHANF_LISTENERZ) && type != SOURCE_None)
|
|
{
|
|
pos->Y = fcampos.Y;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void GameInterface::UpdateSounds()
|
|
{
|
|
if (nFreeze)
|
|
return;
|
|
|
|
const auto pActor = getPlayer(nLocalPlayer)->GetActor();
|
|
|
|
DVector3 pos;
|
|
DAngle ang;
|
|
if (nSnakeCam > -1)
|
|
{
|
|
Snake* pSnake = &SnakeList[nSnakeCam];
|
|
pos = pSnake->pSprites[0]->spr.pos;
|
|
ang = pSnake->pSprites[0]->spr.Angles.Yaw;
|
|
}
|
|
else if (pActor)
|
|
{
|
|
pos = pActor->spr.pos;
|
|
ang = pActor->spr.Angles.Yaw;
|
|
}
|
|
else pos.Zero();
|
|
SoundListener listener;
|
|
listener.angle = float(-ang.Radians()); // Build uses a period of 2048.
|
|
listener.velocity.Zero();
|
|
listener.position = GetSoundPos(pos);
|
|
listener.underwater = false;
|
|
// This should probably use a real environment instead of the pitch hacking in S_PlaySound3D.
|
|
// listenactor->waterlevel == 3;
|
|
//assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber);
|
|
listener.Environment = 0;// primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment;
|
|
listener.valid = true;
|
|
|
|
|
|
soundEngine->SetListener(listener);
|
|
soundEngine->EnumerateChannels([](FSoundChan* chan)
|
|
{
|
|
if (!(chan->ChanFlags & (CHANF_UI|CHANF_FORGETTABLE)))
|
|
{
|
|
int nVolume = int(chan->Volume * 255);
|
|
int nPitch = int(chan->Pitch * (11025.f / 128.f)) - 11025;
|
|
int oVolume = nVolume;
|
|
GetSpriteSoundPitch(&nVolume, &nPitch);
|
|
if (oVolume != nVolume)
|
|
{
|
|
soundEngine->SetPitch(chan, (11025 + nPitch) / 11025.f);
|
|
soundEngine->SetVolume(chan, nVolume / 255.f);
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PlayFX2(int nSound, DExhumedActor* pActor, int sectf, EChanFlags chanflags, int sprflags, const DVector3* soundpos)
|
|
{
|
|
if (!SoundEnabled()) return;
|
|
auto soundid = FSoundID::fromInt((nSound & 0x1ff) + 1);
|
|
if (!soundEngine->isValidSoundId(soundid))
|
|
{
|
|
Printf("PlayFX2: Invalid sound nSound == %i\n", nSound);
|
|
return;
|
|
}
|
|
|
|
bool fullvol = false, hiprio = false;
|
|
if (pActor)
|
|
{
|
|
fullvol = (sprflags & 0x2000) != 0;
|
|
hiprio = (sprflags & 0x4000) != 0;
|
|
soundpos = &pActor->spr.pos;
|
|
}
|
|
if (!soundpos) return;
|
|
|
|
int nVolume = 255;
|
|
|
|
bool allowMultiple = (nSound & 0x2000) != 0;
|
|
bool forcePlay = (nSound & 0x1000) != 0;
|
|
bool midprio = (nSound & 0x4000) != 0;
|
|
|
|
int prio = 0;
|
|
if (forcePlay || midprio) prio = 1000;
|
|
else if (pActor != nullptr && hiprio) prio = 2000;
|
|
|
|
int v10 = (nSound&0xe00)>>9;
|
|
nSound &= 0x1ff;
|
|
|
|
int nPitch = 0;
|
|
if (v10) nPitch = -(totalmoves&((1<<v10)-1))*16;
|
|
|
|
GetSpriteSoundPitch(&nVolume, &nPitch);
|
|
|
|
FVector3 vv = GetSoundPos(*soundpos);
|
|
|
|
// Check if this sound is allowed to play or if it must stop some other sound.
|
|
if (!forcePlay)
|
|
{
|
|
bool res = soundEngine->EnumerateChannels([=](FSoundChan* chan)
|
|
{
|
|
if (chan->SourceType == SOURCE_Actor && pActor != nullptr)
|
|
{
|
|
if (prio >= chan->UserData)
|
|
{
|
|
if (chan->SoundID == soundid)
|
|
{
|
|
if (!allowMultiple && pActor == chan->Source)
|
|
return 1;
|
|
}
|
|
else if (pActor == chan->Source)
|
|
{
|
|
soundEngine->StopChannel(chan);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (chan->SourceType == SOURCE_Unattached && pActor != nullptr)
|
|
{
|
|
if (chan->SoundID == soundid)
|
|
{
|
|
if (vv.X == chan->Point[0] && vv.Y == chan->Point[1] && vv.Z == chan->Point[2])
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
});
|
|
if (res) return;
|
|
}
|
|
FSoundChan* chan = nullptr;
|
|
if (pActor != nullptr)
|
|
{
|
|
chan = soundEngine->StartSound(SOURCE_Actor, pActor, nullptr, CHAN_BODY, chanflags| CHANF_OVERLAP, soundid, nVolume / 255.f,fullvol? 0.5f : ATTN_NORM, nullptr, (11025 + nPitch) / 11025.f);
|
|
}
|
|
else
|
|
{
|
|
chan = soundEngine->StartSound(SOURCE_Unattached, nullptr, &vv, CHAN_BODY, chanflags | CHANF_OVERLAP, soundid, nVolume / 255.f, ATTN_NORM, nullptr, (11025 + nPitch) / 11025.f);
|
|
}
|
|
if (chan)
|
|
{
|
|
if (sectf) chan->UserData = 2000;
|
|
else chan->UserData = prio;
|
|
}
|
|
|
|
// Nuke: added nSprite >= 0 check
|
|
if (pActor != getPlayer(nLocalPlayer)->GetActor() && pActor != nullptr && (pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL))
|
|
nCreepyTimer = kCreepyCount;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PlayFXAtXYZ(int ax, const DVector3& pos, EChanFlags chanflags, int sectf)
|
|
{
|
|
PlayFX2(ax, nullptr, sectf, chanflags, 0, &pos);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void CheckAmbience(sectortype* sect)
|
|
{
|
|
if (!SoundEnabled()) return;
|
|
if (sect->Sound != -1)
|
|
{
|
|
auto pSector2 = sect->pSoundSect;
|
|
walltype* pWall = pSector2->walls.Data();
|
|
if (!soundEngine->IsSourcePlayingSomething(SOURCE_Ambient, &amb, 0))
|
|
{
|
|
DVector3 v = { pWall->pos.X, pWall->pos.Y, pSector2->floorz };
|
|
amb = GetSoundPos(v);
|
|
soundEngine->StartSound(SOURCE_Ambient, &amb, nullptr, CHAN_BODY, CHANF_TRANSIENT, FSoundID::fromInt(sect->Sound + 1), 1.f, ATTN_NORM);
|
|
return;
|
|
}
|
|
soundEngine->EnumerateChannels([=](FSoundChan* chan)
|
|
{
|
|
if (chan->SourceType == SOURCE_Ambient)
|
|
{
|
|
if (sect == pSector2)
|
|
{
|
|
amb = GetSoundPos(getPlayer(0)->GetActor()->spr.pos);
|
|
}
|
|
else
|
|
{
|
|
DVector3 v = { pWall->pos.X, pWall->pos.Y, pSector2->floorz };
|
|
amb = GetSoundPos(v);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
|
|
}
|
|
else
|
|
{
|
|
soundEngine->StopSound(SOURCE_Ambient, &amb, -1);
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void UpdateCreepySounds()
|
|
{
|
|
if ((currentLevel->gameflags & LEVEL_EX_COUNTDOWN) || nFreeze || !SoundEnabled())
|
|
return;
|
|
nCreepyTimer--;
|
|
if (nCreepyTimer <= 0)
|
|
{
|
|
if (Level.kills.got < Level.kills.max && !(getPlayer(nLocalPlayer)->pPlayerViewSect->Flag & 0x2000))
|
|
{
|
|
const auto creepySeq = getSequence("creepy");
|
|
const auto seqFrameSound = creepySeq->frames[totalmoves % creepySeq->frames.Size()].sound;
|
|
if (seqFrameSound >= 0 && (seqFrameSound & 0x1ff) < kMaxSounds)
|
|
{
|
|
DVector2 adder;
|
|
adder.X = ((totalmoves + 32) & 31) / 16.;
|
|
if (totalmoves & 1)
|
|
adder.X = -adder.X;
|
|
adder.Y = ((totalmoves + 32) & 63) / 16.;
|
|
if (totalmoves & 2)
|
|
adder.Y = -adder.Y;
|
|
|
|
auto sp = getPlayer(nLocalPlayer)->GetActor()->spr.pos + adder;
|
|
creepy = GetSoundPos(sp);
|
|
|
|
auto soundid = FSoundID::fromInt((seqFrameSound & 0x1ff) + 1);
|
|
|
|
if (!soundEngine->isValidSoundId(soundid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int nVolume = 255;
|
|
int v10 = (seqFrameSound & 0xe00) >> 9;
|
|
|
|
int nPitch = 0;
|
|
if (v10) nPitch = -(totalmoves & ((1 << v10) - 1)) * 16;
|
|
|
|
GetSpriteSoundPitch(&nVolume, &nPitch);
|
|
soundEngine->StopSound(SOURCE_Ambient, &creepy, CHAN_BODY);
|
|
soundEngine->StartSound(SOURCE_Ambient, &creepy, nullptr, CHAN_BODY, CHANF_TRANSIENT, soundid, nVolume / 255.f, ATTN_NONE, nullptr, (11025 + nPitch) / 11025.f);
|
|
}
|
|
}
|
|
nCreepyTimer = kCreepyCount;
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void StopActorSound(DExhumedActor *pActor)
|
|
{
|
|
if (pActor)
|
|
soundEngine->StopSound(SOURCE_Actor, pActor, -1);
|
|
}
|
|
|
|
void StopAllSounds(void)
|
|
{
|
|
soundEngine->StopAllChannels();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PlayTitleSound(void)
|
|
{
|
|
PlayLocalSound(StaticSound[kSoundItemSpecial], 0, false, CHANF_UI);
|
|
}
|
|
|
|
void PlayGameOverSound(void)
|
|
{
|
|
PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound)
|
|
{
|
|
PARAM_PROLOGUE;
|
|
PARAM_INT(snd);
|
|
PARAM_INT(pitch);
|
|
PARAM_BOOL(unatt);
|
|
PARAM_INT(flags);
|
|
PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags));
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound)
|
|
{
|
|
StopLocalSound();
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying)
|
|
{
|
|
ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO));
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack)
|
|
{
|
|
PARAM_PROLOGUE;
|
|
PARAM_INT(track);
|
|
PARAM_BOOL(loop);
|
|
playCDtrack(track, loop);
|
|
return 0;
|
|
}
|
|
|
|
END_PS_NS
|