mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge pull request #67 from cinaera/pr_sound
Add support for ingame map sounds
This commit is contained in:
commit
378c34f3ff
|
@ -45,8 +45,10 @@ struct CVoice
|
||||||
{
|
{
|
||||||
CSample *m_pSample;
|
CSample *m_pSample;
|
||||||
CChannel *m_pChannel;
|
CChannel *m_pChannel;
|
||||||
|
int m_Age; // increases when reused
|
||||||
int m_Tick;
|
int m_Tick;
|
||||||
int m_Vol; // 0 - 255
|
int m_Vol; // 0 - 255
|
||||||
|
int m_FalloffDistance; // 0 - inifintee (well int)
|
||||||
int m_Flags;
|
int m_Flags;
|
||||||
int m_X, m_Y;
|
int m_X, m_Y;
|
||||||
} ;
|
} ;
|
||||||
|
@ -67,6 +69,12 @@ static int m_NextVoice = 0;
|
||||||
static int *m_pMixBuffer = 0; // buffer only used by the thread callback function
|
static int *m_pMixBuffer = 0; // buffer only used by the thread callback function
|
||||||
static unsigned m_MaxFrames = 0;
|
static unsigned m_MaxFrames = 0;
|
||||||
|
|
||||||
|
static const void *ms_pWVBuffer = 0x0;
|
||||||
|
static int ms_WVBufferPosition = 0;
|
||||||
|
static int ms_WVBufferSize = 0;
|
||||||
|
|
||||||
|
const int DefaultDistance = 1500;
|
||||||
|
|
||||||
// TODO: there should be a faster way todo this
|
// TODO: there should be a faster way todo this
|
||||||
static short Int2Short(int i)
|
static short Int2Short(int i)
|
||||||
{
|
{
|
||||||
|
@ -109,8 +117,8 @@ static void Mix(short *pFinalOut, unsigned Frames)
|
||||||
|
|
||||||
unsigned End = v->m_pSample->m_NumFrames-v->m_Tick;
|
unsigned End = v->m_pSample->m_NumFrames-v->m_Tick;
|
||||||
|
|
||||||
int Rvol = v->m_pChannel->m_Vol;
|
int Rvol = (int)(v->m_pChannel->m_Vol*(v->m_Vol/255.0f));
|
||||||
int Lvol = v->m_pChannel->m_Vol;
|
int Lvol = (int)(v->m_pChannel->m_Vol*(v->m_Vol/255.0f));
|
||||||
|
|
||||||
// make sure that we don't go outside the sound data
|
// make sure that we don't go outside the sound data
|
||||||
if(Frames < End)
|
if(Frames < End)
|
||||||
|
@ -124,11 +132,11 @@ static void Mix(short *pFinalOut, unsigned Frames)
|
||||||
if(v->m_Flags&ISound::FLAG_POS && v->m_pChannel->m_Pan)
|
if(v->m_Flags&ISound::FLAG_POS && v->m_pChannel->m_Pan)
|
||||||
{
|
{
|
||||||
// TODO: we should respect the channel panning value
|
// TODO: we should respect the channel panning value
|
||||||
const int Range = 1500; // magic value, remove
|
|
||||||
int dx = v->m_X - m_CenterX;
|
int dx = v->m_X - m_CenterX;
|
||||||
int dy = v->m_Y - m_CenterY;
|
int dy = v->m_Y - m_CenterY;
|
||||||
int Dist = (int)sqrtf((float)dx*dx+dy*dy); // float here. nasty
|
int Dist = (int)sqrtf((float)dx*dx+dy*dy); // float here. nasty
|
||||||
int p = IntAbs(dx);
|
int p = IntAbs(dx);
|
||||||
|
int Range = v->m_FalloffDistance;
|
||||||
if(Dist >= 0 && Dist < Range)
|
if(Dist >= 0 && Dist < Range)
|
||||||
{
|
{
|
||||||
// panning
|
// panning
|
||||||
|
@ -164,7 +172,10 @@ static void Mix(short *pFinalOut, unsigned Frames)
|
||||||
if(v->m_Flags&ISound::FLAG_LOOP)
|
if(v->m_Flags&ISound::FLAG_LOOP)
|
||||||
v->m_Tick = 0;
|
v->m_Tick = 0;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
v->m_pSample = 0;
|
v->m_pSample = 0;
|
||||||
|
v->m_Age++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,27 +337,90 @@ void CSound::RateConvert(int SampleID)
|
||||||
mem_free(pSample->m_pData);
|
mem_free(pSample->m_pData);
|
||||||
pSample->m_pData = pNewData;
|
pSample->m_pData = pNewData;
|
||||||
pSample->m_NumFrames = NumFrames;
|
pSample->m_NumFrames = NumFrames;
|
||||||
|
pSample->m_Rate = m_MixingRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSound::ReadData(void *pBuffer, int Size)
|
int CSound::ReadData(void *pBuffer, int Size)
|
||||||
{
|
{
|
||||||
return io_read(ms_File, pBuffer, Size);
|
int ChunkSize = min(Size, ms_WVBufferSize - ms_WVBufferPosition);
|
||||||
|
mem_copy(pBuffer, (const char *)ms_pWVBuffer + ms_WVBufferPosition, ChunkSize);
|
||||||
|
ms_WVBufferPosition += ChunkSize;
|
||||||
|
return ChunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSound::DecodeWV(int SampleID, const void *pData, unsigned DataSize)
|
||||||
|
{
|
||||||
|
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
CSample *pSample = &m_aSamples[SampleID];
|
||||||
|
char aError[100];
|
||||||
|
WavpackContext *pContext;
|
||||||
|
|
||||||
|
ms_pWVBuffer = pData;
|
||||||
|
ms_WVBufferSize = DataSize;
|
||||||
|
ms_WVBufferPosition = 0;
|
||||||
|
|
||||||
|
pContext = WavpackOpenFileInput(ReadData, aError);
|
||||||
|
if (pContext)
|
||||||
|
{
|
||||||
|
int NumSamples = WavpackGetNumSamples(pContext);
|
||||||
|
int BitsPerSample = WavpackGetBitsPerSample(pContext);
|
||||||
|
unsigned int SampleRate = WavpackGetSampleRate(pContext);
|
||||||
|
int NumChannels = WavpackGetNumChannels(pContext);
|
||||||
|
int *pSrc;
|
||||||
|
short *pDst;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pSample->m_Channels = NumChannels;
|
||||||
|
pSample->m_Rate = SampleRate;
|
||||||
|
|
||||||
|
if(pSample->m_Channels > 2)
|
||||||
|
{
|
||||||
|
dbg_msg("sound/wv", "file is not mono or stereo.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BitsPerSample != 16)
|
||||||
|
{
|
||||||
|
dbg_msg("sound/wv", "bps is %d, not 16", BitsPerSample);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *pBuffer = (int *)mem_alloc(4*NumSamples*NumChannels, 1);
|
||||||
|
WavpackUnpackSamples(pContext, pBuffer, NumSamples); // TODO: check return value
|
||||||
|
pSrc = pBuffer;
|
||||||
|
|
||||||
|
pSample->m_pData = (short *)mem_alloc(2*NumSamples*NumChannels, 1);
|
||||||
|
pDst = pSample->m_pData;
|
||||||
|
|
||||||
|
for (i = 0; i < NumSamples*NumChannels; i++)
|
||||||
|
*pDst++ = (short)*pSrc++;
|
||||||
|
|
||||||
|
mem_free(pBuffer);
|
||||||
|
|
||||||
|
pSample->m_NumFrames = NumSamples;
|
||||||
|
pSample->m_LoopStart = -1;
|
||||||
|
pSample->m_LoopEnd = -1;
|
||||||
|
pSample->m_PausedAt = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbg_msg("sound/wv", "failed to decode sample (%s)", aError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SampleID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSound::LoadWV(const char *pFilename)
|
int CSound::LoadWV(const char *pFilename)
|
||||||
{
|
{
|
||||||
CSample *pSample;
|
|
||||||
int SampleID = -1;
|
|
||||||
char aError[100];
|
|
||||||
WavpackContext *pContext;
|
|
||||||
|
|
||||||
// don't waste memory on sound when we are stress testing
|
// don't waste memory on sound when we are stress testing
|
||||||
if(g_Config.m_DbgStress)
|
if(g_Config.m_DbgStress)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// no need to load sound when we are running with no sound
|
// no need to load sound when we are running with no sound
|
||||||
if(!m_SoundEnabled)
|
if(!m_SoundEnabled)
|
||||||
return 1;
|
return -1;
|
||||||
|
|
||||||
if(!m_pStorage)
|
if(!m_pStorage)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -358,67 +432,18 @@ int CSound::LoadWV(const char *pFilename)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleID = AllocID();
|
int SampleID = AllocID();
|
||||||
if(SampleID < 0)
|
if(SampleID < 0)
|
||||||
return -1;
|
return -1;
|
||||||
pSample = &m_aSamples[SampleID];
|
|
||||||
|
|
||||||
pContext = WavpackOpenFileInput(ReadData, aError);
|
// read the whole file into memory
|
||||||
if (pContext)
|
int DataSize = io_length(ms_File);
|
||||||
{
|
char *pData = new char[DataSize];
|
||||||
int m_aSamples = WavpackGetNumSamples(pContext);
|
io_read(ms_File, pData, DataSize);
|
||||||
int BitsPerSample = WavpackGetBitsPerSample(pContext);
|
|
||||||
unsigned int SampleRate = WavpackGetSampleRate(pContext);
|
|
||||||
int m_aChannels = WavpackGetNumChannels(pContext);
|
|
||||||
int *pData;
|
|
||||||
int *pSrc;
|
|
||||||
short *pDst;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
pSample->m_Channels = m_aChannels;
|
SampleID = DecodeWV(SampleID, pData, DataSize);
|
||||||
pSample->m_Rate = SampleRate;
|
|
||||||
|
|
||||||
if(pSample->m_Channels > 2)
|
|
||||||
{
|
|
||||||
dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", pFilename);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if(snd->rate != 44100)
|
|
||||||
{
|
|
||||||
dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
|
|
||||||
return -1;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if(BitsPerSample != 16)
|
|
||||||
{
|
|
||||||
dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", BitsPerSample, pFilename);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pData = (int *)mem_alloc(4*m_aSamples*m_aChannels, 1);
|
|
||||||
WavpackUnpackSamples(pContext, pData, m_aSamples); // TODO: check return value
|
|
||||||
pSrc = pData;
|
|
||||||
|
|
||||||
pSample->m_pData = (short *)mem_alloc(2*m_aSamples*m_aChannels, 1);
|
|
||||||
pDst = pSample->m_pData;
|
|
||||||
|
|
||||||
for (i = 0; i < m_aSamples*m_aChannels; i++)
|
|
||||||
*pDst++ = (short)*pSrc++;
|
|
||||||
|
|
||||||
mem_free(pData);
|
|
||||||
|
|
||||||
pSample->m_NumFrames = m_aSamples;
|
|
||||||
pSample->m_LoopStart = -1;
|
|
||||||
pSample->m_LoopEnd = -1;
|
|
||||||
pSample->m_PausedAt = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dbg_msg("sound/wv", "failed to open %s: %s", pFilename, aError);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
delete[] pData;
|
||||||
io_close(ms_File);
|
io_close(ms_File);
|
||||||
ms_File = NULL;
|
ms_File = NULL;
|
||||||
|
|
||||||
|
@ -429,12 +454,125 @@ int CSound::LoadWV(const char *pFilename)
|
||||||
return SampleID;
|
return SampleID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CSound::LoadWVFromMem(const void *pData, unsigned DataSize)
|
||||||
|
{
|
||||||
|
// don't waste memory on sound when we are stress testing
|
||||||
|
if(g_Config.m_DbgStress)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// no need to load sound when we are running with no sound
|
||||||
|
if(!m_SoundEnabled)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(!pData)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int SampleID = AllocID();
|
||||||
|
if(SampleID < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SampleID = DecodeWV(SampleID, pData, DataSize);
|
||||||
|
|
||||||
|
RateConvert(SampleID);
|
||||||
|
return SampleID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::UnloadSample(int SampleID)
|
||||||
|
{
|
||||||
|
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Stop(SampleID);
|
||||||
|
mem_free(m_aSamples[SampleID].m_pData);
|
||||||
|
|
||||||
|
m_aSamples[SampleID].m_pData = 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CSound::GetSampleDuration(int SampleID)
|
||||||
|
{
|
||||||
|
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return (m_aSamples[SampleID].m_NumFrames/m_aSamples[SampleID].m_Rate);
|
||||||
|
}
|
||||||
|
|
||||||
void CSound::SetListenerPos(float x, float y)
|
void CSound::SetListenerPos(float x, float y)
|
||||||
{
|
{
|
||||||
m_CenterX = (int)x;
|
m_CenterX = (int)x;
|
||||||
m_CenterY = (int)y;
|
m_CenterY = (int)y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSound::SetVoiceVolume(CVoiceHandle Voice, float Volume)
|
||||||
|
{
|
||||||
|
if(!Voice.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int VoiceID = Voice.Id();
|
||||||
|
|
||||||
|
if(m_aVoices[VoiceID].m_Age != Voice.Age())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Volume = clamp(Volume, 0.0f, 1.0f);
|
||||||
|
m_aVoices[VoiceID].m_Vol = (int)(Volume*255.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetVoiceMaxDistance(CVoiceHandle Voice, int Distance)
|
||||||
|
{
|
||||||
|
if(!Voice.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int VoiceID = Voice.Id();
|
||||||
|
|
||||||
|
if(m_aVoices[VoiceID].m_Age != Voice.Age())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(Distance < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_aVoices[VoiceID].m_FalloffDistance = Distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetVoiceLocation(CVoiceHandle Voice, float x, float y)
|
||||||
|
{
|
||||||
|
if(!Voice.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int VoiceID = Voice.Id();
|
||||||
|
|
||||||
|
if(m_aVoices[VoiceID].m_Age != Voice.Age())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_aVoices[VoiceID].m_X = x;
|
||||||
|
m_aVoices[VoiceID].m_Y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetVoiceTimeOffset(CVoiceHandle Voice, float offset)
|
||||||
|
{
|
||||||
|
if(!Voice.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int VoiceID = Voice.Id();
|
||||||
|
|
||||||
|
if(m_aVoices[VoiceID].m_Age != Voice.Age())
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock_wait(m_SoundLock);
|
||||||
|
{
|
||||||
|
if(m_aVoices[VoiceID].m_pSample)
|
||||||
|
{
|
||||||
|
int TickOffset = m_aVoices[VoiceID].m_pSample->m_Rate * offset;
|
||||||
|
if(m_aVoices[VoiceID].m_Flags&ISound::FLAG_POS)
|
||||||
|
TickOffset = TickOffset%m_aVoices[VoiceID].m_pSample->m_NumFrames;
|
||||||
|
else
|
||||||
|
TickOffset = clamp(TickOffset, 0, m_aVoices[VoiceID].m_pSample->m_NumFrames);
|
||||||
|
|
||||||
|
// at least 2sec off
|
||||||
|
if(abs(m_aVoices[VoiceID].m_Tick-TickOffset) > 2*m_aVoices[VoiceID].m_pSample->m_Rate)
|
||||||
|
m_aVoices[VoiceID].m_Tick = TickOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock_release(m_SoundLock);
|
||||||
|
}
|
||||||
|
|
||||||
void CSound::SetChannel(int ChannelID, float Vol, float Pan)
|
void CSound::SetChannel(int ChannelID, float Vol, float Pan)
|
||||||
{
|
{
|
||||||
|
@ -442,25 +580,26 @@ void CSound::SetChannel(int ChannelID, float Vol, float Pan)
|
||||||
m_aChannels[ChannelID].m_Pan = (int)(Pan*255.0f); // TODO: this is only on and off right now
|
m_aChannels[ChannelID].m_Pan = (int)(Pan*255.0f); // TODO: this is only on and off right now
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y)
|
ISound::CVoiceHandle CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y)
|
||||||
{
|
{
|
||||||
int VoiceID = -1;
|
int VoiceID = -1;
|
||||||
|
int Age = -1;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(SampleID == 107) // GetSampleID(SOUND_CHAT_SERVER)
|
if(SampleID == 107) // GetSampleID(SOUND_CHAT_SERVER)
|
||||||
{
|
{
|
||||||
if(!g_Config.m_SndServerMessage)
|
if(!g_Config.m_SndServerMessage)
|
||||||
return VoiceID;
|
return CVoiceHandle();
|
||||||
}
|
}
|
||||||
else if(SampleID == 108) // GetSampleID(SOUND_CHAT_CLIENT)
|
else if(SampleID == 108) // GetSampleID(SOUND_CHAT_CLIENT)
|
||||||
{}
|
{}
|
||||||
else if(SampleID == 109) // GetSampleID(SOUND_CHAT_HIGHLIGHT)
|
else if(SampleID == 109) // GetSampleID(SOUND_CHAT_HIGHLIGHT)
|
||||||
{
|
{
|
||||||
if(!g_Config.m_SndHighlight)
|
if(!g_Config.m_SndHighlight)
|
||||||
return VoiceID;
|
return CVoiceHandle();
|
||||||
}
|
}
|
||||||
else if(!g_Config.m_SndGame)
|
else if(!g_Config.m_SndGame)
|
||||||
return VoiceID;
|
return CVoiceHandle();
|
||||||
|
|
||||||
|
|
||||||
lock_wait(m_SoundLock);
|
lock_wait(m_SoundLock);
|
||||||
|
@ -490,18 +629,20 @@ int CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y)
|
||||||
m_aVoices[VoiceID].m_Flags = Flags;
|
m_aVoices[VoiceID].m_Flags = Flags;
|
||||||
m_aVoices[VoiceID].m_X = (int)x;
|
m_aVoices[VoiceID].m_X = (int)x;
|
||||||
m_aVoices[VoiceID].m_Y = (int)y;
|
m_aVoices[VoiceID].m_Y = (int)y;
|
||||||
|
m_aVoices[VoiceID].m_FalloffDistance = DefaultDistance;
|
||||||
|
Age = m_aVoices[VoiceID].m_Age;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock_release(m_SoundLock);
|
lock_release(m_SoundLock);
|
||||||
return VoiceID;
|
return CreateVoiceHandle(VoiceID, Age);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSound::PlayAt(int ChannelID, int SampleID, int Flags, float x, float y)
|
ISound::CVoiceHandle CSound::PlayAt(int ChannelID, int SampleID, int Flags, float x, float y)
|
||||||
{
|
{
|
||||||
return Play(ChannelID, SampleID, Flags|ISound::FLAG_POS, x, y);
|
return Play(ChannelID, SampleID, Flags|ISound::FLAG_POS, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSound::Play(int ChannelID, int SampleID, int Flags)
|
ISound::CVoiceHandle CSound::Play(int ChannelID, int SampleID, int Flags)
|
||||||
{
|
{
|
||||||
return Play(ChannelID, SampleID, Flags, 0, 0);
|
return Play(ChannelID, SampleID, Flags, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -543,6 +684,25 @@ void CSound::StopAll()
|
||||||
lock_release(m_SoundLock);
|
lock_release(m_SoundLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSound::StopVoice(CVoiceHandle Voice)
|
||||||
|
{
|
||||||
|
if(!Voice.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int VoiceID = Voice.Id();
|
||||||
|
|
||||||
|
if(m_aVoices[VoiceID].m_Age != Voice.Age())
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock_wait(m_SoundLock);
|
||||||
|
{
|
||||||
|
m_aVoices[VoiceID].m_pSample = 0;
|
||||||
|
m_aVoices[VoiceID].m_Age++;
|
||||||
|
}
|
||||||
|
lock_release(m_SoundLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
IOHANDLE CSound::ms_File = 0;
|
IOHANDLE CSound::ms_File = 0;
|
||||||
|
|
||||||
IEngineSound *CreateEngineSound() { return new CSound; }
|
IEngineSound *CreateEngineSound() { return new CSound; }
|
||||||
|
|
|
@ -24,19 +24,30 @@ public:
|
||||||
// TODO: Refactor: clean this mess up
|
// TODO: Refactor: clean this mess up
|
||||||
static IOHANDLE ms_File;
|
static IOHANDLE ms_File;
|
||||||
static int ReadData(void *pBuffer, int Size);
|
static int ReadData(void *pBuffer, int Size);
|
||||||
|
static int DecodeWV(int SampleID, const void *pData, unsigned DataSize);
|
||||||
|
|
||||||
virtual bool IsSoundEnabled() { return m_SoundEnabled != 0; }
|
virtual bool IsSoundEnabled() { return m_SoundEnabled != 0; }
|
||||||
|
|
||||||
virtual int LoadWV(const char *pFilename);
|
virtual int LoadWV(const char *pFilename);
|
||||||
|
virtual int LoadWVFromMem(const void *pData, unsigned DataSize);
|
||||||
|
virtual void UnloadSample(int SampleID);
|
||||||
|
|
||||||
|
virtual float GetSampleDuration(int SampleID); // in s
|
||||||
|
|
||||||
virtual void SetListenerPos(float x, float y);
|
virtual void SetListenerPos(float x, float y);
|
||||||
virtual void SetChannel(int ChannelID, float Vol, float Pan);
|
virtual void SetChannel(int ChannelID, float Vol, float Pan);
|
||||||
|
|
||||||
int Play(int ChannelID, int SampleID, int Flags, float x, float y);
|
virtual void SetVoiceVolume(CVoiceHandle Voice, float Volume);
|
||||||
virtual int PlayAt(int ChannelID, int SampleID, int Flags, float x, float y);
|
virtual void SetVoiceMaxDistance(CVoiceHandle Voice, int Distance);
|
||||||
virtual int Play(int ChannelID, int SampleID, int Flags);
|
virtual void SetVoiceLocation(CVoiceHandle Voice, float x, float y);
|
||||||
|
virtual void SetVoiceTimeOffset(CVoiceHandle Voice, float offset); // in s
|
||||||
|
|
||||||
|
CVoiceHandle Play(int ChannelID, int SampleID, int Flags, float x, float y);
|
||||||
|
virtual CVoiceHandle PlayAt(int ChannelID, int SampleID, int Flags, float x, float y);
|
||||||
|
virtual CVoiceHandle Play(int ChannelID, int SampleID, int Flags);
|
||||||
virtual void Stop(int SampleID);
|
virtual void Stop(int SampleID);
|
||||||
virtual void StopAll();
|
virtual void StopAll();
|
||||||
|
virtual void StopVoice(CVoiceHandle Voice);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -68,6 +68,7 @@ MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "S
|
||||||
MACRO_CONFIG_INT(SndMusic, snd_enable_music, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Play background music")
|
MACRO_CONFIG_INT(SndMusic, snd_enable_music, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Play background music")
|
||||||
MACRO_CONFIG_INT(SndVolume, snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume")
|
MACRO_CONFIG_INT(SndVolume, snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume")
|
||||||
MACRO_CONFIG_INT(SndDevice, snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use")
|
MACRO_CONFIG_INT(SndDevice, snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use")
|
||||||
|
MACRO_CONFIG_INT(SndAmbientVolume, snd_ambient_volume, 70, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ambient sound volume")
|
||||||
|
|
||||||
MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||||
MACRO_CONFIG_INT(SndGame, snd_game, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable game sounds")
|
MACRO_CONFIG_INT(SndGame, snd_game, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable game sounds")
|
||||||
|
|
|
@ -16,17 +16,54 @@ public:
|
||||||
FLAG_ALL=3
|
FLAG_ALL=3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CVoiceHandle
|
||||||
|
{
|
||||||
|
friend class ISound;
|
||||||
|
int m_Id;
|
||||||
|
int m_Age;
|
||||||
|
public:
|
||||||
|
CVoiceHandle()
|
||||||
|
: m_Id(-1), m_Age(-1)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool IsValid() const { return (Id() >= 0) && (Age() >= 0); }
|
||||||
|
int Id() const { return m_Id; }
|
||||||
|
int Age() const { return m_Age; }
|
||||||
|
|
||||||
|
bool operator ==(const CVoiceHandle &Other) const { return m_Id == Other.m_Id && m_Age == Other.m_Age; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
virtual bool IsSoundEnabled() = 0;
|
virtual bool IsSoundEnabled() = 0;
|
||||||
|
|
||||||
virtual int LoadWV(const char *pFilename) = 0;
|
virtual int LoadWV(const char *pFilename) = 0;
|
||||||
|
virtual int LoadWVFromMem(const void *pData, unsigned DataSize) = 0;
|
||||||
|
virtual void UnloadSample(int SampleID) = 0;
|
||||||
|
|
||||||
|
virtual float GetSampleDuration(int SampleID) = 0; // in s
|
||||||
|
|
||||||
virtual void SetChannel(int ChannelID, float Volume, float Panning) = 0;
|
virtual void SetChannel(int ChannelID, float Volume, float Panning) = 0;
|
||||||
virtual void SetListenerPos(float x, float y) = 0;
|
virtual void SetListenerPos(float x, float y) = 0;
|
||||||
|
|
||||||
virtual int PlayAt(int ChannelID, int SampleID, int Flags, float x, float y) = 0;
|
virtual void SetVoiceVolume(CVoiceHandle Voice, float Volume) = 0;
|
||||||
virtual int Play(int ChannelID, int SampleID, int Flags) = 0;
|
virtual void SetVoiceMaxDistance(CVoiceHandle Voice, int Distance) = 0;
|
||||||
|
virtual void SetVoiceLocation(CVoiceHandle Voice, float x, float y) = 0;
|
||||||
|
virtual void SetVoiceTimeOffset(CVoiceHandle Voice, float offset) = 0; // in s
|
||||||
|
|
||||||
|
virtual CVoiceHandle PlayAt(int ChannelID, int SampleID, int Flags, float x, float y) = 0;
|
||||||
|
virtual CVoiceHandle Play(int ChannelID, int SampleID, int Flags) = 0;
|
||||||
virtual void Stop(int SampleID) = 0;
|
virtual void Stop(int SampleID) = 0;
|
||||||
virtual void StopAll() = 0;
|
virtual void StopAll() = 0;
|
||||||
|
virtual void StopVoice(CVoiceHandle Voice) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline CVoiceHandle CreateVoiceHandle(int Index, int Age)
|
||||||
|
{
|
||||||
|
CVoiceHandle Voice;
|
||||||
|
Voice.m_Id = Index;
|
||||||
|
Voice.m_Age = Age;
|
||||||
|
return Voice;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,34 +65,7 @@ bool CEmoticon::OnMouseMove(float x, float y)
|
||||||
|
|
||||||
void CEmoticon::DrawCircle(float x, float y, float r, int Segments)
|
void CEmoticon::DrawCircle(float x, float y, float r, int Segments)
|
||||||
{
|
{
|
||||||
IGraphics::CFreeformItem Array[32];
|
RenderTools()->DrawCircle(x, y, r, Segments);
|
||||||
int NumItems = 0;
|
|
||||||
float FSegments = (float)Segments;
|
|
||||||
for(int i = 0; i < Segments; i+=2)
|
|
||||||
{
|
|
||||||
float a1 = i/FSegments * 2*pi;
|
|
||||||
float a2 = (i+1)/FSegments * 2*pi;
|
|
||||||
float a3 = (i+2)/FSegments * 2*pi;
|
|
||||||
float Ca1 = cosf(a1);
|
|
||||||
float Ca2 = cosf(a2);
|
|
||||||
float Ca3 = cosf(a3);
|
|
||||||
float Sa1 = sinf(a1);
|
|
||||||
float Sa2 = sinf(a2);
|
|
||||||
float Sa3 = sinf(a3);
|
|
||||||
|
|
||||||
Array[NumItems++] = IGraphics::CFreeformItem(
|
|
||||||
x, y,
|
|
||||||
x+Ca1*r, y+Sa1*r,
|
|
||||||
x+Ca3*r, y+Sa3*r,
|
|
||||||
x+Ca2*r, y+Sa2*r);
|
|
||||||
if(NumItems == 32)
|
|
||||||
{
|
|
||||||
m_pClient->Graphics()->QuadsDrawFreeform(Array, 32);
|
|
||||||
NumItems = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(NumItems)
|
|
||||||
m_pClient->Graphics()->QuadsDrawFreeform(Array, NumItems);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ class CMapLayers : public CComponent
|
||||||
bool m_EnvelopeUpdate;
|
bool m_EnvelopeUpdate;
|
||||||
|
|
||||||
void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f);
|
void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f);
|
||||||
static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser);
|
|
||||||
public:
|
public:
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -26,6 +25,8 @@ public:
|
||||||
virtual void OnRender();
|
virtual void OnRender();
|
||||||
|
|
||||||
void EnvelopeUpdate();
|
void EnvelopeUpdate();
|
||||||
|
|
||||||
|
static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
219
src/game/client/components/mapsounds.cpp
Normal file
219
src/game/client/components/mapsounds.cpp
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
|
||||||
|
#include <engine/engine.h>
|
||||||
|
#include <engine/sound.h>
|
||||||
|
|
||||||
|
#include <game/client/components/camera.h>
|
||||||
|
#include <game/client/components/maplayers.h> // envelope
|
||||||
|
#include <game/client/components/sounds.h>
|
||||||
|
|
||||||
|
#include "mapsounds.h"
|
||||||
|
|
||||||
|
CMapSounds::CMapSounds()
|
||||||
|
{
|
||||||
|
m_Count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMapSounds::OnMapLoad()
|
||||||
|
{
|
||||||
|
IMap *pMap = Kernel()->RequestInterface<IMap>();
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
// load samples
|
||||||
|
int Start;
|
||||||
|
pMap->GetType(MAPITEMTYPE_SOUND, &Start, &m_Count);
|
||||||
|
|
||||||
|
// load new samples
|
||||||
|
for(int i = 0; i < m_Count; i++)
|
||||||
|
{
|
||||||
|
m_aSounds[i] = 0;
|
||||||
|
|
||||||
|
CMapItemSound *pSound = (CMapItemSound *)pMap->GetItem(Start+i, 0, 0);
|
||||||
|
if(pSound->m_External)
|
||||||
|
{
|
||||||
|
char Buf[256];
|
||||||
|
char *pName = (char *)pMap->GetData(pSound->m_SoundName);
|
||||||
|
str_format(Buf, sizeof(Buf), "mapres/%s.wv", pName);
|
||||||
|
m_aSounds[i] = Sound()->LoadWV(Buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void *pData = pMap->GetData(pSound->m_SoundData);
|
||||||
|
m_aSounds[i] = Sound()->LoadWVFromMem(pData, pSound->m_SoundDataSize);
|
||||||
|
pMap->UnloadData(pSound->m_SoundData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueue sound sources
|
||||||
|
m_lSourceQueue.clear();
|
||||||
|
for(int g = 0; g < Layers()->NumGroups(); g++)
|
||||||
|
{
|
||||||
|
CMapItemGroup *pGroup = Layers()->GetGroup(g);
|
||||||
|
|
||||||
|
if(!pGroup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(int l = 0; l < pGroup->m_NumLayers; l++)
|
||||||
|
{
|
||||||
|
CMapItemLayer *pLayer = Layers()->GetLayer(pGroup->m_StartLayer+l);
|
||||||
|
|
||||||
|
if(!pLayer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(pLayer->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
CMapItemLayerSounds *pSoundLayer = (CMapItemLayerSounds *)pLayer;
|
||||||
|
|
||||||
|
if(pSoundLayer->m_Sound == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CSoundSource *pSources = (CSoundSource *)Layers()->Map()->GetDataSwapped(pSoundLayer->m_Data);
|
||||||
|
|
||||||
|
if(!pSources)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(int i = 0; i < pSoundLayer->m_NumSources; i++) {
|
||||||
|
CSourceQueueEntry source;
|
||||||
|
source.m_Sound = pSoundLayer->m_Sound;
|
||||||
|
source.m_pSource = &pSources[i];
|
||||||
|
|
||||||
|
if(!source.m_pSource || source.m_Sound == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_lSourceQueue.add(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMapSounds::OnRender()
|
||||||
|
{
|
||||||
|
if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// enqueue sounds
|
||||||
|
for(int i = 0; i < m_lSourceQueue.size(); i++)
|
||||||
|
{
|
||||||
|
CSourceQueueEntry *pSource = &m_lSourceQueue[i];
|
||||||
|
|
||||||
|
static float s_Time = 0.0f;
|
||||||
|
if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
|
||||||
|
{
|
||||||
|
s_Time = mix((Client()->PrevGameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)Client()->GameTickSpeed(),
|
||||||
|
(Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)Client()->GameTickSpeed(),
|
||||||
|
Client()->IntraGameTick());
|
||||||
|
}
|
||||||
|
float offset = s_Time-pSource->m_pSource->m_TimeDelay;
|
||||||
|
if(offset >= 0.0f)
|
||||||
|
{
|
||||||
|
if(pSource->m_Voice.IsValid())
|
||||||
|
{
|
||||||
|
// currently playing, set offset
|
||||||
|
Sound()->SetVoiceTimeOffset(pSource->m_Voice, offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// need to enqueue
|
||||||
|
int Flags = 0;
|
||||||
|
if(pSource->m_pSource->m_Loop) Flags |= ISound::FLAG_LOOP;
|
||||||
|
|
||||||
|
pSource->m_Voice = m_pClient->m_pSounds->PlaySampleAt(CSounds::CHN_AMBIENT, m_aSounds[pSource->m_Sound], 1.0f, vec2(fx2f(pSource->m_pSource->m_Position.x), fx2f(pSource->m_pSource->m_Position.y)), Flags);
|
||||||
|
Sound()->SetVoiceMaxDistance(pSource->m_Voice, pSource->m_pSource->m_FalloffDistance);
|
||||||
|
Sound()->SetVoiceTimeOffset(pSource->m_Voice, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// stop voice
|
||||||
|
Sound()->StopVoice(pSource->m_Voice);
|
||||||
|
pSource->m_Voice = ISound::CVoiceHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 Center = m_pClient->m_pCamera->m_Center;
|
||||||
|
for(int g = 0; g < Layers()->NumGroups(); g++)
|
||||||
|
{
|
||||||
|
CMapItemGroup *pGroup = Layers()->GetGroup(g);
|
||||||
|
|
||||||
|
if(!pGroup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(int l = 0; l < pGroup->m_NumLayers; l++)
|
||||||
|
{
|
||||||
|
CMapItemLayer *pLayer = Layers()->GetLayer(pGroup->m_StartLayer+l);
|
||||||
|
|
||||||
|
if(!pLayer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(pLayer->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
CMapItemLayerSounds *pSoundLayer = (CMapItemLayerSounds *)pLayer;
|
||||||
|
|
||||||
|
CSoundSource *pSources = (CSoundSource *)Layers()->Map()->GetDataSwapped(pSoundLayer->m_Data);
|
||||||
|
|
||||||
|
if(!pSources)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(int s = 0; s < pSoundLayer->m_NumSources; s++) {
|
||||||
|
for(int i = 0; i < m_lSourceQueue.size(); i++)
|
||||||
|
{
|
||||||
|
CSourceQueueEntry *pVoice = &m_lSourceQueue[i];
|
||||||
|
|
||||||
|
if(pVoice->m_pSource != &pSources[s])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!pVoice->m_Voice.IsValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float OffsetX = 0, OffsetY = 0;
|
||||||
|
|
||||||
|
if(pVoice->m_pSource->m_PosEnv >= 0)
|
||||||
|
{
|
||||||
|
float aChannels[4];
|
||||||
|
CMapLayers::EnvelopeEval(pVoice->m_pSource->m_PosEnvOffset/1000.0f, pVoice->m_pSource->m_PosEnv, aChannels, m_pClient->m_pMapLayersBackGround);
|
||||||
|
OffsetX = aChannels[0];
|
||||||
|
OffsetY = aChannels[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float x = fx2f(pVoice->m_pSource->m_Position.x)+OffsetX;
|
||||||
|
float y = fx2f(pVoice->m_pSource->m_Position.y)+OffsetY;
|
||||||
|
|
||||||
|
x += Center.x*(1.0f-pGroup->m_ParallaxX/100.0f);
|
||||||
|
y += Center.y*(1.0f-pGroup->m_ParallaxY/100.0f);
|
||||||
|
|
||||||
|
x -= pGroup->m_OffsetX; y -= pGroup->m_OffsetY;
|
||||||
|
|
||||||
|
Sound()->SetVoiceLocation(pVoice->m_Voice, x, y);
|
||||||
|
|
||||||
|
if(pVoice->m_pSource->m_SoundEnv >= 0)
|
||||||
|
{
|
||||||
|
float aChannels[4];
|
||||||
|
CMapLayers::EnvelopeEval(pVoice->m_pSource->m_SoundEnvOffset/1000.0f, pVoice->m_pSource->m_SoundEnv, aChannels, m_pClient->m_pMapLayersBackGround);
|
||||||
|
float Volume = clamp(aChannels[0], 0.0f, 1.0f);
|
||||||
|
|
||||||
|
Sound()->SetVoiceVolume(pVoice->m_Voice, Volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMapSounds::Clear()
|
||||||
|
{
|
||||||
|
// unload all samples
|
||||||
|
for(int i = 0; i < m_Count; i++)
|
||||||
|
{
|
||||||
|
Sound()->UnloadSample(m_aSounds[i]);
|
||||||
|
m_aSounds[i] = -1;
|
||||||
|
}
|
||||||
|
m_Count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMapSounds::OnStateChange(int NewState, int OldState)
|
||||||
|
{
|
||||||
|
if(NewState < IClient::STATE_ONLINE)
|
||||||
|
Clear();
|
||||||
|
}
|
34
src/game/client/components/mapsounds.h
Normal file
34
src/game/client/components/mapsounds.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <base/tl/array.h>
|
||||||
|
|
||||||
|
#include <engine/sound.h>
|
||||||
|
|
||||||
|
#include <game/client/component.h>
|
||||||
|
|
||||||
|
class CMapSounds : public CComponent
|
||||||
|
{
|
||||||
|
int m_aSounds[64];
|
||||||
|
int m_Count;
|
||||||
|
|
||||||
|
struct CSourceQueueEntry
|
||||||
|
{
|
||||||
|
int m_Sound;
|
||||||
|
ISound::CVoiceHandle m_Voice;
|
||||||
|
CSoundSource *m_pSource;
|
||||||
|
|
||||||
|
bool operator ==(const CSourceQueueEntry &Other) const { return (m_Sound == Other.m_Sound) && (m_Voice == Other.m_Voice) && (m_pSource == Other.m_pSource); }
|
||||||
|
};
|
||||||
|
|
||||||
|
array<CSourceQueueEntry> m_lSourceQueue;
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMapSounds();
|
||||||
|
|
||||||
|
virtual void OnMapLoad();
|
||||||
|
virtual void OnRender();
|
||||||
|
virtual void OnStateChange(int NewState, int OldState);
|
||||||
|
};
|
|
@ -1054,6 +1054,18 @@ void CMenus::RenderSettingsSound(CUIRect MainView)
|
||||||
g_Config.m_SndVolume = (int)(DoScrollbarH(&g_Config.m_SndVolume, &Button, g_Config.m_SndVolume/100.0f)*100.0f);
|
g_Config.m_SndVolume = (int)(DoScrollbarH(&g_Config.m_SndVolume, &Button, g_Config.m_SndVolume/100.0f)*100.0f);
|
||||||
MainView.HSplitTop(20.0f, 0, &MainView);
|
MainView.HSplitTop(20.0f, 0, &MainView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// volume slider map sounds
|
||||||
|
{
|
||||||
|
CUIRect Button, Label;
|
||||||
|
MainView.HSplitTop(5.0f, &Button, &MainView);
|
||||||
|
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||||
|
Button.VSplitLeft(190.0f, &Label, &Button);
|
||||||
|
Button.HMargin(2.0f, &Button);
|
||||||
|
UI()->DoLabelScaled(&Label, Localize("Ambient volume"), 14.0f, -1);
|
||||||
|
g_Config.m_SndAmbientVolume = (int)(DoScrollbarH(&g_Config.m_SndAmbientVolume, &Button, g_Config.m_SndAmbientVolume/100.0f)*100.0f);
|
||||||
|
MainView.HSplitTop(20.0f, 0, &MainView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CLanguage
|
class CLanguage
|
||||||
|
|
|
@ -64,10 +64,13 @@ int CSounds::GetSampleId(int SetId)
|
||||||
void CSounds::OnInit()
|
void CSounds::OnInit()
|
||||||
{
|
{
|
||||||
// setup sound channels
|
// setup sound channels
|
||||||
|
m_AmbientVolume = g_Config.m_SndAmbientVolume/100.0f;
|
||||||
|
|
||||||
Sound()->SetChannel(CSounds::CHN_GUI, 1.0f, 0.0f);
|
Sound()->SetChannel(CSounds::CHN_GUI, 1.0f, 0.0f);
|
||||||
Sound()->SetChannel(CSounds::CHN_MUSIC, 1.0f, 0.0f);
|
Sound()->SetChannel(CSounds::CHN_MUSIC, 1.0f, 0.0f);
|
||||||
Sound()->SetChannel(CSounds::CHN_WORLD, 0.9f, 1.0f);
|
Sound()->SetChannel(CSounds::CHN_WORLD, 0.9f, 1.0f);
|
||||||
Sound()->SetChannel(CSounds::CHN_GLOBAL, 1.0f, 0.0f);
|
Sound()->SetChannel(CSounds::CHN_GLOBAL, 1.0f, 0.0f);
|
||||||
|
Sound()->SetChannel(CSounds::CHN_AMBIENT, m_AmbientVolume, 1.0f);
|
||||||
|
|
||||||
Sound()->SetListenerPos(0.0f, 0.0f);
|
Sound()->SetListenerPos(0.0f, 0.0f);
|
||||||
|
|
||||||
|
@ -119,6 +122,14 @@ void CSounds::OnRender()
|
||||||
// set listner pos
|
// set listner pos
|
||||||
Sound()->SetListenerPos(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y);
|
Sound()->SetListenerPos(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y);
|
||||||
|
|
||||||
|
// update volume
|
||||||
|
float NewAmbientVol = g_Config.m_SndAmbientVolume/100.0f;
|
||||||
|
if(NewAmbientVol != m_AmbientVolume)
|
||||||
|
{
|
||||||
|
m_AmbientVolume = NewAmbientVol;
|
||||||
|
Sound()->SetChannel(CSounds::CHN_AMBIENT, m_AmbientVolume, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
// play sound from queue
|
// play sound from queue
|
||||||
if(m_QueuePos > 0)
|
if(m_QueuePos > 0)
|
||||||
{
|
{
|
||||||
|
@ -204,3 +215,25 @@ void CSounds::Stop(int SetId)
|
||||||
for(int i = 0; i < pSet->m_NumSounds; i++)
|
for(int i = 0; i < pSet->m_NumSounds; i++)
|
||||||
Sound()->Stop(pSet->m_aSounds[i].m_Id);
|
Sound()->Stop(pSet->m_aSounds[i].m_Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISound::CVoiceHandle CSounds::PlaySample(int Chn, int SampleId, float Vol, int Flags)
|
||||||
|
{
|
||||||
|
if((Chn == CHN_MUSIC && !g_Config.m_SndMusic) || SampleId == -1)
|
||||||
|
return ISound::CVoiceHandle();
|
||||||
|
|
||||||
|
if(Chn == CHN_MUSIC)
|
||||||
|
Flags |= ISound::FLAG_LOOP;
|
||||||
|
|
||||||
|
return Sound()->Play(Chn, SampleId, Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
ISound::CVoiceHandle CSounds::PlaySampleAt(int Chn, int SampleId, float Vol, vec2 Pos, int Flags)
|
||||||
|
{
|
||||||
|
if((Chn == CHN_MUSIC && !g_Config.m_SndMusic) || SampleId == -1)
|
||||||
|
return ISound::CVoiceHandle();
|
||||||
|
|
||||||
|
if(Chn == CHN_MUSIC)
|
||||||
|
Flags |= ISound::FLAG_LOOP;
|
||||||
|
|
||||||
|
return Sound()->PlayAt(Chn, SampleId, Flags, Pos.x, Pos.y);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||||
#ifndef GAME_CLIENT_COMPONENTS_SOUNDS_H
|
#ifndef GAME_CLIENT_COMPONENTS_SOUNDS_H
|
||||||
#define GAME_CLIENT_COMPONENTS_SOUNDS_H
|
#define GAME_CLIENT_COMPONENTS_SOUNDS_H
|
||||||
|
#include <engine/sound.h>
|
||||||
#include <game/client/component.h>
|
#include <game/client/component.h>
|
||||||
|
|
||||||
class CSounds : public CComponent
|
class CSounds : public CComponent
|
||||||
|
@ -22,6 +23,8 @@ class CSounds : public CComponent
|
||||||
|
|
||||||
int GetSampleId(int SetId);
|
int GetSampleId(int SetId);
|
||||||
|
|
||||||
|
float m_AmbientVolume;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// sound channels
|
// sound channels
|
||||||
enum
|
enum
|
||||||
|
@ -30,6 +33,7 @@ public:
|
||||||
CHN_MUSIC,
|
CHN_MUSIC,
|
||||||
CHN_WORLD,
|
CHN_WORLD,
|
||||||
CHN_GLOBAL,
|
CHN_GLOBAL,
|
||||||
|
CHN_AMBIENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void OnInit();
|
virtual void OnInit();
|
||||||
|
@ -43,6 +47,9 @@ public:
|
||||||
void PlayAt(int Channel, int SetId, float Vol, vec2 Pos);
|
void PlayAt(int Channel, int SetId, float Vol, vec2 Pos);
|
||||||
void PlayAndRecord(int Channel, int SetId, float Vol, vec2 Pos);
|
void PlayAndRecord(int Channel, int SetId, float Vol, vec2 Pos);
|
||||||
void Stop(int SetId);
|
void Stop(int SetId);
|
||||||
|
|
||||||
|
ISound::CVoiceHandle PlaySample(int Channel, int SampleId, float Vol, int Flags = 0);
|
||||||
|
ISound::CVoiceHandle PlaySampleAt(int Channel, int SampleId, float Vol, vec2 Pos, int Flags = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "components/killmessages.h"
|
#include "components/killmessages.h"
|
||||||
#include "components/mapimages.h"
|
#include "components/mapimages.h"
|
||||||
#include "components/maplayers.h"
|
#include "components/maplayers.h"
|
||||||
|
#include "components/mapsounds.h"
|
||||||
#include "components/menus.h"
|
#include "components/menus.h"
|
||||||
#include "components/motd.h"
|
#include "components/motd.h"
|
||||||
#include "components/particles.h"
|
#include "components/particles.h"
|
||||||
|
@ -94,6 +95,8 @@ static CMapImages gs_MapImages;
|
||||||
static CMapLayers gs_MapLayersBackGround(CMapLayers::TYPE_BACKGROUND);
|
static CMapLayers gs_MapLayersBackGround(CMapLayers::TYPE_BACKGROUND);
|
||||||
static CMapLayers gs_MapLayersForeGround(CMapLayers::TYPE_FOREGROUND);
|
static CMapLayers gs_MapLayersForeGround(CMapLayers::TYPE_FOREGROUND);
|
||||||
|
|
||||||
|
static CMapSounds gs_MapSounds;
|
||||||
|
|
||||||
static CRaceDemo gs_RaceDemo;
|
static CRaceDemo gs_RaceDemo;
|
||||||
static CGhost gs_Ghost;
|
static CGhost gs_Ghost;
|
||||||
|
|
||||||
|
@ -148,6 +151,8 @@ void CGameClient::OnConsoleInit()
|
||||||
m_pMapLayersBackGround = &::gs_MapLayersBackGround;
|
m_pMapLayersBackGround = &::gs_MapLayersBackGround;
|
||||||
m_pMapLayersForeGround = &::gs_MapLayersForeGround;
|
m_pMapLayersForeGround = &::gs_MapLayersForeGround;
|
||||||
|
|
||||||
|
m_pMapSounds = &::gs_MapSounds;
|
||||||
|
|
||||||
m_pRaceDemo = &::gs_RaceDemo;
|
m_pRaceDemo = &::gs_RaceDemo;
|
||||||
m_pGhost = &::gs_Ghost;
|
m_pGhost = &::gs_Ghost;
|
||||||
|
|
||||||
|
@ -164,6 +169,7 @@ void CGameClient::OnConsoleInit()
|
||||||
m_All.Add(m_pVoting);
|
m_All.Add(m_pVoting);
|
||||||
m_All.Add(m_pParticles); // doesn't render anything, just updates all the particles
|
m_All.Add(m_pParticles); // doesn't render anything, just updates all the particles
|
||||||
m_All.Add(m_pRaceDemo);
|
m_All.Add(m_pRaceDemo);
|
||||||
|
m_All.Add(m_pMapSounds);
|
||||||
|
|
||||||
m_All.Add(&gs_MapLayersBackGround); // first to render
|
m_All.Add(&gs_MapLayersBackGround); // first to render
|
||||||
m_All.Add(&m_pParticles->m_RenderTrail);
|
m_All.Add(&m_pParticles->m_RenderTrail);
|
||||||
|
|
|
@ -274,6 +274,8 @@ public:
|
||||||
class CMapLayers *m_pMapLayersBackGround;
|
class CMapLayers *m_pMapLayersBackGround;
|
||||||
class CMapLayers *m_pMapLayersForeGround;
|
class CMapLayers *m_pMapLayersForeGround;
|
||||||
|
|
||||||
|
class CMapSounds *m_pMapSounds;
|
||||||
|
|
||||||
// DDRace
|
// DDRace
|
||||||
|
|
||||||
class CRaceDemo *m_pRaceDemo;
|
class CRaceDemo *m_pRaceDemo;
|
||||||
|
|
|
@ -165,6 +165,38 @@ void CRenderTools::DrawUIRect(const CUIRect *r, vec4 Color, int Corners, float R
|
||||||
Graphics()->QuadsEnd();
|
Graphics()->QuadsEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRenderTools::DrawCircle(float x, float y, float r, int Segments)
|
||||||
|
{
|
||||||
|
IGraphics::CFreeformItem Array[32];
|
||||||
|
int NumItems = 0;
|
||||||
|
float FSegments = (float)Segments;
|
||||||
|
for(int i = 0; i < Segments; i+=2)
|
||||||
|
{
|
||||||
|
float a1 = i/FSegments * 2*pi;
|
||||||
|
float a2 = (i+1)/FSegments * 2*pi;
|
||||||
|
float a3 = (i+2)/FSegments * 2*pi;
|
||||||
|
float Ca1 = cosf(a1);
|
||||||
|
float Ca2 = cosf(a2);
|
||||||
|
float Ca3 = cosf(a3);
|
||||||
|
float Sa1 = sinf(a1);
|
||||||
|
float Sa2 = sinf(a2);
|
||||||
|
float Sa3 = sinf(a3);
|
||||||
|
|
||||||
|
Array[NumItems++] = IGraphics::CFreeformItem(
|
||||||
|
x, y,
|
||||||
|
x+Ca1*r, y+Sa1*r,
|
||||||
|
x+Ca3*r, y+Sa3*r,
|
||||||
|
x+Ca2*r, y+Sa2*r);
|
||||||
|
if(NumItems == 32)
|
||||||
|
{
|
||||||
|
Graphics()->QuadsDrawFreeform(Array, 32);
|
||||||
|
NumItems = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(NumItems)
|
||||||
|
Graphics()->QuadsDrawFreeform(Array, NumItems);
|
||||||
|
}
|
||||||
|
|
||||||
void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, bool Alpha)
|
void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, bool Alpha)
|
||||||
{
|
{
|
||||||
vec2 Direction = Dir;
|
vec2 Direction = Dir;
|
||||||
|
|
|
@ -63,6 +63,8 @@ public:
|
||||||
|
|
||||||
void DrawUIRect(const CUIRect *pRect, vec4 Color, int Corners, float Rounding);
|
void DrawUIRect(const CUIRect *pRect, vec4 Color, int Corners, float Rounding);
|
||||||
|
|
||||||
|
void DrawCircle(float x, float y, float r, int Segments);
|
||||||
|
|
||||||
// larger rendering methods
|
// larger rendering methods
|
||||||
void RenderTilemapGenerateSkip(class CLayers *pLayers);
|
void RenderTilemapGenerateSkip(class CLayers *pLayers);
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,16 @@ CEditorImage::~CEditorImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CEditorSound::~CEditorSound()
|
||||||
|
{
|
||||||
|
m_pEditor->Sound()->UnloadSample(m_SoundID);
|
||||||
|
if(m_pData)
|
||||||
|
{
|
||||||
|
delete[] m_pData;
|
||||||
|
m_pData = 0x0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CLayerGroup::CLayerGroup()
|
CLayerGroup::CLayerGroup()
|
||||||
{
|
{
|
||||||
m_aName[0] = 0;
|
m_aName[0] = 0;
|
||||||
|
@ -731,6 +741,16 @@ CQuad *CEditor::GetSelectedQuad()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSoundSource *CEditor::GetSelectedSource()
|
||||||
|
{
|
||||||
|
CLayerSounds *pSounds = (CLayerSounds *)GetSelectedLayerType(0, LAYERTYPE_SOUNDS);
|
||||||
|
if(!pSounds)
|
||||||
|
return 0;
|
||||||
|
if(m_SelectedSource >= 0 && m_SelectedSource < pSounds->m_lSources.size())
|
||||||
|
return &pSounds->m_lSources[m_SelectedSource];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
|
void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
|
||||||
{
|
{
|
||||||
CEditor *pEditor = (CEditor*)pUser;
|
CEditor *pEditor = (CEditor*)pUser;
|
||||||
|
@ -1001,6 +1021,31 @@ void CEditor::DoToolbar(CUIRect ToolBar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sound source manipulation
|
||||||
|
{
|
||||||
|
// do add button
|
||||||
|
TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
|
||||||
|
TB_Top.VSplitLeft(60.0f, &Button, &TB_Top);
|
||||||
|
static int s_NewButton = 0;
|
||||||
|
|
||||||
|
CLayerSounds *pSoundLayer = (CLayerSounds *)GetSelectedLayerType(0, LAYERTYPE_SOUNDS);
|
||||||
|
if(DoButton_Editor(&s_NewButton, "Add Source", pSoundLayer?0:-1, &Button, 0, "Adds a new sound source"))
|
||||||
|
{
|
||||||
|
if(pSoundLayer)
|
||||||
|
{
|
||||||
|
float Mapping[4];
|
||||||
|
CLayerGroup *g = GetSelectedGroup();
|
||||||
|
g->Mapping(Mapping);
|
||||||
|
int AddX = f2fx(Mapping[0] + (Mapping[2]-Mapping[0])/2);
|
||||||
|
int AddY = f2fx(Mapping[1] + (Mapping[3]-Mapping[1])/2);
|
||||||
|
|
||||||
|
CSoundSource *pSource = pSoundLayer->NewSource();
|
||||||
|
pSource->m_Position.x += AddX;
|
||||||
|
pSource->m_Position.y += AddY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// tile manipulation
|
// tile manipulation
|
||||||
{
|
{
|
||||||
TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom);
|
TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom);
|
||||||
|
@ -1111,6 +1156,136 @@ static void Rotate(const CPoint *pCenter, CPoint *pPoint, float Rotation)
|
||||||
pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
|
pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEditor::DoSoundSource(CSoundSource *pSource, int Index)
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
OP_NONE=0,
|
||||||
|
OP_MOVE,
|
||||||
|
OP_CONTEXT_MENU,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *pID = &pSource->m_Position;
|
||||||
|
|
||||||
|
static float s_LastWx;
|
||||||
|
static float s_LastWy;
|
||||||
|
static int s_Operation = OP_NONE;
|
||||||
|
|
||||||
|
float wx = UI()->MouseWorldX();
|
||||||
|
float wy = UI()->MouseWorldY();
|
||||||
|
|
||||||
|
float CenterX = fx2f(pSource->m_Position.x);
|
||||||
|
float CenterY = fx2f(pSource->m_Position.y);
|
||||||
|
|
||||||
|
float dx = (CenterX - wx)/m_WorldZoom;
|
||||||
|
float dy = (CenterY - wy)/m_WorldZoom;
|
||||||
|
if(dx*dx+dy*dy < 50)
|
||||||
|
UI()->SetHotItem(pID);
|
||||||
|
|
||||||
|
bool IgnoreGrid;
|
||||||
|
if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT))
|
||||||
|
IgnoreGrid = true;
|
||||||
|
else
|
||||||
|
IgnoreGrid = false;
|
||||||
|
|
||||||
|
if(UI()->ActiveItem() == pID)
|
||||||
|
{
|
||||||
|
if(m_MouseDeltaWx*m_MouseDeltaWx+m_MouseDeltaWy*m_MouseDeltaWy > 0.5f)
|
||||||
|
{
|
||||||
|
if(s_Operation == OP_MOVE)
|
||||||
|
{
|
||||||
|
if(m_GridActive && !IgnoreGrid)
|
||||||
|
{
|
||||||
|
int LineDistance = GetLineDistance();
|
||||||
|
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
if(wx >= 0)
|
||||||
|
x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
|
||||||
|
else
|
||||||
|
x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
|
||||||
|
if(wy >= 0)
|
||||||
|
y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
|
||||||
|
else
|
||||||
|
y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
|
||||||
|
|
||||||
|
pSource->m_Position.x = f2fx(x);
|
||||||
|
pSource->m_Position.y = f2fx(y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSource->m_Position.x += f2fx(wx-s_LastWx);
|
||||||
|
pSource->m_Position.y += f2fx(wy-s_LastWy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_LastWx = wx;
|
||||||
|
s_LastWy = wy;
|
||||||
|
|
||||||
|
if(s_Operation == OP_CONTEXT_MENU)
|
||||||
|
{
|
||||||
|
if(!UI()->MouseButton(1))
|
||||||
|
{
|
||||||
|
m_Map.m_UndoModified++;
|
||||||
|
|
||||||
|
static int s_SourcePopupID = 0;
|
||||||
|
UiInvokePopupMenu(&s_SourcePopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 180, PopupSource);
|
||||||
|
m_LockMouse = false;
|
||||||
|
s_Operation = OP_NONE;
|
||||||
|
UI()->SetActiveItem(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!UI()->MouseButton(0))
|
||||||
|
{
|
||||||
|
if(s_Operation == OP_MOVE)
|
||||||
|
{
|
||||||
|
m_Map.m_UndoModified++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_LockMouse = false;
|
||||||
|
s_Operation = OP_NONE;
|
||||||
|
UI()->SetActiveItem(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics()->SetColor(1,1,1,1);
|
||||||
|
}
|
||||||
|
else if(UI()->HotItem() == pID)
|
||||||
|
{
|
||||||
|
ms_pUiGotContext = pID;
|
||||||
|
|
||||||
|
Graphics()->SetColor(1,1,1,1);
|
||||||
|
m_pTooltip = "Left mouse button to move. Hold alt to ignore grid.";
|
||||||
|
|
||||||
|
if(UI()->MouseButton(0))
|
||||||
|
{
|
||||||
|
s_Operation = OP_MOVE;
|
||||||
|
|
||||||
|
UI()->SetActiveItem(pID);
|
||||||
|
m_SelectedSource = Index;
|
||||||
|
s_LastWx = wx;
|
||||||
|
s_LastWy = wy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(UI()->MouseButton(1))
|
||||||
|
{
|
||||||
|
m_SelectedSource = Index;
|
||||||
|
s_Operation = OP_CONTEXT_MENU;
|
||||||
|
UI()->SetActiveItem(pID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Graphics()->SetColor(0,1,0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
|
||||||
|
Graphics()->QuadsDraw(&QuadItem, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void CEditor::DoQuad(CQuad *q, int Index)
|
void CEditor::DoQuad(CQuad *q, int Index)
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
|
@ -2083,7 +2258,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// quad editing
|
// quad & sound editing
|
||||||
{
|
{
|
||||||
if(!m_ShowPicker && m_Brush.IsEmpty())
|
if(!m_ShowPicker && m_Brush.IsEmpty())
|
||||||
{
|
{
|
||||||
|
@ -2112,10 +2287,24 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
|
||||||
}
|
}
|
||||||
Graphics()->QuadsEnd();
|
Graphics()->QuadsEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pEditLayers[k]->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
CLayerSounds *pLayer = (CLayerSounds *)pEditLayers[k];
|
||||||
|
|
||||||
|
Graphics()->TextureSet(-1);
|
||||||
|
Graphics()->QuadsBegin();
|
||||||
|
for(int i = 0; i < pLayer->m_lSources.size(); i++)
|
||||||
|
{
|
||||||
|
DoSoundSource(&pLayer->m_lSources[i], i);
|
||||||
|
}
|
||||||
|
Graphics()->QuadsEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
|
Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// do panning
|
// do panning
|
||||||
if(UI()->ActiveItem() == s_pEditorID)
|
if(UI()->ActiveItem() == s_pEditorID)
|
||||||
|
@ -2141,7 +2330,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
|
||||||
UI()->SetActiveItem(0);
|
UI()->SetActiveItem(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(UI()->ActiveItem() == s_pEditorID)
|
else if(UI()->ActiveItem() == s_pEditorID)
|
||||||
{
|
{
|
||||||
|
@ -2416,6 +2606,24 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *
|
||||||
Change = i;
|
Change = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(pProps[i].m_Type == PROPTYPE_SOUND)
|
||||||
|
{
|
||||||
|
char aBuf[64];
|
||||||
|
if(pProps[i].m_Value < 0)
|
||||||
|
str_copy(aBuf, "None", sizeof(aBuf));
|
||||||
|
else
|
||||||
|
str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lSounds[pProps[i].m_Value]->m_aName);
|
||||||
|
|
||||||
|
if(DoButton_Editor(&pIDs[i], aBuf, 0, &Shifter, 0, 0))
|
||||||
|
PopupSelectSoundInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
|
||||||
|
|
||||||
|
int r = PopupSelectSoundResult();
|
||||||
|
if(r >= -1)
|
||||||
|
{
|
||||||
|
*pNewVal = r;
|
||||||
|
Change = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Change;
|
return Change;
|
||||||
|
@ -2513,7 +2721,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
|
||||||
|
|
||||||
static int s_GroupPopupId = 0;
|
static int s_GroupPopupId = 0;
|
||||||
if(Result == 2)
|
if(Result == 2)
|
||||||
UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup);
|
UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 230, PopupGroup);
|
||||||
|
|
||||||
if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick())
|
if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick())
|
||||||
m_Map.m_lGroups[g]->m_Collapse ^= 1;
|
m_Map.m_lGroups[g]->m_Collapse ^= 1;
|
||||||
|
@ -2657,6 +2865,57 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
|
||||||
pEditor->m_Dialog = DIALOG_NONE;
|
pEditor->m_Dialog = DIALOG_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEditor::AddSound(const char *pFileName, int StorageType, void *pUser)
|
||||||
|
{
|
||||||
|
CEditor *pEditor = (CEditor *)pUser;
|
||||||
|
|
||||||
|
// check if we have that image already
|
||||||
|
char aBuf[128];
|
||||||
|
ExtractName(pFileName, aBuf, sizeof(aBuf));
|
||||||
|
for(int i = 0; i < pEditor->m_Map.m_lSounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if(!str_comp(pEditor->m_Map.m_lSounds[i]->m_aName, aBuf))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load external
|
||||||
|
IOHANDLE SoundFile = pEditor->Storage()->OpenFile(pFileName, IOFLAG_READ, IStorage::TYPE_ALL);
|
||||||
|
if(!SoundFile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// read the whole file into memory
|
||||||
|
unsigned DataSize = io_length(SoundFile);
|
||||||
|
void *pData = new char[DataSize];
|
||||||
|
io_read(SoundFile, pData, DataSize);
|
||||||
|
io_close(SoundFile);
|
||||||
|
|
||||||
|
// load sound
|
||||||
|
int SoundId = pEditor->Sound()->LoadWVFromMem(pData, DataSize);
|
||||||
|
if(SoundId == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add sound
|
||||||
|
CEditorSound *pSound = new CEditorSound(pEditor);
|
||||||
|
pSound->m_SoundID = SoundId;
|
||||||
|
pSound->m_External = 1; // external by default
|
||||||
|
pSound->m_DataSize = DataSize;
|
||||||
|
pSound->m_pData = pData;
|
||||||
|
str_copy(pSound->m_aName, aBuf, sizeof(pSound->m_aName));
|
||||||
|
pEditor->m_Map.m_lSounds.add(pSound);
|
||||||
|
|
||||||
|
if(pEditor->m_SelectedSound > -1 && pEditor->m_SelectedSound < pEditor->m_Map.m_lSounds.size())
|
||||||
|
{
|
||||||
|
for(int i = 0; i <= pEditor->m_SelectedSound; ++i)
|
||||||
|
if(!str_comp(pEditor->m_Map.m_lSounds[i]->m_aName, aBuf))
|
||||||
|
{
|
||||||
|
pEditor->m_SelectedSound++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pEditor->m_Dialog = DIALOG_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int gs_ModifyIndexDeletedIndex;
|
static int gs_ModifyIndexDeletedIndex;
|
||||||
static void ModifyIndexDeleted(int *pIndex)
|
static void ModifyIndexDeleted(int *pIndex)
|
||||||
|
@ -2717,6 +2976,57 @@ int CEditor::PopupImage(CEditor *pEditor, CUIRect View)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CEditor::PopupSound(CEditor *pEditor, CUIRect View)
|
||||||
|
{
|
||||||
|
static int s_ReplaceButton = 0;
|
||||||
|
static int s_RemoveButton = 0;
|
||||||
|
|
||||||
|
CUIRect Slot;
|
||||||
|
View.HSplitTop(2.0f, &Slot, &View);
|
||||||
|
View.HSplitTop(12.0f, &Slot, &View);
|
||||||
|
CEditorSound *pSound = pEditor->m_Map.m_lSounds[pEditor->m_SelectedImage];
|
||||||
|
|
||||||
|
static int s_ExternalButton = 0;
|
||||||
|
if(pSound->m_External)
|
||||||
|
{
|
||||||
|
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the sound into the map file."))
|
||||||
|
{
|
||||||
|
pSound->m_External = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the sound from the map file."))
|
||||||
|
{
|
||||||
|
pSound->m_External = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
View.HSplitTop(10.0f, &Slot, &View);
|
||||||
|
View.HSplitTop(12.0f, &Slot, &View);
|
||||||
|
if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the sound with a new one"))
|
||||||
|
{
|
||||||
|
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace sound", "Replace", "mapres", "", Replacesound, pEditor);
|
||||||
|
return 1;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
View.HSplitTop(10.0f, &Slot, &View);
|
||||||
|
View.HSplitTop(12.0f, &Slot, &View);
|
||||||
|
if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the sound from the map"))
|
||||||
|
{
|
||||||
|
delete pSound;
|
||||||
|
pEditor->m_Map.m_lSounds.remove_index(pEditor->m_SelectedSound);
|
||||||
|
gs_ModifyIndexDeletedIndex = pEditor->m_SelectedSound;
|
||||||
|
pEditor->m_Map.ModifySoundIndex(ModifyIndexDeleted);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int CompareImageName(const void *pObject1, const void *pObject2)
|
static int CompareImageName(const void *pObject1, const void *pObject2)
|
||||||
{
|
{
|
||||||
CEditorImage *pImage1 = *(CEditorImage**)pObject1;
|
CEditorImage *pImage1 = *(CEditorImage**)pObject1;
|
||||||
|
@ -2918,6 +3228,129 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
|
||||||
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
|
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEditor::RenderSounds(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
|
||||||
|
{
|
||||||
|
static int s_ScrollBar = 0;
|
||||||
|
static float s_ScrollValue = 0;
|
||||||
|
float SoundsHeight = 30.0f + 14.0f * m_Map.m_lSounds.size() + 27.0f;
|
||||||
|
float ScrollDifference = SoundsHeight - ToolBox.h;
|
||||||
|
|
||||||
|
if(SoundsHeight > ToolBox.h) // Do we even need a scrollbar?
|
||||||
|
{
|
||||||
|
CUIRect Scroll;
|
||||||
|
ToolBox.VSplitRight(15.0f, &ToolBox, &Scroll);
|
||||||
|
ToolBox.VSplitRight(3.0f, &ToolBox, 0); // extra spacing
|
||||||
|
Scroll.HMargin(5.0f, &Scroll);
|
||||||
|
s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
|
||||||
|
|
||||||
|
if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox))
|
||||||
|
{
|
||||||
|
int ScrollNum = (int)((SoundsHeight-ToolBox.h)/14.0f)+1;
|
||||||
|
if(ScrollNum > 0)
|
||||||
|
{
|
||||||
|
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
|
||||||
|
s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
|
||||||
|
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
|
||||||
|
s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ScrollNum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float SoundStartAt = ScrollDifference * s_ScrollValue;
|
||||||
|
if(SoundStartAt < 0.0f)
|
||||||
|
SoundStartAt = 0.0f;
|
||||||
|
|
||||||
|
float SoundStopAt = SoundsHeight - ScrollDifference * (1 - s_ScrollValue);
|
||||||
|
float ImageCur = 0.0f;
|
||||||
|
|
||||||
|
for(int e = 0; e < 2; e++) // two passes, first embedded, then external
|
||||||
|
{
|
||||||
|
CUIRect Slot;
|
||||||
|
|
||||||
|
if(ImageCur > SoundStopAt)
|
||||||
|
break;
|
||||||
|
else if(ImageCur >= SoundStartAt)
|
||||||
|
{
|
||||||
|
|
||||||
|
ToolBox.HSplitTop(15.0f, &Slot, &ToolBox);
|
||||||
|
if(e == 0)
|
||||||
|
UI()->DoLabel(&Slot, "Embedded", 12.0f, 0);
|
||||||
|
else
|
||||||
|
UI()->DoLabel(&Slot, "External", 12.0f, 0);
|
||||||
|
}
|
||||||
|
ImageCur += 15.0f;
|
||||||
|
|
||||||
|
for(int i = 0; i < m_Map.m_lSounds.size(); i++)
|
||||||
|
{
|
||||||
|
if((e && !m_Map.m_lSounds[i]->m_External) ||
|
||||||
|
(!e && m_Map.m_lSounds[i]->m_External))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImageCur > SoundStopAt)
|
||||||
|
break;
|
||||||
|
else if(ImageCur < SoundStartAt)
|
||||||
|
{
|
||||||
|
ImageCur += 14.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ImageCur += 14.0f;
|
||||||
|
|
||||||
|
char aBuf[128];
|
||||||
|
str_copy(aBuf, m_Map.m_lSounds[i]->m_aName, sizeof(aBuf));
|
||||||
|
ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
|
||||||
|
|
||||||
|
|
||||||
|
int Selected = m_SelectedSound == i;
|
||||||
|
for(int x = 0; x < m_Map.m_lGroups.size(); ++x)
|
||||||
|
for(int j = 0; j < m_Map.m_lGroups[x]->m_lLayers.size(); ++j)
|
||||||
|
if(m_Map.m_lGroups[x]->m_lLayers[j]->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
CLayerSounds *pLayer = static_cast<CLayerSounds *>(m_Map.m_lGroups[x]->m_lLayers[j]);
|
||||||
|
if(pLayer->m_Sound == i)
|
||||||
|
{
|
||||||
|
Selected = 2 + Selected;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if(int Result = DoButton_Editor(&m_Map.m_lSounds[i], aBuf, Selected, &Slot,
|
||||||
|
BUTTON_CONTEXT, "Select sound"))
|
||||||
|
{
|
||||||
|
m_SelectedSound = i;
|
||||||
|
|
||||||
|
static int s_PopupSoundID = 0;
|
||||||
|
if(Result == 2)
|
||||||
|
UiInvokePopupMenu(&s_PopupSoundID, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolBox.HSplitTop(2.0f, 0, &ToolBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
// separator
|
||||||
|
ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
|
||||||
|
ImageCur += 5.0f;
|
||||||
|
IGraphics::CLineItem LineItem(Slot.x, Slot.y+Slot.h/2, Slot.x+Slot.w, Slot.y+Slot.h/2);
|
||||||
|
Graphics()->TextureSet(-1);
|
||||||
|
Graphics()->LinesBegin();
|
||||||
|
Graphics()->LinesDraw(&LineItem, 1);
|
||||||
|
Graphics()->LinesEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
CUIRect Slot;
|
||||||
|
ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
|
||||||
|
|
||||||
|
// new Sound
|
||||||
|
static int s_NewSoundButton = 0;
|
||||||
|
ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
|
||||||
|
if(DoButton_Editor(&s_NewSoundButton, "Add", 0, &Slot, 0, "Load a new sound to use in the map"))
|
||||||
|
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Add Sound", "Add", "mapres", "", AddSound, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
|
static int EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
|
||||||
{
|
{
|
||||||
|
@ -2926,15 +3359,21 @@ static int EditorListdirCallback(const char *pName, int IsDir, int StorageType,
|
||||||
if((pName[0] == '.' && (pName[1] == 0 ||
|
if((pName[0] == '.' && (pName[1] == 0 ||
|
||||||
(pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
|
(pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
|
||||||
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && (Length < 4 || str_comp(pName+Length-4, ".map"))) ||
|
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && (Length < 4 || str_comp(pName+Length-4, ".map"))) ||
|
||||||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && (Length < 4 || str_comp(pName+Length-4, ".png"))))))
|
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && (Length < 4 || str_comp(pName+Length-4, ".png"))) ||
|
||||||
|
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && (Length < 3 || str_comp(pName+Length-3, ".wv"))))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
CEditor::CFilelistItem Item;
|
CEditor::CFilelistItem Item;
|
||||||
str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
|
str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
|
||||||
if(IsDir)
|
if(IsDir)
|
||||||
str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
|
str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND)
|
||||||
|
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-2));
|
||||||
else
|
else
|
||||||
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-3));
|
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-3));
|
||||||
|
}
|
||||||
Item.m_IsDir = IsDir != 0;
|
Item.m_IsDir = IsDir != 0;
|
||||||
Item.m_IsLink = false;
|
Item.m_IsLink = false;
|
||||||
Item.m_StorageType = StorageType;
|
Item.m_StorageType = StorageType;
|
||||||
|
@ -3300,11 +3739,21 @@ void CEditor::RenderModebar(CUIRect View)
|
||||||
View.VSplitLeft(65.0f, &Button, &View);
|
View.VSplitLeft(65.0f, &Button, &View);
|
||||||
Button.HSplitTop(30.0f, 0, &Button);
|
Button.HSplitTop(30.0f, 0, &Button);
|
||||||
static int s_Button = 0;
|
static int s_Button = 0;
|
||||||
const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images";
|
const char *pButName = "";
|
||||||
if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment."))
|
|
||||||
|
if(m_Mode == MODE_LAYERS)
|
||||||
|
pButName = "Layers";
|
||||||
|
else if(m_Mode == MODE_IMAGES)
|
||||||
|
pButName = "Images";
|
||||||
|
else if(m_Mode == MODE_SOUNDS)
|
||||||
|
pButName = "Sounds";
|
||||||
|
|
||||||
|
if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images, sounds and layers managment."))
|
||||||
{
|
{
|
||||||
if (m_Mode == MODE_LAYERS)
|
if (m_Mode == MODE_LAYERS)
|
||||||
m_Mode = MODE_IMAGES;
|
m_Mode = MODE_IMAGES;
|
||||||
|
else if(m_Mode == MODE_IMAGES)
|
||||||
|
m_Mode = MODE_SOUNDS;
|
||||||
else
|
else
|
||||||
m_Mode = MODE_LAYERS;
|
m_Mode = MODE_LAYERS;
|
||||||
}
|
}
|
||||||
|
@ -3423,6 +3872,17 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
|
||||||
CUIRect Button;
|
CUIRect Button;
|
||||||
CEnvelope *pNewEnv = 0;
|
CEnvelope *pNewEnv = 0;
|
||||||
|
|
||||||
|
|
||||||
|
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
|
||||||
|
static int s_NewSoundButton = 0;
|
||||||
|
if(DoButton_Editor(&s_NewSoundButton, "Sound.+", 0, &Button, 0, "Creates a new sound envelope"))
|
||||||
|
{
|
||||||
|
m_Map.m_Modified = true;
|
||||||
|
m_Map.m_UndoModified++;
|
||||||
|
pNewEnv = m_Map.NewEnvelope(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolBar.VSplitRight(5.0f, &ToolBar, &Button);
|
||||||
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
|
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
|
||||||
static int s_New4dButton = 0;
|
static int s_New4dButton = 0;
|
||||||
if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
|
if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
|
||||||
|
@ -3530,12 +3990,16 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
|
||||||
|
|
||||||
ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
|
ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
|
||||||
|
|
||||||
static const char *s_paNames[2][4] = {
|
static const char *s_paNames[4][4] = {
|
||||||
|
{"V", "", "", ""},
|
||||||
|
{"", "", "", ""},
|
||||||
{"X", "Y", "R", ""},
|
{"X", "Y", "R", ""},
|
||||||
{"R", "G", "B", "A"},
|
{"R", "G", "B", "A"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *paDescriptions[2][4] = {
|
const char *paDescriptions[4][4] = {
|
||||||
|
{"Volume of the envelope", "", "", ""},
|
||||||
|
{"", "", "", ""},
|
||||||
{"X-axis of the envelope", "Y-axis of the envelope", "Rotation of the envelope", ""},
|
{"X-axis of the envelope", "Y-axis of the envelope", "Rotation of the envelope", ""},
|
||||||
{"Red value of the envelope", "Green value of the envelope", "Blue value of the envelope", "Alpha value of the envelope"},
|
{"Red value of the envelope", "Green value of the envelope", "Blue value of the envelope", "Alpha value of the envelope"},
|
||||||
};
|
};
|
||||||
|
@ -3552,7 +4016,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
|
||||||
else if(i == envelope->channels-1) draw_func = draw_editor_button_r;
|
else if(i == envelope->channels-1) draw_func = draw_editor_button_r;
|
||||||
else draw_func = draw_editor_button_m;*/
|
else draw_func = draw_editor_button_m;*/
|
||||||
|
|
||||||
if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-3][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-3][i]))
|
if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-1][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-1][i]))
|
||||||
s_ActiveChannels ^= Bit;
|
s_ActiveChannels ^= Bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4091,6 +4555,8 @@ void CEditor::Render()
|
||||||
RenderLayers(ToolBox, ToolBar, View);
|
RenderLayers(ToolBox, ToolBar, View);
|
||||||
else if(m_Mode == MODE_IMAGES)
|
else if(m_Mode == MODE_IMAGES)
|
||||||
RenderImages(ToolBox, ToolBar, View);
|
RenderImages(ToolBox, ToolBar, View);
|
||||||
|
else if(m_Mode == MODE_SOUNDS)
|
||||||
|
RenderSounds(ToolBox, ToolBar, View);
|
||||||
|
|
||||||
Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
|
Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
|
||||||
|
|
||||||
|
@ -4198,6 +4664,8 @@ void CEditor::Reset(bool CreateDefault)
|
||||||
m_SelectedPoints = 0;
|
m_SelectedPoints = 0;
|
||||||
m_SelectedEnvelope = 0;
|
m_SelectedEnvelope = 0;
|
||||||
m_SelectedImage = 0;
|
m_SelectedImage = 0;
|
||||||
|
m_SelectedSound = 0;
|
||||||
|
m_SelectedSource = -1;
|
||||||
|
|
||||||
m_WorldOffsetX = 0;
|
m_WorldOffsetX = 0;
|
||||||
m_WorldOffsetY = 0;
|
m_WorldOffsetY = 0;
|
||||||
|
@ -4318,6 +4786,7 @@ void CEditorMap::Clean()
|
||||||
m_lGroups.delete_all();
|
m_lGroups.delete_all();
|
||||||
m_lEnvelopes.delete_all();
|
m_lEnvelopes.delete_all();
|
||||||
m_lImages.delete_all();
|
m_lImages.delete_all();
|
||||||
|
m_lSounds.delete_all();
|
||||||
|
|
||||||
m_MapInfo.Reset();
|
m_MapInfo.Reset();
|
||||||
|
|
||||||
|
@ -4376,6 +4845,7 @@ void CEditor::Init()
|
||||||
m_pGraphics = Kernel()->RequestInterface<IGraphics>();
|
m_pGraphics = Kernel()->RequestInterface<IGraphics>();
|
||||||
m_pTextRender = Kernel()->RequestInterface<ITextRender>();
|
m_pTextRender = Kernel()->RequestInterface<ITextRender>();
|
||||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||||
|
m_pSound = Kernel()->RequestInterface<ISound>();
|
||||||
m_RenderTools.m_pGraphics = m_pGraphics;
|
m_RenderTools.m_pGraphics = m_pGraphics;
|
||||||
m_RenderTools.m_pUI = &m_UI;
|
m_RenderTools.m_pUI = &m_UI;
|
||||||
m_UI.SetGraphics(m_pGraphics, m_pTextRender);
|
m_UI.SetGraphics(m_pGraphics, m_pTextRender);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <engine/shared/datafile.h>
|
#include <engine/shared/datafile.h>
|
||||||
#include <engine/editor.h>
|
#include <engine/editor.h>
|
||||||
#include <engine/graphics.h>
|
#include <engine/graphics.h>
|
||||||
|
#include <engine/sound.h>
|
||||||
|
|
||||||
#include "auto_map.h"
|
#include "auto_map.h"
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ enum
|
||||||
{
|
{
|
||||||
MODE_LAYERS=0,
|
MODE_LAYERS=0,
|
||||||
MODE_IMAGES,
|
MODE_IMAGES,
|
||||||
|
MODE_SOUNDS,
|
||||||
|
|
||||||
DIALOG_NONE=0,
|
DIALOG_NONE=0,
|
||||||
DIALOG_FILE,
|
DIALOG_FILE,
|
||||||
|
@ -155,6 +157,7 @@ public:
|
||||||
|
|
||||||
virtual void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
virtual void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
||||||
virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
||||||
|
virtual void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
||||||
|
|
||||||
virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;}
|
virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;}
|
||||||
|
|
||||||
|
@ -263,6 +266,12 @@ public:
|
||||||
for(int i = 0; i < m_lLayers.size(); i++)
|
for(int i = 0; i < m_lLayers.size(); i++)
|
||||||
m_lLayers[i]->ModifyEnvelopeIndex(Func);
|
m_lLayers[i]->ModifyEnvelopeIndex(Func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModifySoundIndex(INDEX_MODIFY_FUNC Func)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < m_lLayers.size(); i++)
|
||||||
|
m_lLayers[i]->ModifySoundIndex(Func);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CEditorImage : public CImageInfo
|
class CEditorImage : public CImageInfo
|
||||||
|
@ -294,6 +303,32 @@ public:
|
||||||
class CAutoMapper m_AutoMapper;
|
class CAutoMapper m_AutoMapper;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CEditorSound
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CEditor *m_pEditor;
|
||||||
|
|
||||||
|
CEditorSound(CEditor *pEditor)
|
||||||
|
{
|
||||||
|
m_pEditor = pEditor;
|
||||||
|
m_aName[0] = 0;
|
||||||
|
m_External = 0;
|
||||||
|
m_SoundID = 0;
|
||||||
|
|
||||||
|
m_pData = 0x0;
|
||||||
|
m_DataSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CEditorSound();
|
||||||
|
|
||||||
|
int m_SoundID;
|
||||||
|
int m_External;
|
||||||
|
char m_aName[128];
|
||||||
|
|
||||||
|
void *m_pData;
|
||||||
|
unsigned m_DataSize;
|
||||||
|
};
|
||||||
|
|
||||||
class CEditorMap
|
class CEditorMap
|
||||||
{
|
{
|
||||||
void MakeGameGroup(CLayerGroup *pGroup);
|
void MakeGameGroup(CLayerGroup *pGroup);
|
||||||
|
@ -311,6 +346,7 @@ public:
|
||||||
array<CLayerGroup*> m_lGroups;
|
array<CLayerGroup*> m_lGroups;
|
||||||
array<CEditorImage*> m_lImages;
|
array<CEditorImage*> m_lImages;
|
||||||
array<CEnvelope*> m_lEnvelopes;
|
array<CEnvelope*> m_lEnvelopes;
|
||||||
|
array<CEditorSound*> m_lSounds;
|
||||||
|
|
||||||
class CMapInfo
|
class CMapInfo
|
||||||
{
|
{
|
||||||
|
@ -400,6 +436,14 @@ public:
|
||||||
m_lGroups[i]->ModifyEnvelopeIndex(pfnFunc);
|
m_lGroups[i]->ModifyEnvelopeIndex(pfnFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc)
|
||||||
|
{
|
||||||
|
m_Modified = true;
|
||||||
|
m_UndoModified++;
|
||||||
|
for(int i = 0; i < m_lGroups.size(); i++)
|
||||||
|
m_lGroups[i]->ModifySoundIndex(pfnFunc);
|
||||||
|
}
|
||||||
|
|
||||||
void Clean();
|
void Clean();
|
||||||
void CreateDefault(int EntitiesTexture);
|
void CreateDefault(int EntitiesTexture);
|
||||||
|
|
||||||
|
@ -441,6 +485,7 @@ enum
|
||||||
PROPTYPE_IMAGE,
|
PROPTYPE_IMAGE,
|
||||||
PROPTYPE_ENVELOPE,
|
PROPTYPE_ENVELOPE,
|
||||||
PROPTYPE_SHIFT,
|
PROPTYPE_SHIFT,
|
||||||
|
PROPTYPE_SOUND,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -548,6 +593,7 @@ class CEditor : public IEditor
|
||||||
class IConsole *m_pConsole;
|
class IConsole *m_pConsole;
|
||||||
class IGraphics *m_pGraphics;
|
class IGraphics *m_pGraphics;
|
||||||
class ITextRender *m_pTextRender;
|
class ITextRender *m_pTextRender;
|
||||||
|
class ISound *m_pSound;
|
||||||
class IStorage *m_pStorage;
|
class IStorage *m_pStorage;
|
||||||
CRenderTools m_RenderTools;
|
CRenderTools m_RenderTools;
|
||||||
CUI m_UI;
|
CUI m_UI;
|
||||||
|
@ -556,6 +602,7 @@ public:
|
||||||
class IClient *Client() { return m_pClient; };
|
class IClient *Client() { return m_pClient; };
|
||||||
class IConsole *Console() { return m_pConsole; };
|
class IConsole *Console() { return m_pConsole; };
|
||||||
class IGraphics *Graphics() { return m_pGraphics; };
|
class IGraphics *Graphics() { return m_pGraphics; };
|
||||||
|
class ISound *Sound() { return m_pSound; }
|
||||||
class ITextRender *TextRender() { return m_pTextRender; };
|
class ITextRender *TextRender() { return m_pTextRender; };
|
||||||
class IStorage *Storage() { return m_pStorage; };
|
class IStorage *Storage() { return m_pStorage; };
|
||||||
CUI *UI() { return &m_UI; }
|
CUI *UI() { return &m_UI; }
|
||||||
|
@ -567,6 +614,7 @@ public:
|
||||||
m_pClient = 0;
|
m_pClient = 0;
|
||||||
m_pGraphics = 0;
|
m_pGraphics = 0;
|
||||||
m_pTextRender = 0;
|
m_pTextRender = 0;
|
||||||
|
m_pSound = 0;
|
||||||
|
|
||||||
m_Mode = MODE_LAYERS;
|
m_Mode = MODE_LAYERS;
|
||||||
m_Dialog = 0;
|
m_Dialog = 0;
|
||||||
|
@ -692,6 +740,7 @@ public:
|
||||||
CLayer *GetSelectedLayerType(int Index, int Type);
|
CLayer *GetSelectedLayerType(int Index, int Type);
|
||||||
CLayer *GetSelectedLayer(int Index);
|
CLayer *GetSelectedLayer(int Index);
|
||||||
CLayerGroup *GetSelectedGroup();
|
CLayerGroup *GetSelectedGroup();
|
||||||
|
CSoundSource *GetSelectedSource();
|
||||||
|
|
||||||
int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIDs, int *pNewVal, vec4 color = vec4(1,1,1,0.5f));
|
int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIDs, int *pNewVal, vec4 color = vec4(1,1,1,0.5f));
|
||||||
|
|
||||||
|
@ -723,6 +772,7 @@ public:
|
||||||
{
|
{
|
||||||
FILETYPE_MAP,
|
FILETYPE_MAP,
|
||||||
FILETYPE_IMG,
|
FILETYPE_IMG,
|
||||||
|
FILETYPE_SOUND,
|
||||||
|
|
||||||
MAX_PATH_LENGTH = 512
|
MAX_PATH_LENGTH = 512
|
||||||
};
|
};
|
||||||
|
@ -801,6 +851,8 @@ public:
|
||||||
int m_SelectedEnvelopePoint;
|
int m_SelectedEnvelopePoint;
|
||||||
int m_SelectedQuadEnvelope;
|
int m_SelectedQuadEnvelope;
|
||||||
int m_SelectedImage;
|
int m_SelectedImage;
|
||||||
|
int m_SelectedSound;
|
||||||
|
int m_SelectedSource;
|
||||||
|
|
||||||
static int ms_CheckerTexture;
|
static int ms_CheckerTexture;
|
||||||
static int ms_BackgroundTexture;
|
static int ms_BackgroundTexture;
|
||||||
|
@ -851,10 +903,13 @@ public:
|
||||||
static int PopupMapInfo(CEditor *pEditor, CUIRect View);
|
static int PopupMapInfo(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupEvent(CEditor *pEditor, CUIRect View);
|
static int PopupEvent(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupSelectImage(CEditor *pEditor, CUIRect View);
|
static int PopupSelectImage(CEditor *pEditor, CUIRect View);
|
||||||
|
static int PopupSelectSound(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View);
|
static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupImage(CEditor *pEditor, CUIRect View);
|
static int PopupImage(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupMenuFile(CEditor *pEditor, CUIRect View);
|
static int PopupMenuFile(CEditor *pEditor, CUIRect View);
|
||||||
static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View);
|
static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View);
|
||||||
|
static int PopupSound(CEditor *pEditor, CUIRect View);
|
||||||
|
static int PopupSource(CEditor *pEditor, CUIRect View);
|
||||||
|
|
||||||
static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
|
static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
|
||||||
static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
|
static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
|
||||||
|
@ -869,12 +924,17 @@ public:
|
||||||
void PopupSelectConfigAutoMapInvoke(float x, float y);
|
void PopupSelectConfigAutoMapInvoke(float x, float y);
|
||||||
int PopupSelectConfigAutoMapResult();
|
int PopupSelectConfigAutoMapResult();
|
||||||
|
|
||||||
|
void PopupSelectSoundInvoke(int Current, float x, float y);
|
||||||
|
int PopupSelectSoundResult();
|
||||||
|
|
||||||
vec4 ButtonColorMul(const void *pID);
|
vec4 ButtonColorMul(const void *pID);
|
||||||
|
|
||||||
void DoQuadEnvelopes(const array<CQuad> &m_lQuads, int TexID = -1);
|
void DoQuadEnvelopes(const array<CQuad> &m_lQuads, int TexID = -1);
|
||||||
void DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int pIndex);
|
void DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int pIndex);
|
||||||
void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v);
|
void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v);
|
||||||
|
|
||||||
|
void DoSoundSource(CSoundSource *pSource, int Index);
|
||||||
|
|
||||||
void DoMapEditor(CUIRect View, CUIRect Toolbar);
|
void DoMapEditor(CUIRect View, CUIRect Toolbar);
|
||||||
void DoToolbar(CUIRect Toolbar);
|
void DoToolbar(CUIRect Toolbar);
|
||||||
void DoQuad(CQuad *pQuad, int Index);
|
void DoQuad(CQuad *pQuad, int Index);
|
||||||
|
@ -883,9 +943,11 @@ public:
|
||||||
|
|
||||||
static void ReplaceImage(const char *pFilename, int StorageType, void *pUser);
|
static void ReplaceImage(const char *pFilename, int StorageType, void *pUser);
|
||||||
static void AddImage(const char *pFilename, int StorageType, void *pUser);
|
static void AddImage(const char *pFilename, int StorageType, void *pUser);
|
||||||
|
static void AddSound(const char *pFileName, int StorageType, void *pUser);
|
||||||
|
|
||||||
void RenderImages(CUIRect Toolbox, CUIRect Toolbar, CUIRect View);
|
void RenderImages(CUIRect Toolbox, CUIRect Toolbar, CUIRect View);
|
||||||
void RenderLayers(CUIRect Toolbox, CUIRect Toolbar, CUIRect View);
|
void RenderLayers(CUIRect Toolbox, CUIRect Toolbar, CUIRect View);
|
||||||
|
void RenderSounds(CUIRect Toolbox, CUIRect Toolbar, CUIRect View);
|
||||||
void RenderModebar(CUIRect View);
|
void RenderModebar(CUIRect View);
|
||||||
void RenderStatusbar(CUIRect View);
|
void RenderStatusbar(CUIRect View);
|
||||||
void RenderEnvelopeEditor(CUIRect View);
|
void RenderEnvelopeEditor(CUIRect View);
|
||||||
|
@ -1026,5 +1088,27 @@ public:
|
||||||
virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect);
|
virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CLayerSounds : public CLayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLayerSounds();
|
||||||
|
~CLayerSounds();
|
||||||
|
|
||||||
|
virtual void Render();
|
||||||
|
CSoundSource *NewSource();
|
||||||
|
|
||||||
|
virtual void BrushSelecting(CUIRect Rect);
|
||||||
|
virtual int BrushGrab(CLayerGroup *pBrush, CUIRect Rect);
|
||||||
|
virtual void BrushPlace(CLayer *pBrush, float wx, float wy);
|
||||||
|
|
||||||
|
virtual int RenderProperties(CUIRect *pToolbox);
|
||||||
|
|
||||||
|
virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc);
|
||||||
|
virtual void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc);
|
||||||
|
|
||||||
|
int m_Sound;
|
||||||
|
array<CSoundSource> m_lSources;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -271,6 +271,30 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
|
||||||
df.AddItem(MAPITEMTYPE_IMAGE, i, sizeof(Item), &Item);
|
df.AddItem(MAPITEMTYPE_IMAGE, i, sizeof(Item), &Item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save sounds
|
||||||
|
for(int i = 0; i < m_lSounds.size(); i++)
|
||||||
|
{
|
||||||
|
CEditorSound *pSound = m_lSounds[i];
|
||||||
|
|
||||||
|
CMapItemSound Item;
|
||||||
|
Item.m_Version = 1;
|
||||||
|
|
||||||
|
Item.m_External = pSound->m_External;
|
||||||
|
Item.m_SoundName = df.AddData(str_length(pSound->m_aName)+1, pSound->m_aName);
|
||||||
|
if(pSound->m_External)
|
||||||
|
{
|
||||||
|
Item.m_SoundDataSize = 0;
|
||||||
|
Item.m_SoundData = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Item.m_SoundData = df.AddData(pSound->m_DataSize, pSound->m_pData);
|
||||||
|
Item.m_SoundDataSize = pSound->m_DataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
df.AddItem(MAPITEMTYPE_SOUND, i, sizeof(Item), &Item);
|
||||||
|
}
|
||||||
|
|
||||||
// save layers
|
// save layers
|
||||||
int LayerCount = 0, GroupCount = 0;
|
int LayerCount = 0, GroupCount = 0;
|
||||||
for(int g = 0; g < m_lGroups.size(); g++)
|
for(int g = 0; g < m_lGroups.size(); g++)
|
||||||
|
@ -415,6 +439,30 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
|
||||||
LayerCount++;
|
LayerCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(pGroup->m_lLayers[l]->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "editor", "saving sounds layer");
|
||||||
|
CLayerSounds *pLayer = (CLayerSounds *)pGroup->m_lLayers[l];
|
||||||
|
if(pLayer->m_lSources.size())
|
||||||
|
{
|
||||||
|
CMapItemLayerSounds Item;
|
||||||
|
Item.m_Version = CMapItemLayerSounds::CURRENT_VERSION;
|
||||||
|
Item.m_Layer.m_Flags = pLayer->m_Flags;
|
||||||
|
Item.m_Layer.m_Type = pLayer->m_Type;
|
||||||
|
Item.m_Sound = pLayer->m_Sound;
|
||||||
|
|
||||||
|
// add the data
|
||||||
|
Item.m_NumSources = pLayer->m_lSources.size();
|
||||||
|
Item.m_Data = df.AddDataSwapped(pLayer->m_lSources.size()*sizeof(CSoundSource), pLayer->m_lSources.base_ptr());
|
||||||
|
|
||||||
|
// save layer name
|
||||||
|
StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName);
|
||||||
|
|
||||||
|
df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
|
||||||
|
GItem.m_NumLayers++;
|
||||||
|
LayerCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
df.AddItem(MAPITEMTYPE_GROUP, GroupCount++, sizeof(GItem), &GItem);
|
df.AddItem(MAPITEMTYPE_GROUP, GroupCount++, sizeof(GItem), &GItem);
|
||||||
|
@ -579,6 +627,59 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load sounds
|
||||||
|
{
|
||||||
|
int Start, Num;
|
||||||
|
DataFile.GetType( MAPITEMTYPE_SOUND, &Start, &Num);
|
||||||
|
for(int i = 0; i < Num; i++)
|
||||||
|
{
|
||||||
|
CMapItemSound *pItem = (CMapItemSound *)DataFile.GetItem(Start+i, 0, 0);
|
||||||
|
char *pName = (char *)DataFile.GetData(pItem->m_SoundName);
|
||||||
|
|
||||||
|
// copy base info
|
||||||
|
CEditorSound *pSound = new CEditorSound(m_pEditor);
|
||||||
|
pSound->m_External = pItem->m_External;
|
||||||
|
|
||||||
|
if(pItem->m_External)
|
||||||
|
{
|
||||||
|
char aBuf[256];
|
||||||
|
str_format(aBuf, sizeof(aBuf),"mapres/%s.wv", pName);
|
||||||
|
|
||||||
|
// load external
|
||||||
|
IOHANDLE SoundFile = pStorage->OpenFile(pName, IOFLAG_READ, IStorage::TYPE_ALL);
|
||||||
|
if(SoundFile)
|
||||||
|
{
|
||||||
|
// read the whole file into memory
|
||||||
|
pSound->m_DataSize = io_length(SoundFile);
|
||||||
|
pSound->m_pData = new char[pSound->m_DataSize];
|
||||||
|
io_read(SoundFile, pSound->m_pData, pSound->m_DataSize);
|
||||||
|
io_close(SoundFile);
|
||||||
|
pSound->m_SoundID = m_pEditor->Sound()->LoadWVFromMem(pSound->m_pData, pSound->m_DataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSound->m_DataSize = pItem->m_SoundDataSize;
|
||||||
|
|
||||||
|
// copy sample data
|
||||||
|
void *pData = DataFile.GetData(pItem->m_SoundData);
|
||||||
|
pSound->m_pData = mem_alloc(pSound->m_DataSize, 1);
|
||||||
|
mem_copy(pSound->m_pData, pData, pSound->m_DataSize);
|
||||||
|
pSound->m_SoundID = m_pEditor->Sound()->LoadWVFromMem(pSound->m_pData, pSound->m_DataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy image name
|
||||||
|
if(pName)
|
||||||
|
str_copy(pSound->m_aName, pName, sizeof(pSound->m_aName));
|
||||||
|
|
||||||
|
m_lSounds.add(pSound);
|
||||||
|
|
||||||
|
// unload image
|
||||||
|
DataFile.UnloadData(pItem->m_SoundData);
|
||||||
|
DataFile.UnloadData(pItem->m_SoundName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load groups
|
// load groups
|
||||||
{
|
{
|
||||||
int LayersStart, LayersNum;
|
int LayersStart, LayersNum;
|
||||||
|
@ -885,6 +986,29 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
|
||||||
mem_copy(pQuads->m_lQuads.base_ptr(), pData, sizeof(CQuad)*pQuadsItem->m_NumQuads);
|
mem_copy(pQuads->m_lQuads.base_ptr(), pData, sizeof(CQuad)*pQuadsItem->m_NumQuads);
|
||||||
DataFile.UnloadData(pQuadsItem->m_Data);
|
DataFile.UnloadData(pQuadsItem->m_Data);
|
||||||
}
|
}
|
||||||
|
else if(pLayerItem->m_Type == LAYERTYPE_SOUNDS)
|
||||||
|
{
|
||||||
|
CMapItemLayerSounds *pSoundsItem = (CMapItemLayerSounds *)pLayerItem;
|
||||||
|
CLayerSounds *pSounds = new CLayerSounds;
|
||||||
|
pSounds->m_pEditor = m_pEditor;
|
||||||
|
pLayer = pSounds;
|
||||||
|
pSounds->m_Sound = pSoundsItem->m_Sound;
|
||||||
|
|
||||||
|
// validate m_Sound
|
||||||
|
if(pSounds->m_Sound < -1 || pSounds->m_Sound >= m_lSounds.size())
|
||||||
|
pSounds->m_Sound = -1;
|
||||||
|
|
||||||
|
// load layer name
|
||||||
|
if(pSoundsItem->m_Version >= 1)
|
||||||
|
IntsToStr(pSoundsItem->m_aName, sizeof(pSounds->m_aName)/sizeof(int), pSounds->m_aName);
|
||||||
|
|
||||||
|
// load data
|
||||||
|
void *pData = DataFile.GetDataSwapped(pSoundsItem->m_Data);
|
||||||
|
pGroup->AddLayer(pSounds);
|
||||||
|
pSounds->m_lSources.set_size(pSoundsItem->m_NumSources);
|
||||||
|
mem_copy(pSounds->m_lSources.base_ptr(), pData, sizeof(CSoundSource)*pSoundsItem->m_NumSources);
|
||||||
|
DataFile.UnloadData(pSoundsItem->m_Data);
|
||||||
|
}
|
||||||
|
|
||||||
if(pLayer)
|
if(pLayer)
|
||||||
pLayer->m_Flags = pLayerItem->m_Flags;
|
pLayer->m_Flags = pLayerItem->m_Flags;
|
||||||
|
|
188
src/game/editor/layer_sounds.cpp
Normal file
188
src/game/editor/layer_sounds.cpp
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
|
||||||
|
#include "editor.h"
|
||||||
|
|
||||||
|
static const float s_SourceVisualSize = 50.0f;
|
||||||
|
|
||||||
|
CLayerSounds::CLayerSounds()
|
||||||
|
{
|
||||||
|
m_Type = LAYERTYPE_SOUNDS;
|
||||||
|
str_copy(m_aName, "Sounds", sizeof(m_aName));
|
||||||
|
m_Sound = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLayerSounds::~CLayerSounds()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLayerSounds::Render()
|
||||||
|
{
|
||||||
|
// TODO: nice texture
|
||||||
|
Graphics()->TextureSet(-1);
|
||||||
|
Graphics()->BlendNormal();
|
||||||
|
Graphics()->QuadsBegin();
|
||||||
|
|
||||||
|
// draw falloff distance
|
||||||
|
Graphics()->SetColor(0.6f, 0.8f, 1.0f, 0.4f);
|
||||||
|
for(int i = 0; i < m_lSources.size(); i++)
|
||||||
|
{
|
||||||
|
CSoundSource *pSource = &m_lSources[i];
|
||||||
|
|
||||||
|
float OffsetX = 0;
|
||||||
|
float OffsetY = 0;
|
||||||
|
|
||||||
|
if(pSource->m_PosEnv >= 0)
|
||||||
|
{
|
||||||
|
float aChannels[4];
|
||||||
|
m_pEditor->EnvelopeEval(pSource->m_PosEnvOffset/1000.0f, pSource->m_PosEnv, aChannels, m_pEditor);
|
||||||
|
OffsetX = aChannels[0];
|
||||||
|
OffsetY = aChannels[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pEditor->RenderTools()->DrawCircle(fx2f(pSource->m_Position.x)+OffsetX, fx2f(pSource->m_Position.y)+OffsetY, pSource->m_FalloffDistance, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// draw handles
|
||||||
|
Graphics()->SetColor(1.0f, 0.0f, 1.0f, 1.0f);
|
||||||
|
for(int i = 0; i < m_lSources.size(); i++)
|
||||||
|
{
|
||||||
|
CSoundSource *pSource = &m_lSources[i];
|
||||||
|
|
||||||
|
float OffsetX = 0;
|
||||||
|
float OffsetY = 0;
|
||||||
|
|
||||||
|
if(pSource->m_PosEnv >= 0)
|
||||||
|
{
|
||||||
|
float aChannels[4];
|
||||||
|
m_pEditor->EnvelopeEval(pSource->m_PosEnvOffset/1000.0f, pSource->m_PosEnv, aChannels, m_pEditor);
|
||||||
|
OffsetX = aChannels[0];
|
||||||
|
OffsetY = aChannels[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
IGraphics::CQuadItem QuadItem(fx2f(pSource->m_Position.x)+OffsetX, fx2f(pSource->m_Position.y)+OffsetY, s_SourceVisualSize, s_SourceVisualSize);
|
||||||
|
Graphics()->QuadsDraw(&QuadItem, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics()->QuadsEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSoundSource *CLayerSounds::NewSource()
|
||||||
|
{
|
||||||
|
m_pEditor->m_Map.m_Modified = true;
|
||||||
|
|
||||||
|
CSoundSource *pSource = &m_lSources[m_lSources.add(CSoundSource())];
|
||||||
|
|
||||||
|
pSource->m_Position.x = 0;
|
||||||
|
pSource->m_Position.y = 0;
|
||||||
|
|
||||||
|
pSource->m_Loop = 1;
|
||||||
|
pSource->m_TimeDelay = 0;
|
||||||
|
pSource->m_FalloffDistance = 1500;
|
||||||
|
|
||||||
|
pSource->m_PosEnv = -1;
|
||||||
|
pSource->m_PosEnvOffset = 0;
|
||||||
|
pSource->m_SoundEnv = -1;
|
||||||
|
pSource->m_SoundEnvOffset = 0;
|
||||||
|
|
||||||
|
return pSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLayerSounds::BrushSelecting(CUIRect Rect)
|
||||||
|
{
|
||||||
|
// draw selection rectangle
|
||||||
|
IGraphics::CLineItem Array[4] = {
|
||||||
|
IGraphics::CLineItem(Rect.x, Rect.y, Rect.x+Rect.w, Rect.y),
|
||||||
|
IGraphics::CLineItem(Rect.x+Rect.w, Rect.y, Rect.x+Rect.w, Rect.y+Rect.h),
|
||||||
|
IGraphics::CLineItem(Rect.x+Rect.w, Rect.y+Rect.h, Rect.x, Rect.y+Rect.h),
|
||||||
|
IGraphics::CLineItem(Rect.x, Rect.y+Rect.h, Rect.x, Rect.y)};
|
||||||
|
Graphics()->TextureSet(-1);
|
||||||
|
Graphics()->LinesBegin();
|
||||||
|
Graphics()->LinesDraw(Array, 4);
|
||||||
|
Graphics()->LinesEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CLayerSounds::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
|
||||||
|
{
|
||||||
|
// create new layer
|
||||||
|
CLayerSounds *pGrabbed = new CLayerSounds();
|
||||||
|
pGrabbed->m_pEditor = m_pEditor;
|
||||||
|
pGrabbed->m_Sound = m_Sound;
|
||||||
|
pBrush->AddLayer(pGrabbed);
|
||||||
|
|
||||||
|
for(int i = 0; i < m_lSources.size(); i++)
|
||||||
|
{
|
||||||
|
CSoundSource *pSource = &m_lSources[i];
|
||||||
|
float px = fx2f(pSource->m_Position.x);
|
||||||
|
float py = fx2f(pSource->m_Position.y);
|
||||||
|
|
||||||
|
if(px > Rect.x && px < Rect.x+Rect.w && py > Rect.y && py < Rect.y+Rect.h)
|
||||||
|
{
|
||||||
|
CSoundSource n;
|
||||||
|
n = *pSource;
|
||||||
|
|
||||||
|
n.m_Position.x -= f2fx(Rect.x);
|
||||||
|
n.m_Position.y -= f2fx(Rect.y);
|
||||||
|
|
||||||
|
pGrabbed->m_lSources.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pGrabbed->m_lSources.size()?1:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLayerSounds::BrushPlace(CLayer *pBrush, float wx, float wy)
|
||||||
|
{
|
||||||
|
CLayerSounds *l = (CLayerSounds *)pBrush;
|
||||||
|
for(int i = 0; i < l->m_lSources.size(); i++)
|
||||||
|
{
|
||||||
|
CSoundSource n = l->m_lSources[i];
|
||||||
|
|
||||||
|
n.m_Position.x += f2fx(wx);
|
||||||
|
n.m_Position.y += f2fx(wy);
|
||||||
|
|
||||||
|
m_lSources.add(n);
|
||||||
|
}
|
||||||
|
m_pEditor->m_Map.m_Modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CLayerSounds::RenderProperties(CUIRect *pToolBox)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_SOUND=0,
|
||||||
|
NUM_PROPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
CProperty aProps[] = {
|
||||||
|
{"Sound", m_Sound, PROPTYPE_SOUND, -1, 0},
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int s_aIds[NUM_PROPS] = {0};
|
||||||
|
int NewVal = 0;
|
||||||
|
int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal);
|
||||||
|
if(Prop != -1)
|
||||||
|
m_pEditor->m_Map.m_Modified = true;
|
||||||
|
|
||||||
|
if(Prop == PROP_SOUND)
|
||||||
|
{
|
||||||
|
if(NewVal >= 0)
|
||||||
|
m_Sound = NewVal%m_pEditor->m_Map.m_lSounds.size();
|
||||||
|
else
|
||||||
|
m_Sound = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLayerSounds::ModifySoundIndex(INDEX_MODIFY_FUNC Func)
|
||||||
|
{
|
||||||
|
Func(&m_Sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLayerSounds::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < m_lSources.size(); i++)
|
||||||
|
Func(&m_lSources[i].m_SoundEnv);
|
||||||
|
}
|
|
@ -226,8 +226,8 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// new tile layer
|
// new quad layer
|
||||||
View.HSplitBottom(10.0f, &View, &Button);
|
View.HSplitBottom(7.0f, &View, &Button);
|
||||||
View.HSplitBottom(12.0f, &View, &Button);
|
View.HSplitBottom(12.0f, &View, &Button);
|
||||||
static int s_NewQuadLayerButton = 0;
|
static int s_NewQuadLayerButton = 0;
|
||||||
if(pEditor->DoButton_Editor(&s_NewQuadLayerButton, "Add quads layer", 0, &Button, 0, "Creates a new quad layer"))
|
if(pEditor->DoButton_Editor(&s_NewQuadLayerButton, "Add quads layer", 0, &Button, 0, "Creates a new quad layer"))
|
||||||
|
@ -240,7 +240,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new quad layer
|
// new tile layer
|
||||||
View.HSplitBottom(5.0f, &View, &Button);
|
View.HSplitBottom(5.0f, &View, &Button);
|
||||||
View.HSplitBottom(12.0f, &View, &Button);
|
View.HSplitBottom(12.0f, &View, &Button);
|
||||||
static int s_NewTileLayerButton = 0;
|
static int s_NewTileLayerButton = 0;
|
||||||
|
@ -254,6 +254,20 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new sound layer
|
||||||
|
View.HSplitBottom(5.0f, &View, &Button);
|
||||||
|
View.HSplitBottom(12.0f, &View, &Button);
|
||||||
|
static int s_NewSoundLayerButton = 0;
|
||||||
|
if(pEditor->DoButton_Editor(&s_NewSoundLayerButton, "Add sound layer", 0, &Button, 0, "Creates a new sound layer"))
|
||||||
|
{
|
||||||
|
CLayer *l = new CLayerSounds;
|
||||||
|
l->m_pEditor = pEditor;
|
||||||
|
pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
|
||||||
|
pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
|
||||||
|
pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// group name
|
// group name
|
||||||
if(!pEditor->GetSelectedGroup()->m_GameGroup)
|
if(!pEditor->GetSelectedGroup()->m_GameGroup)
|
||||||
{
|
{
|
||||||
|
@ -589,6 +603,100 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CEditor::PopupSource(CEditor *pEditor, CUIRect View)
|
||||||
|
{
|
||||||
|
CSoundSource *pSource = pEditor->GetSelectedSource();
|
||||||
|
|
||||||
|
CUIRect Button;
|
||||||
|
|
||||||
|
// delete button
|
||||||
|
View.HSplitBottom(12.0f, &View, &Button);
|
||||||
|
static int s_DeleteButton = 0;
|
||||||
|
if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete", 0, &Button, 0, "Deletes the current source"))
|
||||||
|
{
|
||||||
|
CLayerSounds *pLayer = (CLayerSounds *)pEditor->GetSelectedLayerType(0, LAYERTYPE_SOUNDS);
|
||||||
|
if(pLayer)
|
||||||
|
{
|
||||||
|
pEditor->m_Map.m_Modified = true;
|
||||||
|
pLayer->m_lSources.remove_index(pEditor->m_SelectedSource);
|
||||||
|
pEditor->m_SelectedSource--;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_POS_X=0,
|
||||||
|
PROP_POS_Y,
|
||||||
|
PROP_LOOP,
|
||||||
|
PROP_TIME_DELAY,
|
||||||
|
PROP_DISTANCE,
|
||||||
|
PROP_POS_ENV,
|
||||||
|
PROP_POS_ENV_OFFSET,
|
||||||
|
PROP_SOUND_ENV,
|
||||||
|
PROP_SOUND_ENV_OFFSET,
|
||||||
|
NUM_PROPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
CProperty aProps[] = {
|
||||||
|
{"Pos X", pSource->m_Position.x/1000, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||||
|
{"Pos Y", pSource->m_Position.y/1000, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||||
|
{"Loop", pSource->m_Loop, PROPTYPE_BOOL, 0, 1},
|
||||||
|
{"Delay", pSource->m_TimeDelay, PROPTYPE_INT_SCROLL, 0, 1000000},
|
||||||
|
{"Distance", pSource->m_FalloffDistance, PROPTYPE_INT_SCROLL, 0, 1000000},
|
||||||
|
{"Pos. Env", pSource->m_PosEnv+1, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lEnvelopes.size()+1},
|
||||||
|
{"Pos. TO", pSource->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||||
|
{"Sound Env", pSource->m_SoundEnv+1, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lEnvelopes.size()+1},
|
||||||
|
{"Sound. TO", pSource->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||||
|
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int s_aIds[NUM_PROPS] = {0};
|
||||||
|
int NewVal = 0;
|
||||||
|
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
|
||||||
|
if(Prop != -1)
|
||||||
|
pEditor->m_Map.m_Modified = true;
|
||||||
|
|
||||||
|
if(Prop == PROP_POS_X) pSource->m_Position.x = NewVal*1000;
|
||||||
|
if(Prop == PROP_POS_Y) pSource->m_Position.y = NewVal*1000;
|
||||||
|
if(Prop == PROP_LOOP) pSource->m_Loop = NewVal;
|
||||||
|
if(Prop == PROP_TIME_DELAY) pSource->m_TimeDelay = NewVal;
|
||||||
|
if(Prop == PROP_DISTANCE) pSource->m_FalloffDistance = NewVal;
|
||||||
|
if(Prop == PROP_POS_ENV)
|
||||||
|
{
|
||||||
|
int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
|
||||||
|
int Step = (Index-pSource->m_PosEnv)%2;
|
||||||
|
if(Step != 0)
|
||||||
|
{
|
||||||
|
for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step)
|
||||||
|
if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 3)
|
||||||
|
{
|
||||||
|
pSource->m_PosEnv = Index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Prop == PROP_POS_ENV_OFFSET) pSource->m_PosEnvOffset = NewVal;
|
||||||
|
if(Prop == PROP_SOUND_ENV)
|
||||||
|
{
|
||||||
|
int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
|
||||||
|
int Step = (Index-pSource->m_SoundEnv)%2;
|
||||||
|
if(Step != 0)
|
||||||
|
{
|
||||||
|
for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step)
|
||||||
|
if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 1)
|
||||||
|
{
|
||||||
|
pSource->m_SoundEnv = Index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Prop == PROP_SOUND_ENV_OFFSET) pSource->m_SoundEnvOffset = NewVal;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int CEditor::PopupPoint(CEditor *pEditor, CUIRect View)
|
int CEditor::PopupPoint(CEditor *pEditor, CUIRect View)
|
||||||
{
|
{
|
||||||
CQuad *pQuad = pEditor->GetSelectedQuad();
|
CQuad *pQuad = pEditor->GetSelectedQuad();
|
||||||
|
@ -978,6 +1086,101 @@ int CEditor::PopupSelectImageResult()
|
||||||
return g_SelectImageCurrent;
|
return g_SelectImageCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int g_SelectSoundSelected = -100;
|
||||||
|
static int g_SelectSoundCurrent = -100;
|
||||||
|
|
||||||
|
int CEditor::PopupSelectSound(CEditor *pEditor, CUIRect View)
|
||||||
|
{
|
||||||
|
CUIRect ButtonBar, SoundView;
|
||||||
|
View.VSplitLeft(80.0f, &ButtonBar, &View);
|
||||||
|
View.Margin(10.0f, &SoundView);
|
||||||
|
|
||||||
|
int ShowSound = g_SelectSoundCurrent;
|
||||||
|
|
||||||
|
static int s_ScrollBar = 0;
|
||||||
|
static float s_ScrollValue = 0;
|
||||||
|
float SoundsHeight = pEditor->m_Map.m_lSounds.size() * 14;
|
||||||
|
float ScrollDifference = SoundsHeight - ButtonBar.h;
|
||||||
|
|
||||||
|
if(pEditor->m_Map.m_lSounds.size() > 20) // Do we need a scrollbar?
|
||||||
|
{
|
||||||
|
CUIRect Scroll;
|
||||||
|
ButtonBar.VSplitRight(15.0f, &ButtonBar, &Scroll);
|
||||||
|
ButtonBar.VSplitRight(3.0f, &ButtonBar, 0); // extra spacing
|
||||||
|
Scroll.HMargin(5.0f, &Scroll);
|
||||||
|
s_ScrollValue = pEditor->UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
|
||||||
|
|
||||||
|
if(pEditor->UI()->MouseInside(&Scroll) || pEditor->UI()->MouseInside(&ButtonBar))
|
||||||
|
{
|
||||||
|
int ScrollNum = (int)((SoundsHeight-ButtonBar.h)/14.0f)+1;
|
||||||
|
if(ScrollNum > 0)
|
||||||
|
{
|
||||||
|
if(pEditor->Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
|
||||||
|
s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
|
||||||
|
if(pEditor->Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
|
||||||
|
s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ScrollNum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float SoundStartAt = ScrollDifference * s_ScrollValue;
|
||||||
|
if(SoundStartAt < 0.0f)
|
||||||
|
SoundStartAt = 0.0f;
|
||||||
|
|
||||||
|
float SoundStopAt = SoundsHeight - ScrollDifference * (1 - s_ScrollValue);
|
||||||
|
float SoundCur = 0.0f;
|
||||||
|
for(int i = -1; i < pEditor->m_Map.m_lSounds.size(); i++)
|
||||||
|
{
|
||||||
|
if(SoundCur > SoundStopAt)
|
||||||
|
break;
|
||||||
|
if(SoundCur < SoundStartAt)
|
||||||
|
{
|
||||||
|
SoundCur += 14.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SoundCur += 14.0f;
|
||||||
|
|
||||||
|
CUIRect Button;
|
||||||
|
ButtonBar.HSplitTop(14.0f, &Button, &ButtonBar);
|
||||||
|
|
||||||
|
if(pEditor->UI()->MouseInside(&Button))
|
||||||
|
ShowSound = i;
|
||||||
|
|
||||||
|
if(i == -1)
|
||||||
|
{
|
||||||
|
if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lSounds[i], "None", i==g_SelectSoundCurrent, &Button))
|
||||||
|
g_SelectSoundSelected = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lSounds[i], pEditor->m_Map.m_lSounds[i]->m_aName, i==g_SelectSoundCurrent, &Button))
|
||||||
|
g_SelectSoundSelected = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEditor::PopupSelectSoundInvoke(int Current, float x, float y)
|
||||||
|
{
|
||||||
|
static int s_SelectSoundPopupId = 0;
|
||||||
|
g_SelectSoundSelected = -100;
|
||||||
|
g_SelectSoundCurrent = Current;
|
||||||
|
UiInvokePopupMenu(&s_SelectSoundPopupId, 0, x, y, 400, 300, PopupSelectSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CEditor::PopupSelectSoundResult()
|
||||||
|
{
|
||||||
|
if(g_SelectSoundSelected == -100)
|
||||||
|
return -100;
|
||||||
|
|
||||||
|
g_SelectSoundCurrent = g_SelectSoundSelected;
|
||||||
|
g_SelectSoundSelected = -100;
|
||||||
|
return g_SelectSoundCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
static int s_GametileOpSelected = -1;
|
static int s_GametileOpSelected = -1;
|
||||||
|
|
||||||
int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View)
|
int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View)
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum
|
||||||
LAYERTYPE_SPEEDUP,
|
LAYERTYPE_SPEEDUP,
|
||||||
LAYERTYPE_SWITCH,
|
LAYERTYPE_SWITCH,
|
||||||
LAYERTYPE_TUNE,
|
LAYERTYPE_TUNE,
|
||||||
|
LAYERTYPE_SOUNDS,
|
||||||
|
|
||||||
MAPITEMTYPE_VERSION=0,
|
MAPITEMTYPE_VERSION=0,
|
||||||
MAPITEMTYPE_INFO,
|
MAPITEMTYPE_INFO,
|
||||||
|
@ -26,6 +27,7 @@ enum
|
||||||
MAPITEMTYPE_GROUP,
|
MAPITEMTYPE_GROUP,
|
||||||
MAPITEMTYPE_LAYER,
|
MAPITEMTYPE_LAYER,
|
||||||
MAPITEMTYPE_ENVPOINTS,
|
MAPITEMTYPE_ENVPOINTS,
|
||||||
|
MAPITEMTYPE_SOUND,
|
||||||
|
|
||||||
|
|
||||||
CURVETYPE_STEP=0,
|
CURVETYPE_STEP=0,
|
||||||
|
@ -327,6 +329,45 @@ struct CMapItemEnvelope : public CMapItemEnvelope_v1
|
||||||
int m_Synchronized;
|
int m_Synchronized;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CSoundSource
|
||||||
|
{
|
||||||
|
CPoint m_Position;
|
||||||
|
int m_Loop;
|
||||||
|
int m_TimeDelay; // in s
|
||||||
|
int m_FalloffDistance;
|
||||||
|
|
||||||
|
int m_PosEnv;
|
||||||
|
int m_PosEnvOffset;
|
||||||
|
int m_SoundEnv;
|
||||||
|
int m_SoundEnvOffset;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CMapItemLayerSounds
|
||||||
|
{
|
||||||
|
enum { CURRENT_VERSION=1 };
|
||||||
|
|
||||||
|
CMapItemLayer m_Layer;
|
||||||
|
int m_Version;
|
||||||
|
|
||||||
|
int m_NumSources;
|
||||||
|
int m_Data;
|
||||||
|
int m_Sound;
|
||||||
|
|
||||||
|
int m_aName[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CMapItemSound
|
||||||
|
{
|
||||||
|
int m_Version;
|
||||||
|
|
||||||
|
int m_External;
|
||||||
|
|
||||||
|
int m_SoundName;
|
||||||
|
int m_SoundData;
|
||||||
|
int m_SoundDataSize;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
|
||||||
// DDRace
|
// DDRace
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue