Programming/vgui soundscape maker.cpp: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Made it so the 'Sound list' panel now works for soundscapes, allowing you to look for the specific soundscape to insert.)
(Added clipboard for copying and pasting)
 
(6 intermediate revisions by the same user not shown)
Line 11: Line 11:
#include "c_soundscape.h"
#include "c_soundscape.h"
#include "vgui_soundscape_maker.h"
#include "vgui_soundscape_maker.h"
#include "vgui/GraphPanel.h"
#include <vgui_controls/Frame.h>
#include <vgui_controls/Frame.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Button.h>
Line 26: Line 25:
#include <vgui_controls/MenuItem.h>
#include <vgui_controls/MenuItem.h>
#include <vgui_controls/RichText.h>
#include <vgui_controls/RichText.h>
#include <vgui/ISystem.h>
#include <engine/IEngineSound.h>
#include <engine/IEngineSound.h>
#include <vgui/IVGui.h>
#include <vgui/IVGui.h>
#include <vgui/IInput.h>
#include <vgui/IInput.h>
#include <vgui/ISurface.h>
#include <vgui/ISurface.h>
#include <ienginevgui.h>
#include <filesystem.h>
#include <filesystem.h>
#include <usermessages.h>
#include <usermessages.h>
#include <fmtstr.h>
#include <fmtstr.h>


//selected text mode
//graph panel for debugging
enum class SoundscapeMode
 
class CGraphPanel : public vgui::Panel
{
{
Mode_Random,
public:
Mode_Soundscape,
DECLARE_CLASS_SIMPLE(CGraphPanel, vgui::Panel);
Mode_Looping,
 
CGraphPanel(Panel* parent, const char* panelName);
 
//think and paint
virtual void OnThink();
virtual void Paint();
 
//start and stop functions
virtual void Start();
virtual void Stop();
virtual void Restart();
virtual void Clear();
 
//Adding/doing stuff to lines functions
virtual void AddLine(bool ascending, unsigned char r, unsigned char g, unsigned char b, float speed, float flGraphWidthFraction = 1.0f);
virtual void RemoveLine(int index);
 
//set functions
virtual void SetDuration(float seconds) { m_flDuration = seconds; }
virtual void SetHorizontalLinesMax(float seconds) { m_nHorizontalLinesMax = seconds; }
virtual void SetMaxTextValue(float maxvalue) { m_flMaxValue = maxvalue; }
 
//other
virtual void ApplySchemeSettings(vgui::IScheme* scheme);
 
//sets the font
void SetFont(vgui::HFont font) { m_Font = font; }
inline vgui::HFont GetFont() { return m_Font; };
 
//gets the number of lines
int GetNumLines() { return m_Lines.Count(); }
 
private:
 
//line information
struct LineInfo
{
float startTime;
float offset;
float elapsedWhenStopped;
float m_flGraphWidthFraction;
bool ascending;
unsigned char r, g, b;
float speed;
};
 
//lines and other stuff
CUtlVector<LineInfo> m_Lines;
float m_flDuration;
float m_flTimeOffset;
bool m_bRunning;
 
//mad horizontal lines
int m_nHorizontalLinesMax = 2;
float m_flMaxValue = 1.0f;
 
vgui::HFont m_Font;
};
};


//dsp effects
extern vgui::ILocalize* g_pVGuiLocalize;
static const char* g_DspEffects[] = {
 
"Normal (off)",
//-----------------------------------------------------------------------------
"Generic",
// Purpose: Graph panel
"Metal Small",
//-----------------------------------------------------------------------------
"Metal Medium",
CGraphPanel::CGraphPanel(Panel* parent, const char* panelName)
"Metal Large",
: BaseClass(parent, panelName)
"Tunnel Small",
{
"Tunnel Medium",
SetPaintBackgroundEnabled(false);
"Tunnel Large",
m_flDuration = 2.0f;
"Chamber Small",
m_bRunning = false;
"Chamber Medium",
m_flTimeOffset = 0.0f;
"Chamber Large",
m_Font = vgui::INVALID_FONT;
"Bright Small",
SetBgColor(Color(0, 0, 0, 255));
"Bright Medium",
}
"Bright Large",
 
"Water 1",
//-----------------------------------------------------------------------------
"Water 2",
// Purpose: Called when this panel thinks
"Water 3",
//-----------------------------------------------------------------------------
"Concrete Small",
void CGraphPanel::OnThink()
"Concrete Medium",
{
"Concrete Large",
if (m_bRunning)
"Big 1",
Repaint();
"Big 2",
}
"Big 3",
 
"Cavern Small",
//-----------------------------------------------------------------------------
"Cavern Medium",
// Purpose: Called when this panel paints
"Cavern Large",
//-----------------------------------------------------------------------------
"Weirdo 1",
void CGraphPanel::Paint()
"Weirdo 2",
{
"Weirdo 3",
//get size
};
int w, h;
GetSize(w, h);
 
//set fill background
vgui::surface()->DrawSetColor(GetBgColor());
vgui::surface()->DrawFilledRect(0, 0, w, h);
 
//draw horizontal grid lines
if (m_nHorizontalLinesMax > 1)
{
vgui::surface()->DrawSetColor(100, 100, 100, 128); //grey lines
 
//draw lines
for (int i = 0; i < m_nHorizontalLinesMax; ++i)
{
float frac = (float)i / (m_nHorizontalLinesMax - 1);
int y = h - (int)(frac * h);
 
//draw the line
vgui::surface()->DrawLine(0, y, w, y);
 
float labelValue = frac * m_flMaxValue;
 
char buf[32];
Q_snprintf(buf, sizeof(buf), "%.2f", labelValue);


//sound levels
vgui::surface()->DrawSetTextFont(GetFont());
static const char* g_SoundLevels[] = {
vgui::surface()->DrawSetTextColor(255, 255, 255, 255);
"SNDLVL_50dB",
 
"SNDLVL_55dB",
//set text pos
"SNDLVL_IDLE",
if (labelValue == m_flMaxValue)
"SNDLVL_TALKING",
vgui::surface()->DrawSetTextPos(5, y);
"SNDLVL_60dB",
else if (labelValue <= 0.0f)
"SNDLVL_65dB",
vgui::surface()->DrawSetTextPos(5, y - 14);
"SNDLVL_STATIC",
else
"SNDLVL_70dB",
vgui::surface()->DrawSetTextPos(5, y - 8);
"SNDLVL_NORM",
"SNDLVL_75dB",
"SNDLVL_80dB",
"SNDLVL_85dB",
"SNDLVL_90dB",
"SNDLVL_95dB",
"SNDLVL_100dB",
"SNDLVL_105dB",
"SNDLVL_120dB",
"SNDLVL_130dB",
"SNDLVL_GUNFIRE",
"SNDLVL_140dB",
"SNDLVL_150dB"
};


bool g_bSSMHack = false;
wchar_t wbuf[32];
g_pVGuiLocalize->ConvertANSIToUnicode(buf, wbuf, sizeof(wbuf));
vgui::surface()->DrawPrintText(wbuf, wcslen(wbuf));
}
}


//-----------------------------------------------------------------------------
//get now time
// Purpose: Helper funciton to compare vector and string
float now = vgui::system()->GetFrameTime();
//-----------------------------------------------------------------------------
int Q_vecstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
{
//check for null substring
if (!substr || !*substr)
return -1;
//store variables
int substrLen = Q_strlen(substr);


//search for match
//now draw the graphs
for (int i = startindex; i <= endindex; ++i)
for (int i = 0; i < m_Lines.Count(); ++i)
{
{
bool match = true;
//get line info
for (int j = 0; j < substrLen; ++j)
const LineInfo& line = m_Lines[i];
{
wchar_t wc = vec[i + j];
char ch = substr[j];
if (wc != ch)
{
match = false;
break;
}
}


//found match
//set color
if (match)
vgui::surface()->DrawSetColor(line.r, line.g, line.b, 255);
return i;
}


//didnt find match
//do stuff
return -1;
float elapsed = m_bRunning ? (now - line.startTime - line.offset) : line.elapsedWhenStopped;
if (elapsed < 0.0f)
continue;
 
float effectiveDuration = m_flDuration / line.speed;
 
if (elapsed > effectiveDuration)
elapsed = effectiveDuration;
 
float progress = elapsed / effectiveDuration;
 
int lastX = -1;
int lastY = -1;
 
// Calculate the maximum possible width fraction for this line considering the offset
float maxWidthForLine = line.m_flGraphWidthFraction - line.offset;
if (maxWidthForLine < 0.0f)
maxWidthForLine = 0.0f; // prevent negative width
 
//make 128 line points
for (int j = 0; j < 128; ++j)
{
float t = (float)j / 127.0f;
 
// Stop drawing if t > progress for this line
if (t > progress)
break;
 
float curve;
if (line.ascending)
curve = t * t * t;
else
curve = 1.0f - t * t;
 
// Calculate the X position using offset + scaled max width for this line
float combinedPos = line.offset + t * maxWidthForLine;
 
// Stop if combinedPos is beyond max graph width fraction (to not draw outside graph area)
if (combinedPos > line.m_flGraphWidthFraction)
break;
 
int x = (int)(w * combinedPos);
int y = (int)(h - curve * h);
 
//draw the line
if (lastX >= 0 && lastY >= 0)
vgui::surface()->DrawLine(lastX, lastY, x, y);
 
lastX = x;
lastY = y;
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Helper funciton to compare vector and string but reversed
// Purpose: Starts drawing the graphs
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int Q_vecrstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
void CGraphPanel::Start()
{
{
//check for null substring
//check for not running
if (!substr || !*substr)
if (!m_bRunning)
return -1;
//store variables
int substrLen = Q_strlen(substr);
 
//search for match
for (int i = endindex - substrLen + 1; i >= startindex; --i)
{
{
bool match = true;
float now = vgui::system()->GetFrameTime();
for (int j = 0; j < substrLen; ++j)
for (int i = 0; i < m_Lines.Count(); ++i)
{
{
wchar_t wc = vec[i + j];
if (m_Lines[i].elapsedWhenStopped < m_flDuration / m_Lines[i].speed)
char ch = substr[j];
m_Lines[i].startTime = now - m_Lines[i].elapsedWhenStopped;
if (wc != ch)
{
match = false;
break;
}
}
}


//found match
//start running
if (match)
m_bRunning = true;
return i;
}
}
}


//didnt find match
//-----------------------------------------------------------------------------
return -1;
// Purpose: Stops drawing the graphs
//-----------------------------------------------------------------------------
void CGraphPanel::Stop()
{
//check for running
if (m_bRunning)
{
float now = vgui::system()->GetFrameTime();
for (int i = 0; i < m_Lines.Count(); ++i)
m_Lines[i].elapsedWhenStopped = now - m_Lines[i].startTime;
 
//stop running
m_bRunning = false;
}
}
}


//holds all the sound names
//-----------------------------------------------------------------------------
static CUtlVector<char*> g_SoundDirectories;
// Purpose: Clears the line graph
//-----------------------------------------------------------------------------
void CGraphPanel::Clear()
{
m_Lines.RemoveAll();
m_flTimeOffset = 0.0f;
m_bRunning = false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Sort function for utl vector
// Purpose: Resets the lines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static int VectorSortFunc(char* const* p1, char* const* p2)
void CGraphPanel::Restart()
{
{
return Q_stricmp(*p1, *p2);
for (int i = 0; i < m_Lines.Count(); i++)
{
m_Lines[i].startTime = vgui::system()->GetFrameTime();
m_Lines[i].elapsedWhenStopped = 0.0f;
}
 
m_flTimeOffset += 0.1f;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Gets all the sound names and stores them into g_SoundDirectories
// Purpose: Adds a line to the line graph
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void GetSoundNames()
void CGraphPanel::AddLine(bool ascending, unsigned char r, unsigned char g, unsigned char b, float speed, float flGraphWidthFraction)
{
{
//first off clear the sound array first
//check speed
for (int i = 0; i < g_SoundDirectories.Count(); i++)
if (speed <= 0.0f)
free(g_SoundDirectories[i]);
speed = 1.0f;


g_SoundDirectories.RemoveAll();
//create line info
LineInfo line;
line.startTime = vgui::system()->GetFrameTime();
line.ascending = ascending;
line.m_flGraphWidthFraction = flGraphWidthFraction;
line.offset = m_flTimeOffset;
line.elapsedWhenStopped = 0.0f;
line.r = r;
line.g = g;
line.b = b;
line.speed = speed;


//directories to search
//add to lines array
CUtlVector<char*> directoriesToSearch;
m_Lines.AddToTail(line);
directoriesToSearch.AddToTail(strdup("sound"));
m_flTimeOffset += 0.1f;
}


//loop until all directories have been processed
//-----------------------------------------------------------------------------
while (directoriesToSearch.Count() > 0)
// Purpose: Removes a line graph
{
//-----------------------------------------------------------------------------
//take the last added directory (depth-first search)
void CGraphPanel::RemoveLine(int index)
char* currentDir = directoriesToSearch[directoriesToSearch.Count() - 1];
{
directoriesToSearch.Remove(directoriesToSearch.Count() - 1);
//bounds check
if (index >= m_Lines.Count() || index < 0)
return;


//create a wildcard path to search all files and subdirs
//remove the line
char searchPath[MAX_PATH];
m_Lines.Remove(index);
Q_snprintf(searchPath, sizeof(searchPath), "%s/*", currentDir);


FileFindHandle_t findHandle;
//move everything down
const char* filename = g_pFullFileSystem->FindFirst(searchPath, &findHandle);
m_flTimeOffset = 0.0f;
for (int i = 0; i < m_Lines.Count(); ++i)
{
m_Lines[i].offset = m_flTimeOffset;
m_flTimeOffset += 0.1f;
}
}


while (filename)
//-----------------------------------------------------------------------------
{
// Purpose: Called when scheme settings are set
//ignore special directories
//-----------------------------------------------------------------------------
if (Q_strcmp(filename, ".") != 0 && Q_strcmp(filename, "..") != 0)
void CGraphPanel::ApplySchemeSettings(vgui::IScheme* scheme)
{
{
char fullPath[MAX_PATH];
SetFont(scheme->GetFont("Default", IsProportional()));
Q_snprintf(fullPath, sizeof(fullPath), "%s/%s", currentDir, filename);
}


//if it's a directory, add it to the list for later processing
//selected text mode
if (g_pFullFileSystem->FindIsDirectory(findHandle))
enum class SoundscapeMode
{
{
directoriesToSearch.AddToTail(strdup(fullPath));
Mode_Random,
}
Mode_Soundscape,
else
Mode_Looping,
{
};
//check file extension and print if it's .wav or .mp3
const char* ext = V_GetFileExtension(filename);
if (ext && (!Q_stricmp(ext, "wav") || !Q_stricmp(ext, "mp3")))
{
g_SoundDirectories.AddToTail(strdup(fullPath + 6));
}
}
}


// Move to next file
//soundscape clipboard type
filename = g_pFullFileSystem->FindNext(findHandle);
enum class SoundscapeClipboardType
}
{
Type_SoundscapeNone,
Type_SoundscapeName,
Type_SoundscapeData,
Type_SoundscapeRandomWave,
};


//free the memory
//dsp effects
g_pFullFileSystem->FindClose(findHandle);
static const char* g_DspEffects[] = {
free(currentDir);
"Normal (off)",
}
"Generic",
 
"Metal Small",
//
"Metal Medium",
g_SoundDirectories.Sort(VectorSortFunc);
"Metal Large",
}
"Tunnel Small",
 
"Tunnel Medium",
 
"Tunnel Large",
//text entry for text edit panel
"Chamber Small",
"Chamber Medium",
"Chamber Large",
"Bright Small",
"Bright Medium",
"Bright Large",
"Water 1",
"Water 2",
"Water 3",
"Concrete Small",
"Concrete Medium",
"Concrete Large",
"Big 1",
"Big 2",
"Big 3",
"Cavern Small",
"Cavern Medium",
"Cavern Large",
"Weirdo 1",
"Weirdo 2",
"Weirdo 3",
};


//sound levels
static const char* g_SoundLevels[] = {
"SNDLVL_50dB",
"SNDLVL_55dB",
"SNDLVL_IDLE",
"SNDLVL_TALKING",
"SNDLVL_60dB",
"SNDLVL_65dB",
"SNDLVL_STATIC",
"SNDLVL_70dB",
"SNDLVL_NORM",
"SNDLVL_75dB",
"SNDLVL_80dB",
"SNDLVL_85dB",
"SNDLVL_90dB",
"SNDLVL_95dB",
"SNDLVL_100dB",
"SNDLVL_105dB",
"SNDLVL_120dB",
"SNDLVL_130dB",
"SNDLVL_GUNFIRE",
"SNDLVL_140dB",
"SNDLVL_150dB"
};


class CTextPanelTextEntry : public vgui::TextEntry
bool g_bSSMHack = false;
 
//max clipboard size
#define MAX_CLIPBOARD_ITEMS 10
 
//current clipboard stuff
static CUtlVector<KeyValues*> CurrClipboardName; //for soundscape name
static CUtlVector<KeyValues*> CurrClipboardData; //for soundscape data
static CUtlVector<KeyValues*> CurrClipboardRandom; //for random wave
 
//-----------------------------------------------------------------------------
// Purpose: Helper funciton to compare vector and string
//-----------------------------------------------------------------------------
int Q_vecstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
{
{
public:
//check for null substring
DECLARE_CLASS_SIMPLE(CTextPanelTextEntry, vgui::TextEntry)
if (!substr || !*substr)
return -1;


//constructor
//store variables
CTextPanelTextEntry(vgui::Panel* parent, const char* panelName)
int substrLen = Q_strlen(substr);
: TextEntry(parent, panelName)
{
SetMultiline(true);
}


//called on keycode typed
//search for match
virtual void OnKeyCodeTyped(vgui::KeyCode code)
for (int i = startindex; i <= endindex; ++i)
{
{
//check for enter or enter
bool match = true;
if (code == KEY_ENTER || code == KEY_PAD_ENTER)
for (int j = 0; j < substrLen; ++j)
InsertString("\n");
{
wchar_t wc = vec[i + j];
char ch = substr[j];
if (wc != ch)
{
match = false;
break;
}
}


//check for tab
//found match
else if (code == KEY_TAB)
if (match)
InsertString("    ");
return i;
//do other key code
else
BaseClass::OnKeyCodeTyped(code);
}
}


//called on keycode pressed
//didnt find match
void OnKeyCodePressed(vgui::KeyCode code)
return -1;
{
}
if (code == KEY_ENTER || code == KEY_PAD_ENTER
 
|| code == KEY_TAB)
//-----------------------------------------------------------------------------
return;
// Purpose: Helper funciton to compare vector and string but reversed
//-----------------------------------------------------------------------------
int Q_vecrstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
{
//check for null substring
if (!substr || !*substr)
return -1;


BaseClass::OnKeyCodePressed(code);
//store variables
}
int substrLen = Q_strlen(substr);


//called on keycode insert
//search for match
void OnKeyTyped(wchar_t c)
for (int i = endindex - substrLen + 1; i >= startindex; --i)
{
{
//if (c == '{')
bool match = true;
//{
for (int j = 0; j < substrLen; ++j)
// //insert:
{
// //{
wchar_t wc = vec[i + j];
// //
char ch = substr[j];
// //}
if (wc != ch)
// //and set cursor in the middle
// BaseClass::OnKeyTyped(c);
// InsertString("\n\n}    ");
// GotoLeft();
// GotoUp();
// SelectNoText();
//}
if (c == '"')
{
//check next item
if (_cursorPos < m_TextStream.Count())
{
{
match = false;
break;
}
}


//check for " so you dont insert string inside string
//found match
if (m_TextStream[_cursorPos] == '"')
if (match)
{
return i;
GotoRight();
}
SelectNone();
return;
}


}
//didnt find match
return -1;
}


//insert:
//holds all the sound names
//""
static CUtlVector<char*> g_SoundDirectories;
//and set cursor in the middle
BaseClass::OnKeyTyped(c);
InsertString("\"");
GotoLeft();
SelectNone();
}
else
{
BaseClass::OnKeyTyped(c);
}
}
};


//-----------------------------------------------------------------------------
// Purpose: Sort function for utl vector
//-----------------------------------------------------------------------------
static int VectorSortFunc(char* const* p1, char* const* p2)
{
return Q_stricmp(*p1, *p2);
}


//soundscape maker text editor panel
//-----------------------------------------------------------------------------
#define TEXT_PANEL_WIDTH 760
// Purpose: Sort function for utl vector
#define TEXT_PANEL_HEIGHT 630
//-----------------------------------------------------------------------------
static int VectorSortFunc(const char* const* p1, const char* const* p2)
{
return Q_stricmp(*p1, *p2);
}


#define TEXT_PANEL_COMMAND_SET "Set"
//-----------------------------------------------------------------------------
#define TEXT_PANEL_COMMAND_SET_OK "SetOk"
// Purpose: Gets all the sound names and stores them into g_SoundDirectories
#define TEXT_PANEL_COMMAND_FIND "FInd"
//-----------------------------------------------------------------------------
 
static void GetSoundNames()
class CSoundscapeTextPanel : public vgui::Frame
{
{
public:
//first off clear the sound array first
DECLARE_CLASS_SIMPLE(CSoundscapeTextPanel, vgui::Frame);
for (int i = 0; i < g_SoundDirectories.Count(); i++)
free(g_SoundDirectories[i]);


CSoundscapeTextPanel(vgui::VPANEL parent, const char* name);
g_SoundDirectories.RemoveAll();


//sets the keyvalues
//directories to search
void Set(KeyValues* keyvalues);
CUtlVector<char*> directoriesToSearch;
void RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent);
directoriesToSearch.AddToTail(strdup("sound"));


//other
//loop until all directories have been processed
void OnCommand(const char* pszCommand);
while (directoriesToSearch.Count() > 0)
void PerformLayout();
{
void OnClose() { BaseClass::OnClose(); }
//take the last added directory (depth-first search)
char* currentDir = directoriesToSearch[directoriesToSearch.Count() - 1];
directoriesToSearch.Remove(directoriesToSearch.Count() - 1);


private:
//create a wildcard path to search all files and subdirs
CTextPanelTextEntry* m_Text;
char searchPath[MAX_PATH];
vgui::Button* m_SetButton;
Q_snprintf(searchPath, sizeof(searchPath), "%s/*", currentDir);
vgui::TextEntry* m_FindTextEntry;
vgui::Button* m_FindButton;
};


//-----------------------------------------------------------------------------
FileFindHandle_t findHandle;
// Purpose: Constructor
const char* filename = g_pFullFileSystem->FindFirst(searchPath, &findHandle);
//-----------------------------------------------------------------------------
CSoundscapeTextPanel::CSoundscapeTextPanel(vgui::VPANEL parent, const char* name)
: BaseClass(nullptr, name)
{
SetParent(parent);


SetKeyBoardInputEnabled(true);
while (filename)
SetMouseInputEnabled(true);
{
//ignore special directories
if (Q_strcmp(filename, ".") != 0 && Q_strcmp(filename, "..") != 0)
{
char fullPath[MAX_PATH];
Q_snprintf(fullPath, sizeof(fullPath), "%s/%s", currentDir, filename);


SetProportional(false);
//if it's a directory, add it to the list for later processing
SetTitleBarVisible(true);
if (g_pFullFileSystem->FindIsDirectory(findHandle))
SetMinimizeButtonVisible(false);
{
SetMaximizeButtonVisible(false);
directoriesToSearch.AddToTail(strdup(fullPath));
SetCloseButtonVisible(true);
}
SetSizeable(true);
else
SetMoveable(true);
{
SetVisible(false);
//check file extension and print if it's .wav or .mp3
SetMinimumSize(575, 120);
const char* ext = V_GetFileExtension(filename);
if (ext && (!Q_stricmp(ext, "wav") || !Q_stricmp(ext, "mp3")))
{
g_SoundDirectories.AddToTail(strdup(fullPath + 6));
}
}
}


int ScreenWide, ScreenTall;
// Move to next file
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
filename = g_pFullFileSystem->FindNext(findHandle);
}


SetTitle("Soundscape Text Editor", true);
//free the memory
SetSize(TEXT_PANEL_WIDTH, TEXT_PANEL_HEIGHT);
g_pFullFileSystem->FindClose(findHandle);
SetPos((ScreenWide - TEXT_PANEL_WIDTH) / 2, (ScreenTall - TEXT_PANEL_HEIGHT) / 2);
free(currentDir);
}


//
g_SoundDirectories.Sort(VectorSortFunc);
}


//make text entry
m_Text = new CTextPanelTextEntry(this, "EditBox");
m_Text->SetBounds(5, 25, TEXT_PANEL_WIDTH - 10, TEXT_PANEL_HEIGHT - 55);
m_Text->SetEnabled(true);
m_Text->SetMultiline(true);
m_Text->SetVerticalScrollbar(true);


//make set button
//text entry for text edit panel
m_SetButton = new vgui::Button(this, "SetButton", "Apply Changes To Keyvalue Maker");
m_SetButton->SetBounds(5, TEXT_PANEL_HEIGHT - 27, 250, 25);
m_SetButton->SetCommand(TEXT_PANEL_COMMAND_SET);


//make find text entry
m_FindTextEntry = new vgui::TextEntry(this, "FindTextEntry");
m_FindTextEntry->SetBounds(450, TEXT_PANEL_HEIGHT - 27, 200, 25);


//make find button
class CTextPanelTextEntry : public vgui::TextEntry
m_FindButton = new vgui::Button(this, "FindButton", "Find String");
m_FindButton->SetBounds(655, TEXT_PANEL_HEIGHT - 27, 100, 25);
m_FindButton->SetCommand(TEXT_PANEL_COMMAND_FIND);
}
 
//-----------------------------------------------------------------------------
// Purpose: Sets the keyvalues
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::Set(KeyValues* keyvalues)
{
{
//write everything into a buffer
public:
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
DECLARE_CLASS_SIMPLE(CTextPanelTextEntry, vgui::TextEntry)


//now write the keyvalues
//constructor
KeyValues* pCurrent = keyvalues;
CTextPanelTextEntry(vgui::Panel* parent, const char* panelName)
while (pCurrent)
: TextEntry(parent, panelName)
{
{
RecursiveSetText(pCurrent, buf, 0);
SetMultiline(true);
 
//put a newline
if (pCurrent->GetNextTrueSubKey())
buf.PutChar('\n');
//get next
pCurrent = pCurrent->GetNextTrueSubKey();
}
}


//write that to the m_Text
//called on keycode typed
m_Text->SetText((const char*)buf.Base());
virtual void OnKeyCodeTyped(vgui::KeyCode code)
}
{
//check for enter or enter
if (code == KEY_ENTER || code == KEY_PAD_ENTER)
InsertString("\n");


//-----------------------------------------------------------------------------
//check for tab
// Purpose: Recursively writes to a util buffer
else if (code == KEY_TAB)
//-----------------------------------------------------------------------------
InsertString("    ");
void CSoundscapeTextPanel::RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent)
{
//write \t indent
for (int i = 0; i < indent; i++)
buffer.PutString("    ");


//write name
//do other key code
buffer.PutChar('"');
else
buffer.PutString(keyvalues->GetName());
BaseClass::OnKeyCodeTyped(code);
buffer.PutString("\"\n");
}


//write {
//called on keycode pressed
for (int i = 0; i < indent; i++)
void OnKeyCodePressed(vgui::KeyCode code)
buffer.PutString("    ");
{
if (code == KEY_ENTER || code == KEY_PAD_ENTER
|| code == KEY_TAB)
return;


buffer.PutString("{\n");
BaseClass::OnKeyCodePressed(code);
}


//increment indent
//called on keycode insert
indent++;
void OnKeyTyped(wchar_t c)
 
//write all the keys first
FOR_EACH_VALUE(keyvalues, value)
{
{
for (int i = 0; i < indent; i++)
//if (c == '{')
buffer.PutString("    ");
//{
 
// //insert:
//write name and value
// //{
buffer.PutChar('"');
// //
buffer.PutString(value->GetName());
// //}
buffer.PutString("\"    ");
// //and set cursor in the middle
// BaseClass::OnKeyTyped(c);
// InsertString("\n\n}   ");
// GotoLeft();
// GotoUp();
// SelectNoText();
//}
if (c == '"')
{
//check next item
if (_cursorPos < m_TextStream.Count())
{


buffer.PutChar('"');
//check for " so you dont insert string inside string
buffer.PutString(value->GetString());
if (m_TextStream[_cursorPos] == '"')
buffer.PutString("\"\n");
{
}
GotoRight();
SelectNone();
return;
}


//write all the subkeys now
}
FOR_EACH_TRUE_SUBKEY(keyvalues, value)
{
//increment indent
RecursiveSetText(value, buffer, indent);


if (value->GetNextTrueSubKey())
//insert:
buffer.PutChar('\n');
//""
}
//and set cursor in the middle
 
BaseClass::OnKeyTyped(c);
//decrement indent
InsertString("\"");
indent--;
GotoLeft();
SelectNone();
}
else
{
BaseClass::OnKeyTyped(c);
}
}
};


//write ending }
for (int i = 0; i < indent; i++)
buffer.PutString("    ");


buffer.PutString("}\n");
}


//-----------------------------------------------------------------------------
//simple clipboard panel
// Purpose: Called on command
class CSoundscapeClipboard : public vgui::Frame
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::OnCommand(const char* pszCommand)
{
{
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET))
public:
{
DECLARE_CLASS_SIMPLE(CSoundscapeClipboard, vgui::Frame)
//play sound
 
vgui::surface()->PlaySound("ui/buttonclickrelease.wav");
CSoundscapeClipboard(SoundscapeClipboardType type);


//check first incase you accidentally press it
//creates all the buttons
vgui::QueryBox* popup = new vgui::QueryBox("Set File?", "Are you sure you want to set the current keyvalues for the keyvalue maker?\nIf there are errors then this could break the keyvalue file.", this);
void CreateButtons();
popup->SetOKCommand(new KeyValues("Command", "command", TEXT_PANEL_COMMAND_SET_OK));
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
return;
}


//set text
//other
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET_OK))
void OnCommand(const char* pszCommand);
{
void OnClose();
//get string
int len = m_Text->GetTextLength() + 1;


char* buf = new char[len];
private:
m_Text->GetText(buf, len);
SoundscapeClipboardType m_Type;
};


g_SoundscapeMaker->SetBuffer(buf);
//static soundscape clipboard panel
static CSoundscapeClipboard* g_SoundscapeClipboard;


//delete string
//-----------------------------------------------------------------------------
delete[] buf;
// Purpose: Constructor
//-----------------------------------------------------------------------------
//hide this
CSoundscapeClipboard::CSoundscapeClipboard(SoundscapeClipboardType type)
SetVisible(false);
: BaseClass(nullptr, "SoundscapeMakerClipboard"), m_Type(type)
return;
{
//get the size of the panel
int tall = 30;
 
switch (type)
{
case SoundscapeClipboardType::Type_SoundscapeName:
tall += 29 * CurrClipboardName.Count();
break;
case SoundscapeClipboardType::Type_SoundscapeData:
tall += 29 * CurrClipboardData.Count();
break;
case SoundscapeClipboardType::Type_SoundscapeRandomWave:
tall += 29 * CurrClipboardRandom.Count();
break;
}
}


//find text
SetParent(enginevgui->GetPanel(VGuiPanel_t::PANEL_TOOLS));
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_FIND))
SetCloseButtonVisible(true);
{
SetSize(300, tall);
//get buffer
MoveToCenterOfScreen();
char buf[128];
SetTitle("Soundscape Clipboard", true);
m_FindTextEntry->GetText(buf, sizeof(buf));
SetSizeable(false);
SetDeleteSelfOnClose(true);


int index = m_Text->_cursorPos + 1;
SetVisible(true);
int find = -1;
RequestFocus();
MoveToFront();
//go in reversed order if holding shift
if (vgui::input()->IsKeyDown(KEY_LSHIFT) || vgui::input()->IsKeyDown(KEY_RSHIFT))
{


//see if we find index
CreateButtons();
find = Q_vecrstr(m_Text->m_TextStream, 0, index - 2, buf);
}
if (find == -1)
//look again
find = Q_vecrstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count() - 1, buf);


//-----------------------------------------------------------------------------
// Purpose: Creates all the clipboard buttons
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::CreateButtons()
{
switch (m_Type)
{
case SoundscapeClipboardType::Type_SoundscapeName:
{
//add all the buttons
for (int i = 0; i < CurrClipboardName.Count(); i++)
{
vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), CFmtStr("%.50s", CurrClipboardName[i]->GetName()));
button->SetBounds(10, 29 + (i * 27), 280, 25);
button->SetCommand(CFmtStr("$PASTE%d", i));
}
}
else
break;
}
case SoundscapeClipboardType::Type_SoundscapeData:
{
//add all the buttons
for (int i = 0; i < CurrClipboardData.Count(); i++)
{
{
vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), "");


//see if we find index
//set text
find = Q_vecstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count(), buf);
const char* name = CurrClipboardData[i]->GetName();
if (find == -1)
if (!Q_stricmp(name, "playrandom"))
{
button->SetText("playrandom");
}
else if (!Q_stricmp(name, "playlooping"))
{
const char* looping = CurrClipboardData[i]->GetString("wave");
if (strlen(looping) > 25)
looping += strlen(looping) - 25;
 
button->SetText(CFmtStr("%s : '%s'", name, looping));
}
else
{
const char* looping = CurrClipboardData[i]->GetString("name");
if (strlen(looping) > 25)
looping += strlen(looping) - 25;


//look again
button->SetText(CFmtStr("%s : '%s'", name, looping));
find = Q_vecstr(m_Text->m_TextStream, 0, index, buf);
}


//set other stuff
button->SetBounds(10, 29 + (i * 27), 280, 25);
button->SetCommand(CFmtStr("$PASTE%d", i));
}
}
 
break;
//check for invalid index
}
if (find == -1)
case SoundscapeClipboardType::Type_SoundscapeRandomWave:
{
//add all the buttons
for (int i = 0; i < CurrClipboardRandom.Count(); i++)
{
{
//play an error sound
vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), CurrClipboardRandom[i]->GetString());
vgui::surface()->PlaySound("resource/warning.wav");
button->SetBounds(10, 29 + (i * 27), 280, 25);
button->SetCommand(CFmtStr("$PASTE%d", i));
}
break;
}
}
}


//get text
//-----------------------------------------------------------------------------
char error[512];
// Purpose: Called when focus is killed
Q_snprintf(error, sizeof(error), "Couldnt find any instances of '%s'", buf);
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::OnClose()
{
g_SoundscapeClipboard = nullptr;


//show an error
BaseClass::OnClose();
vgui::QueryBox* popup = new vgui::QueryBox("No Instances Found", error, this);
}
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


return;
//-----------------------------------------------------------------------------
}
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::OnCommand(const char* command)
{
if (Q_stristr(command, "$PASTE") == command)
{
//get index
int index = atoi(command + 6);


//get number of newlines
//so this is what i am gonna do:
/*int newline = 0;
// 1. copy KeyValue from index <index> to the top of the clipboard
int column = 0;
// 2. call g_SoundscapeMaker.PasteFromClipboard((int)m_Type);
for (int i = 0; i < find; i++)
// 3. remove keyvalues at last index of clipboard CUtlVector
{
switch (m_Type)
if (m_Text->m_TextStream[i] == '\n')
{
{
case SoundscapeClipboardType::Type_SoundscapeName:
newline++;
if (index >= CurrClipboardName.Count() || CurrClipboardName.Count() <= 0)
column = 0;
return;
}
 
else
CurrClipboardName.AddToTail(CurrClipboardName[index]);
{
g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
column++;
CurrClipboardName.Remove(CurrClipboardName.Count() - 1);
}
break;
}*/
case SoundscapeClipboardType::Type_SoundscapeData:
if (index >= CurrClipboardData.Count() || CurrClipboardData.Count() <= 0)
return;


//select that
CurrClipboardData.AddToTail(CurrClipboardData[index]);
m_Text->_cursorPos = find;
g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
m_Text->_select[0] = find;
CurrClipboardData.Remove(CurrClipboardData.Count() - 1);
m_Text->_select[1] = find + Q_strlen(buf);
break;
m_Text->LayoutVerticalScrollBarSlider();
case SoundscapeClipboardType::Type_SoundscapeRandomWave:
m_Text->Repaint();
if (index >= CurrClipboardRandom.Count() || CurrClipboardRandom.Count() <= 0)
m_Text->RequestFocus();
return;


return;
CurrClipboardRandom.AddToTail(CurrClipboardRandom[index]);
g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
CurrClipboardRandom.Remove(CurrClipboardRandom.Count() - 1);
break;
}
}
}
 
BaseClass::OnCommand(pszCommand);
BaseClass::OnCommand(command);
}
}


//-----------------------------------------------------------------------------
// Purpose: Called on panel size changed
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::PerformLayout()
{
BaseClass::PerformLayout();


int wide, tall;
GetSize(wide, tall);


if (m_Text)
//soundscape maker text editor panel
m_Text->SetBounds(5, 25, wide - 10, tall - 55);
#define TEXT_PANEL_WIDTH 760
#define TEXT_PANEL_HEIGHT 630


if (m_SetButton)
#define TEXT_PANEL_COMMAND_SET "Set"
m_SetButton->SetBounds(5, tall - 27, 250, 25);
#define TEXT_PANEL_COMMAND_SET_OK "SetOk"
#define TEXT_PANEL_COMMAND_FIND "FInd"


if (m_FindTextEntry)
class CSoundscapeTextPanel : public vgui::Frame
m_FindTextEntry->SetBounds(wide - 310, tall - 27, 200, 25);
 
if (m_FindButton)
m_FindButton->SetBounds(wide - 105, tall - 27, 100, 25);
}
 
//soundscape settings panel
static CSoundscapeTextPanel* g_SoundscapeTextPanel = nullptr;
 
 
 
 
//soundscape maker text editor panel
#define DEBUG_PANEL_WIDTH 725
#define DEBUG_PANEL_HEIGHT 530
 
#define DEBUG_PANEL_COMMAND_CLEAR "Clear"
 
class CSoundscapeDebugPanel : public vgui::Frame
{
{
public:
public:
DECLARE_CLASS_SIMPLE(CSoundscapeDebugPanel, vgui::Frame);
DECLARE_CLASS_SIMPLE(CSoundscapeTextPanel, vgui::Frame);


CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name);
CSoundscapeTextPanel(vgui::VPANEL parent, const char* name);


//sets the keyvalues
//sets the keyvalues
void AddMessage(Color color, const char* text);
void Set(KeyValues* keyvalues);
void RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent);


//other
//other
Line 682: Line 900:


private:
private:
vgui::RichText* m_Text;
CTextPanelTextEntry* m_Text;
vgui::Button* m_ClearButton;
vgui::Button* m_SetButton;
vgui::Label* m_SoundscapesFadingInText;
vgui::TextEntry* m_FindTextEntry;
 
vgui::Button* m_FindButton;
public:
CGraphPanel* m_PanelSoundscapesFadingIn;
};
};


Line 693: Line 909:
// Purpose: Constructor
// Purpose: Constructor
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CSoundscapeDebugPanel::CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name)
CSoundscapeTextPanel::CSoundscapeTextPanel(vgui::VPANEL parent, const char* name)
: BaseClass(nullptr, name)
: BaseClass(nullptr, name)
{
{
Line 709: Line 925:
SetMoveable(true);
SetMoveable(true);
SetVisible(false);
SetVisible(false);
SetMinimumSize(575, 280);
SetMinimumSize(575, 120);
 
int ScreenWide, ScreenTall;
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
 
SetTitle("Soundscape Text Editor", true);
SetSize(TEXT_PANEL_WIDTH, TEXT_PANEL_HEIGHT);
SetPos((ScreenWide - TEXT_PANEL_WIDTH) / 2, (ScreenTall - TEXT_PANEL_HEIGHT) / 2);


SetTitle("Soundscape Debug Panel", true);
SetSize(DEBUG_PANEL_WIDTH, DEBUG_PANEL_HEIGHT);
SetPos(0, 0);




//make text entry
//make text entry
m_Text = new vgui::RichText(this, "DebugText");
m_Text = new CTextPanelTextEntry(this, "EditBox");
m_Text->SetBounds(5, 25, DEBUG_PANEL_WIDTH - 10, DEBUG_PANEL_HEIGHT - 55);
m_Text->SetBounds(5, 25, TEXT_PANEL_WIDTH - 10, TEXT_PANEL_HEIGHT - 55);
m_Text->SetEnabled(true);
m_Text->SetEnabled(true);
m_Text->SetMultiline(true);
m_Text->SetVerticalScrollbar(true);
m_Text->SetVerticalScrollbar(true);


//make clear button
//make set button
m_ClearButton = new vgui::Button(this, "ClearButton", "Clear");
m_SetButton = new vgui::Button(this, "SetButton", "Apply Changes To Keyvalue Maker");
m_ClearButton->SetBounds(5, DEBUG_PANEL_HEIGHT - 215, DEBUG_PANEL_WIDTH - 10, 25);
m_SetButton->SetBounds(5, TEXT_PANEL_HEIGHT - 27, 250, 25);
m_ClearButton->SetCommand(DEBUG_PANEL_COMMAND_CLEAR);
m_SetButton->SetCommand(TEXT_PANEL_COMMAND_SET);


//make fading in label
//make find text entry
m_SoundscapesFadingInText = new vgui::Label(this, "LabelFadingIn", "Soundscapes Fading In");
m_FindTextEntry = new vgui::TextEntry(this, "FindTextEntry");
m_SoundscapesFadingInText->SetBounds(5, DEBUG_PANEL_HEIGHT - 187, DEBUG_PANEL_WIDTH - 10, 20);
m_FindTextEntry->SetBounds(450, TEXT_PANEL_HEIGHT - 27, 200, 25);


//make soundscapes fading in thing
//make find button
m_PanelSoundscapesFadingIn = new CGraphPanel(this, "SoundscapesFadingIn");
m_FindButton = new vgui::Button(this, "FindButton", "Find String");
m_PanelSoundscapesFadingIn->SetBounds(5, DEBUG_PANEL_HEIGHT - 165, DEBUG_PANEL_WIDTH - 10, 155);
m_FindButton->SetBounds(655, TEXT_PANEL_HEIGHT - 27, 100, 25);
m_PanelSoundscapesFadingIn->SetMaxTextValue(1.0f);
m_FindButton->SetCommand(TEXT_PANEL_COMMAND_FIND);
m_PanelSoundscapesFadingIn->SetHorizontalLinesMax(5);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: adds a message to the debug panel
// Purpose: Sets the keyvalues
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::AddMessage(Color color, const char* text)
void CSoundscapeTextPanel::Set(KeyValues* keyvalues)
{
{
m_Text->InsertColorChange(color);
//write everything into a buffer
m_Text->InsertString(text);
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
 
//now write the keyvalues
KeyValues* pCurrent = keyvalues;
while (pCurrent)
{
RecursiveSetText(pCurrent, buf, 0);


m_Text->SetMaximumCharCount(100000);
//put a newline
}
if (pCurrent->GetNextTrueSubKey())
buf.PutChar('\n');


//-----------------------------------------------------------------------------
//get next
// Purpose: Called on command
pCurrent = pCurrent->GetNextTrueSubKey();
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, DEBUG_PANEL_COMMAND_CLEAR))
{
//clear the text
m_Text->SetText("");
m_Text->GotoTextEnd();
return;
}
}


BaseClass::OnCommand(pszCommand);
//write that to the m_Text
m_Text->SetText((const char*)buf.Base());
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called on panel size changed
// Purpose: Recursively writes to a util buffer
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::PerformLayout()
void CSoundscapeTextPanel::RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent)
{
{
BaseClass::PerformLayout();
//write \t indent
for (int i = 0; i < indent; i++)
buffer.PutString("    ");


int wide, tall;
//write name
GetSize(wide, tall);
buffer.PutChar('"');
buffer.PutString(keyvalues->GetName());
buffer.PutString("\"\n");


m_Text->SetBounds(5, 25, wide - 10, tall - 245);
//write {
m_ClearButton->SetBounds(5, tall - 215, wide - 10, 25);
for (int i = 0; i < indent; i++)
m_PanelSoundscapesFadingIn->SetBounds(5, tall - 165, wide - 10, 155);
buffer.PutString("    ");
m_SoundscapesFadingInText->SetBounds(5, tall - 187, wide - 10, 20);
}


//soundscape debug panel
buffer.PutString("{\n");
static CSoundscapeDebugPanel* g_SoundscapeDebugPanel = nullptr;


//-----------------------------------------------------------------------------
//increment indent
// Purpose: Function to print text to debug panel
indent++;
//-----------------------------------------------------------------------------
void SoundscapePrint(Color color, const char* msg, ...)
{
//format string
va_list args;
va_start(args, msg);


char buf[2048];
//write all the keys first
Q_vsnprintf(buf, sizeof(buf), msg, args);
FOR_EACH_VALUE(keyvalues, value)
g_SoundscapeDebugPanel->AddMessage(color, buf);
{
for (int i = 0; i < indent; i++)
buffer.PutString("    ");


va_end(args);
//write name and value
}
buffer.PutChar('"');
buffer.PutString(value->GetName());
buffer.PutString("\"    ");


//-----------------------------------------------------------------------------
buffer.PutChar('"');
// Purpose: Function to add a line to the soundscape debug panel
buffer.PutString(value->GetString());
//-----------------------------------------------------------------------------
buffer.PutString("\"\n");
void SoundscapeAddLine(Color color, float speed, float width, bool accending)
}
{
if (g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines() <= 6)
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->AddLine(accending, color.r(), color.g(), color.b(), speed, width);
}


//-----------------------------------------------------------------------------
//write all the subkeys now
// Purpose: Function to get debug line num
FOR_EACH_TRUE_SUBKEY(keyvalues, value)
//-----------------------------------------------------------------------------
{
int SoundscapeGetLineNum()
//increment indent
{
RecursiveSetText(value, buffer, indent);
return g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines();
}


//vector positions
if (value->GetNextTrueSubKey())
Vector g_SoundscapePositions[] = {
buffer.PutChar('\n');
vec3_origin,
}
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin
};


//decrement indent
indent--;


//write ending }
for (int i = 0; i < indent; i++)
buffer.PutString("    ");


#define SETTINGS_PANEL_WIDTH 350
buffer.PutString("}\n");
#define SETTINGS_PANEL_HEIGHT 277
}


#define SETTINGS_PANEL_COMMAND_POS1 "GetPos0"
//-----------------------------------------------------------------------------
#define SETTINGS_PANEL_COMMAND_POS2 "GetPos1"
// Purpose: Called on command
#define SETTINGS_PANEL_COMMAND_POS3 "GetPos2"
//-----------------------------------------------------------------------------
#define SETTINGS_PANEL_COMMAND_POS4 "GetPos3"
void CSoundscapeTextPanel::OnCommand(const char* pszCommand)
#define SETTINGS_PANEL_COMMAND_POS5 "GetPos4"
{
#define SETTINGS_PANEL_COMMAND_POS6 "GetPos5"
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET))
#define SETTINGS_PANEL_COMMAND_POS7 "GetPos6"
{
#define SETTINGS_PANEL_COMMAND_POS8 "GetPos7"
//play sound
#define SETTINGS_PANEL_COMMAND_SHOW "ShowPositions"
vgui::surface()->PlaySound("ui/buttonclickrelease.wav");
#define SETTINGS_PANEL_COMMAND_DEBUG "Debug"


#define MAX_SOUNDSCAPES 8
//check first incase you accidentally press it
vgui::QueryBox* popup = new vgui::QueryBox("Set File?", "Are you sure you want to set the current keyvalues for the keyvalue maker?\nIf there are errors then this could break the keyvalue file.", this);
popup->SetOKCommand(new KeyValues("Command", "command", TEXT_PANEL_COMMAND_SET_OK));
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
return;
}


//soundscape maker settings panel
//set text
class CSoundscapeSettingsPanel : public vgui::Frame
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET_OK))
{
{
public:
//get string
DECLARE_CLASS_SIMPLE(CSoundscapeSettingsPanel, vgui::Frame);
int len = m_Text->GetTextLength() + 1;


CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name);
char* buf = new char[len];
m_Text->GetText(buf, len);


//other
g_SoundscapeMaker->SetBuffer(buf);
void OnCommand(const char* pszCommand);


//sets the text
//delete string
void SetItem(int index, const Vector& value);
delete[] buf;


//message funcs
//hide this
MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);
SetVisible(false);
return;
}


~CSoundscapeSettingsPanel();
//find text
if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_FIND))
{
//get buffer
char buf[128];
m_FindTextEntry->GetText(buf, sizeof(buf));


private:
int index = m_Text->_cursorPos + 1;
//position text entries
int find = -1;
vgui::TextEntry* m_TextEntryPos0;
vgui::TextEntry* m_TextEntryPos1;
vgui::TextEntry* m_TextEntryPos2;
vgui::TextEntry* m_TextEntryPos3;
vgui::TextEntry* m_TextEntryPos4;
vgui::TextEntry* m_TextEntryPos5;
vgui::TextEntry* m_TextEntryPos6;
vgui::TextEntry* m_TextEntryPos7;
vgui::CheckButton* m_ShowSoundscapePositions;
vgui::Button* m_ShowSoundscapeDebug;


friend class CSoundscapeMaker;
//go in reversed order if holding shift
};
if (vgui::input()->IsKeyDown(KEY_LSHIFT) || vgui::input()->IsKeyDown(KEY_RSHIFT))
{


//see if we find index
find = Q_vecrstr(m_Text->m_TextStream, 0, index - 2, buf);
if (find == -1)


//-----------------------------------------------------------------------------
//look again
// Purpose: Constructor
find = Q_vecrstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count() - 1, buf);
//-----------------------------------------------------------------------------
CSoundscapeSettingsPanel::CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name)
: BaseClass(nullptr, name)
{
SetParent(parent);


SetKeyBoardInputEnabled(true);
}
SetMouseInputEnabled(true);
else
{


SetProportional(false);
//see if we find index
SetTitleBarVisible(true);
find = Q_vecstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count(), buf);
SetMinimizeButtonVisible(false);
if (find == -1)
SetMaximizeButtonVisible(false);
SetCloseButtonVisible(true);
SetSizeable(false);
SetMoveable(true);
SetVisible(false);


//set the size and pos
//look again
int ScreenWide, ScreenTall;
find = Q_vecstr(m_Text->m_TextStream, 0, index, buf);
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);


SetTitle("Soundscape Maker Settings", true);
}
SetSize(SETTINGS_PANEL_WIDTH, SETTINGS_PANEL_HEIGHT);
SetPos((ScreenWide - SETTINGS_PANEL_WIDTH) / 2, (ScreenTall - SETTINGS_PANEL_HEIGHT) / 2);


//check for invalid index
if (find == -1)
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//get text
char error[512];
Q_snprintf(error, sizeof(error), "Couldnt find any instances of '%s'", buf);


//load settings
//show an error
KeyValues* settings = new KeyValues("settings");
vgui::QueryBox* popup = new vgui::QueryBox("No Instances Found", error, this);
if (!settings->LoadFromFile(filesystem, "cfg/soundscape_maker.txt", "MOD"))
popup->SetOKButtonText("Ok");
ConWarning("Failed to load settings for 'cfg/soundscape_maker.txt'. Using default settings.");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


//get positions
return;
const char* pos0 = settings->GetString("Position0", "0 0 0");
}
const char* pos1 = settings->GetString("Position1", "0 0 0");
const char* pos2 = settings->GetString("Position2", "0 0 0");
const char* pos3 = settings->GetString("Position3", "0 0 0");
const char* pos4 = settings->GetString("Position4", "0 0 0");
const char* pos5 = settings->GetString("Position5", "0 0 0");
const char* pos6 = settings->GetString("Position6", "0 0 0");
const char* pos7 = settings->GetString("Position7", "0 0 0");


//create position text 1
//get number of newlines
m_TextEntryPos0 = new vgui::TextEntry(this, "PosTextEntry0");
/*int newline = 0;
m_TextEntryPos0->SetEnabled(true);
int column = 0;
m_TextEntryPos0->SetText(pos0 ? pos0 : "0 0 0");
for (int i = 0; i < find; i++)
m_TextEntryPos0->SetBounds(5, 30, 230, 20);
{
m_TextEntryPos0->SetMaximumCharCount(32);
if (m_Text->m_TextStream[i] == '\n')
{
newline++;
column = 0;
}
else
{
column++;
}
}*/
 
//select that
m_Text->_cursorPos = find;
m_Text->_select[0] = find;
m_Text->_select[1] = find + Q_strlen(buf);
m_Text->LayoutVerticalScrollBarSlider();
m_Text->Repaint();
m_Text->RequestFocus();


//create position 1 button
return;
vgui::Button* m_ButtonPos0 = new vgui::Button(this, "PosButton0", "Find Position 0", this, SETTINGS_PANEL_COMMAND_POS1);
}
m_ButtonPos0->SetBounds(240, 30, 100, 20);


//create position text 1
BaseClass::OnCommand(pszCommand);
m_TextEntryPos1 = new vgui::TextEntry(this, "PosTextEntry1");
}
m_TextEntryPos1->SetEnabled(true);
m_TextEntryPos1->SetText(pos1 ? pos1 : "0 0 0");
m_TextEntryPos1->SetBounds(5, 55, 230, 20);
m_TextEntryPos1->SetMaximumCharCount(32);


//create position 2 button
//-----------------------------------------------------------------------------
vgui::Button* m_ButtonPos1 = new vgui::Button(this, "PosButton1", "Find Position 1", this, SETTINGS_PANEL_COMMAND_POS2);
// Purpose: Called on panel size changed
m_ButtonPos1->SetBounds(240, 55, 100, 20);
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::PerformLayout()
//create position text 3
{
m_TextEntryPos2 = new vgui::TextEntry(this, "PosTextEntry0");
BaseClass::PerformLayout();
m_TextEntryPos2->SetEnabled(true);
m_TextEntryPos2->SetText(pos2 ? pos2 : "0 0 0");
m_TextEntryPos2->SetBounds(5, 80, 230, 20);
m_TextEntryPos2->SetMaximumCharCount(32);


//create position 1 button
int wide, tall;
vgui::Button* m_ButtonPos2 = new vgui::Button(this, "PosButton2", "Find Position 2", this, SETTINGS_PANEL_COMMAND_POS3);
GetSize(wide, tall);
m_ButtonPos2->SetBounds(240, 80, 100, 20);


// create position text 4
if (m_Text)
m_TextEntryPos3 = new vgui::TextEntry(this, "PosTextEntry3");
m_Text->SetBounds(5, 25, wide - 10, tall - 55);
m_TextEntryPos3->SetEnabled(true);
m_TextEntryPos3->SetText(pos3 ? pos3 : "0 0 0");
m_TextEntryPos3->SetBounds(5, 105, 230, 20);
m_TextEntryPos3->SetMaximumCharCount(32);


// create position 4 button
if (m_SetButton)
vgui::Button* m_ButtonPos3 = new vgui::Button(this, "PosButton3", "Find Position 3", this, SETTINGS_PANEL_COMMAND_POS4);
m_SetButton->SetBounds(5, tall - 27, 250, 25);
m_ButtonPos3->SetBounds(240, 105, 100, 20);


// create position text 5
if (m_FindTextEntry)
m_TextEntryPos4 = new vgui::TextEntry(this, "PosTextEntry4");
m_FindTextEntry->SetBounds(wide - 310, tall - 27, 200, 25);
m_TextEntryPos4->SetEnabled(true);
m_TextEntryPos4->SetText(pos4 ? pos4 : "0 0 0");
m_TextEntryPos4->SetBounds(5, 130, 230, 20);
m_TextEntryPos4->SetMaximumCharCount(32);


// create position 5 button
if (m_FindButton)
vgui::Button* m_ButtonPos4 = new vgui::Button(this, "PosButton4", "Find Position 4", this, SETTINGS_PANEL_COMMAND_POS5);
m_FindButton->SetBounds(wide - 105, tall - 27, 100, 25);
m_ButtonPos4->SetBounds(240, 130, 100, 20);
}


// create position text 6
//soundscape settings panel
m_TextEntryPos5 = new vgui::TextEntry(this, "PosTextEntry5");
static CSoundscapeTextPanel* g_SoundscapeTextPanel = nullptr;
m_TextEntryPos5->SetEnabled(true);
m_TextEntryPos5->SetText(pos5 ? pos5 : "0 0 0");
m_TextEntryPos5->SetBounds(5, 155, 230, 20);
m_TextEntryPos5->SetMaximumCharCount(32);


// create position 6 button
vgui::Button* m_ButtonPos5 = new vgui::Button(this, "PosButton5", "Find Position 5", this, SETTINGS_PANEL_COMMAND_POS6);
m_ButtonPos5->SetBounds(240, 155, 100, 20);


// create position text 7
m_TextEntryPos6 = new vgui::TextEntry(this, "PosTextEntry6");
m_TextEntryPos6->SetEnabled(true);
m_TextEntryPos6->SetText(pos6 ? pos6 : "0 0 0");
m_TextEntryPos6->SetBounds(5, 180, 230, 20);
m_TextEntryPos6->SetMaximumCharCount(32);


// create position 7 button
vgui::Button* m_ButtonPos6 = new vgui::Button(this, "PosButton6", "Find Position 6", this, SETTINGS_PANEL_COMMAND_POS7);
m_ButtonPos6->SetBounds(240, 180, 100, 20);
// create position text 8
m_TextEntryPos7 = new vgui::TextEntry(this, "PosTextEntry7");
m_TextEntryPos7->SetEnabled(true);
m_TextEntryPos7->SetText(pos7 ? pos7 : "0 0 0");
m_TextEntryPos7->SetBounds(5, 205, 230, 20);
m_TextEntryPos7->SetMaximumCharCount(32);


// create position 8 button
//soundscape maker text editor panel
vgui::Button* m_ButtonPos7 = new vgui::Button(this, "PosButton7", "Find Position 7", this, SETTINGS_PANEL_COMMAND_POS8);
#define DEBUG_PANEL_WIDTH 725
m_ButtonPos7->SetBounds(240, 205, 100, 20);
#define DEBUG_PANEL_HEIGHT 530


// create show soundscape positions checkbox
#define DEBUG_PANEL_COMMAND_CLEAR "Clear"
m_ShowSoundscapePositions = new vgui::CheckButton(this, "ShowCheckox", "Show Soundscape Positions");
m_ShowSoundscapePositions->SetBounds(75, 225, 200, 20);
m_ShowSoundscapePositions->SetCommand(SETTINGS_PANEL_COMMAND_SHOW);
m_ShowSoundscapePositions->SetSelected(settings->GetBool("ShowSoundscapes", false));


//set convar value
class CSoundscapeDebugPanel : public vgui::Frame
ConVar* cv = cvar->FindVar("__ss_draw");
{
if (cv)
public:
cv->SetValue(m_ShowSoundscapePositions->IsSelected());
DECLARE_CLASS_SIMPLE(CSoundscapeDebugPanel, vgui::Frame);


//create divider
CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name);
vgui::Divider* div = new vgui::Divider(this, "Divider");
div->SetBounds(-2, 247, SETTINGS_PANEL_WIDTH + 4, 2);


//create debug thing
//sets the keyvalues
m_ShowSoundscapeDebug = new vgui::Button(this, "DebugInfo", "Show soundscape debug panel");
void AddMessage(Color color, const char* text);
m_ShowSoundscapeDebug->SetBounds(20, 254, SETTINGS_PANEL_WIDTH - 40, 20);
m_ShowSoundscapeDebug->SetCommand(SETTINGS_PANEL_COMMAND_DEBUG);


//set server positions
//other
ConCommand* cc = cvar->FindCommand("__ss_maker_set");
void OnCommand(const char* pszCommand);
if (cc)
void PerformLayout();
{
void OnClose() { BaseClass::OnClose(); }
CCommand args;


//do pos 0
private:
if (pos0)
vgui::RichText* m_Text;
{
vgui::Button* m_ClearButton;
args.Tokenize(CFmtStr("ssmaker 0 %s 1", pos0));
vgui::Label* m_SoundscapesFadingInText;
cc->Dispatch(args);


UTIL_StringToVector(g_SoundscapePositions[0].Base(), pos0);
public:
}
CGraphPanel* m_PanelSoundscapesFadingIn;
};


//do pos 1
//-----------------------------------------------------------------------------
if (pos1)
// Purpose: Constructor
{
//-----------------------------------------------------------------------------
args.Tokenize(CFmtStr("ssmaker 1 %s 1", pos1));
CSoundscapeDebugPanel::CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name)
cc->Dispatch(args);
: BaseClass(nullptr, name)
{
SetParent(parent);


UTIL_StringToVector(g_SoundscapePositions[1].Base(), pos1);
SetKeyBoardInputEnabled(true);
}
SetMouseInputEnabled(true);


//do pos 2
SetProportional(false);
if (pos2)
SetTitleBarVisible(true);
{
SetMinimizeButtonVisible(false);
args.Tokenize(CFmtStr("ssmaker 2 %s 1", pos2));
SetMaximizeButtonVisible(false);
cc->Dispatch(args);
SetCloseButtonVisible(true);
SetSizeable(true);
SetMoveable(true);
SetVisible(false);
SetMinimumSize(575, 280);


UTIL_StringToVector(g_SoundscapePositions[2].Base(), pos2);
SetTitle("Soundscape Debug Panel", true);
}
SetSize(DEBUG_PANEL_WIDTH, DEBUG_PANEL_HEIGHT);
SetPos(0, 0);


//do pos 3
if (pos3)
{
args.Tokenize(CFmtStr("ssmaker 3 %s 1", pos3));
cc->Dispatch(args);


UTIL_StringToVector(g_SoundscapePositions[3].Base(), pos3);
}


//do pos 4
//make text entry
if (pos4)
m_Text = new vgui::RichText(this, "DebugText");
{
m_Text->SetBounds(5, 25, DEBUG_PANEL_WIDTH - 10, DEBUG_PANEL_HEIGHT - 55);
args.Tokenize(CFmtStr("ssmaker 4 %s 1", pos4));
m_Text->SetEnabled(true);
cc->Dispatch(args);
m_Text->SetVerticalScrollbar(true);


UTIL_StringToVector(g_SoundscapePositions[4].Base(), pos4);
//make clear button
}
m_ClearButton = new vgui::Button(this, "ClearButton", "Clear");
m_ClearButton->SetBounds(5, DEBUG_PANEL_HEIGHT - 215, DEBUG_PANEL_WIDTH - 10, 25);
m_ClearButton->SetCommand(DEBUG_PANEL_COMMAND_CLEAR);


//do pos 5
//make fading in label
if (pos5)
m_SoundscapesFadingInText = new vgui::Label(this, "LabelFadingIn", "Soundscapes Fading In");
{
m_SoundscapesFadingInText->SetBounds(5, DEBUG_PANEL_HEIGHT - 187, DEBUG_PANEL_WIDTH - 10, 20);
args.Tokenize(CFmtStr("ssmaker 5 %s 1", pos5));
cc->Dispatch(args);


UTIL_StringToVector(g_SoundscapePositions[5].Base(), pos5);
//make soundscapes fading in thing
}
m_PanelSoundscapesFadingIn = new CGraphPanel(this, "SoundscapesFadingIn");
m_PanelSoundscapesFadingIn->SetBounds(5, DEBUG_PANEL_HEIGHT - 165, DEBUG_PANEL_WIDTH - 10, 155);
m_PanelSoundscapesFadingIn->SetMaxTextValue(1.0f);
m_PanelSoundscapesFadingIn->SetHorizontalLinesMax(5);
}


//do pos 6
//-----------------------------------------------------------------------------
if (pos6)
// Purpose: adds a message to the debug panel
{
//-----------------------------------------------------------------------------
args.Tokenize(CFmtStr("ssmaker 6 %s 1", pos6));
void CSoundscapeDebugPanel::AddMessage(Color color, const char* text)
cc->Dispatch(args);
{
m_Text->InsertColorChange(color);
m_Text->InsertString(text);


UTIL_StringToVector(g_SoundscapePositions[6].Base(), pos6);
m_Text->SetMaximumCharCount(100000);
}
 
//do pos 7
if (pos7)
{
args.Tokenize(CFmtStr("ssmaker 7 %s", pos7));
cc->Dispatch(args);
 
UTIL_StringToVector(g_SoundscapePositions[7].Base(), pos7);
}
}
 
//delete settings
settings->deleteThis();
}
}


Line 1,123: Line 1,287:
// Purpose: Called on command
// Purpose: Called on command
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::OnCommand(const char* pszCommand)
void CSoundscapeDebugPanel::OnCommand(const char* pszCommand)
{
{
if (Q_strstr(pszCommand, "GetPos") == pszCommand)
if (!Q_strcmp(pszCommand, DEBUG_PANEL_COMMAND_CLEAR))
{
{
//search for number
//clear the text
pszCommand = pszCommand + 6;
m_Text->SetText("");
 
m_Text->GotoTextEnd();
//execute command
static ConCommand* cc = cvar->FindCommand("__ss_maker_start");
if (cc)
{
//hide everything first
g_SoundscapeMaker->SetAllVisible(false);
 
CCommand args;
args.Tokenize(CFmtStr("ssmaker %d", atoi(pszCommand)));
cc->Dispatch(args);
}
 
return;
}
 
else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_SHOW))
{
static ConVar* cv = cvar->FindVar("__ss_draw");
if (cv)
cv->SetValue(m_ShowSoundscapePositions->IsSelected());
 
return;
}
 
//handle debug thing
else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_DEBUG))
{
g_SoundscapeDebugPanel->SetVisible(true);
g_SoundscapeDebugPanel->RequestFocus();
g_SoundscapeDebugPanel->MoveToFront();
return;
return;
}
}
Line 1,167: Line 1,301:


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Purpose: Called on panel size changed
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::SetItem(int index, const Vector& value)
void CSoundscapeDebugPanel::PerformLayout()
{
{
const char* text = CFmtStr("%.3f %.3f %.3f", value.x, value.y, value.z);
BaseClass::PerformLayout();


//check index
int wide, tall;
switch (index)
GetSize(wide, tall);
{
case 0:
m_TextEntryPos0->RequestFocus();
m_TextEntryPos0->SetText(text);
g_SoundscapePositions[0] = value;
break;
case 1:
m_TextEntryPos1->RequestFocus();
m_TextEntryPos1->SetText(text);
g_SoundscapePositions[1] = value;
break;
case 2:
m_TextEntryPos2->RequestFocus();
m_TextEntryPos2->SetText(text);
g_SoundscapePositions[2] = value;
break;
case 3:
m_TextEntryPos3->RequestFocus();
m_TextEntryPos3->SetText(text);
g_SoundscapePositions[3] = value;
break;
case 4:
m_TextEntryPos4->RequestFocus();
m_TextEntryPos4->SetText(text);
g_SoundscapePositions[4] = value;
break;
case 5:
m_TextEntryPos5->RequestFocus();
m_TextEntryPos5->SetText(text);
g_SoundscapePositions[5] = value;
break;


case 6:
m_Text->SetBounds(5, 25, wide - 10, tall - 245);
m_TextEntryPos6->RequestFocus();
m_ClearButton->SetBounds(5, tall - 215, wide - 10, 25);
m_TextEntryPos6->SetText(text);
m_PanelSoundscapesFadingIn->SetBounds(5, tall - 165, wide - 10, 155);
g_SoundscapePositions[6] = value;
m_SoundscapesFadingInText->SetBounds(5, tall - 187, wide - 10, 20);
break;
}


case 7:
//soundscape debug panel
m_TextEntryPos7->RequestFocus();
static CSoundscapeDebugPanel* g_SoundscapeDebugPanel = nullptr;
m_TextEntryPos7->SetText(text);
g_SoundscapePositions[7] = value;
break;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called on text changed
// Purpose: Function to print text to debug panel
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::OnTextChanged(KeyValues* kv)
void SoundscapePrint(Color color, const char* msg, ...)
{
{
static ConCommand* cc = cvar->FindCommand("__ss_maker_set");
//format string
va_list args;
va_start(args, msg);


//check focus
char buf[2048];
if (m_TextEntryPos0->HasFocus())
Q_vsnprintf(buf, sizeof(buf), msg, args);
{
g_SoundscapeDebugPanel->AddMessage(color, buf);
//get text
char buf[512];
m_TextEntryPos0->GetText(buf, sizeof(buf));


//convert to vector
va_end(args);
UTIL_StringToVector(g_SoundscapePositions[0].Base(), buf);
}


//do command
//-----------------------------------------------------------------------------
if (cc)
// Purpose: Function to add a line to the soundscape debug panel
{
//-----------------------------------------------------------------------------
CCommand args;
void SoundscapeAddLine(Color color, float speed, float width, bool accending)
args.Tokenize(CFmtStr("ssmaker 0 %s 1", buf));
{
cc->Dispatch(args);
if (g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines() <= 6)
}
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->AddLine(accending, color.r(), color.g(), color.b(), speed, width);
}


return;
//-----------------------------------------------------------------------------
}
// Purpose: Function to get debug line num
//-----------------------------------------------------------------------------
int SoundscapeGetLineNum()
{
return g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines();
}


//check focus
//vector positions
if (m_TextEntryPos1->HasFocus())
Vector g_SoundscapePositions[] = {
{
vec3_origin,
//get text
vec3_origin,
char buf[512];
vec3_origin,
m_TextEntryPos1->GetText(buf, sizeof(buf));
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin
};


//convert to vector
#define SETTINGS_PANEL_WIDTH 350
UTIL_StringToVector(g_SoundscapePositions[1].Base(), buf);
#define SETTINGS_PANEL_HEIGHT 277


//do command
#define SETTINGS_PANEL_COMMAND_POS1 "GetPos0"
if (cc)
#define SETTINGS_PANEL_COMMAND_POS2 "GetPos1"
{
#define SETTINGS_PANEL_COMMAND_POS3 "GetPos2"
CCommand args;
#define SETTINGS_PANEL_COMMAND_POS4 "GetPos3"
args.Tokenize(CFmtStr("ssmaker 1 %s 1", buf));
#define SETTINGS_PANEL_COMMAND_POS5 "GetPos4"
cc->Dispatch(args);
#define SETTINGS_PANEL_COMMAND_POS6 "GetPos5"
}
#define SETTINGS_PANEL_COMMAND_POS7 "GetPos6"
#define SETTINGS_PANEL_COMMAND_POS8 "GetPos7"
#define SETTINGS_PANEL_COMMAND_SHOW "ShowPositions"
#define SETTINGS_PANEL_COMMAND_DEBUG "Debug"


return;
#define MAX_SOUNDSCAPES 8
}


//check focus
//soundscape maker settings panel
if (m_TextEntryPos2->HasFocus())
class CSoundscapeSettingsPanel : public vgui::Frame
{
{
//get text
public:
char buf[512];
DECLARE_CLASS_SIMPLE(CSoundscapeSettingsPanel, vgui::Frame);
m_TextEntryPos2->GetText(buf, sizeof(buf));


//convert to vector
CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name);
UTIL_StringToVector(g_SoundscapePositions[2].Base(), buf);


//do command
//other
if (cc)
void OnCommand(const char* pszCommand);
{
CCommand args;
args.Tokenize(CFmtStr("ssmaker 2 %s 1", buf));
cc->Dispatch(args);
}


return;
//sets the text
}
void SetItem(int index, const Vector& value);


//check focus
//message funcs
if (m_TextEntryPos3->HasFocus())
MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);
{
//get text
char buf[512];
m_TextEntryPos3->GetText(buf, sizeof(buf));


//convert to vector
~CSoundscapeSettingsPanel();
UTIL_StringToVector(g_SoundscapePositions[3].Base(), buf);


//do command
private:
if (cc)
//position text entries
{
vgui::TextEntry* m_TextEntryPos0;
CCommand args;
vgui::TextEntry* m_TextEntryPos1;
args.Tokenize(CFmtStr("ssmaker 3 %s 1", buf));
vgui::TextEntry* m_TextEntryPos2;
cc->Dispatch(args);
vgui::TextEntry* m_TextEntryPos3;
}
vgui::TextEntry* m_TextEntryPos4;
vgui::TextEntry* m_TextEntryPos5;
vgui::TextEntry* m_TextEntryPos6;
vgui::TextEntry* m_TextEntryPos7;
vgui::CheckButton* m_ShowSoundscapePositions;
vgui::Button* m_ShowSoundscapeDebug;


return;
friend class CSoundscapeMaker;
}
};


//check focus
if (m_TextEntryPos4->HasFocus())
{
//get text
char buf[512];
m_TextEntryPos4->GetText(buf, sizeof(buf));


//convert to vector
//-----------------------------------------------------------------------------
UTIL_StringToVector(g_SoundscapePositions[4].Base(), buf);
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundscapeSettingsPanel::CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name)
: BaseClass(nullptr, name)
{
SetParent(parent);


//do command
SetKeyBoardInputEnabled(true);
if (cc)
SetMouseInputEnabled(true);
{
CCommand args;
args.Tokenize(CFmtStr("ssmaker 4 %s 1", buf));
cc->Dispatch(args);
}


return;
SetProportional(false);
}
SetTitleBarVisible(true);
SetMinimizeButtonVisible(false);
SetMaximizeButtonVisible(false);
SetCloseButtonVisible(true);
SetSizeable(false);
SetMoveable(true);
SetVisible(false);


//check focus
//set the size and pos
if (m_TextEntryPos5->HasFocus())
int ScreenWide, ScreenTall;
{
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
//get text
char buf[512];
m_TextEntryPos5->GetText(buf, sizeof(buf));


//convert to vector
SetTitle("Soundscape Maker Settings", true);
UTIL_StringToVector(g_SoundscapePositions[5].Base(), buf);
SetSize(SETTINGS_PANEL_WIDTH, SETTINGS_PANEL_HEIGHT);
SetPos((ScreenWide - SETTINGS_PANEL_WIDTH) / 2, (ScreenTall - SETTINGS_PANEL_HEIGHT) / 2);


//do command
if (cc)
{
CCommand args;
args.Tokenize(CFmtStr("ssmaker 5 %s 1", buf));
cc->Dispatch(args);
}


return;
}


//check focus
//load settings
if (m_TextEntryPos6->HasFocus())
KeyValues* settings = new KeyValues("settings");
{
if (!settings->LoadFromFile(filesystem, "cfg/soundscape_maker.txt", "MOD"))
//get text
ConWarning("Failed to load settings for 'cfg/soundscape_maker.txt'. Using default settings.");
char buf[512];
m_TextEntryPos6->GetText(buf, sizeof(buf));


//convert to vector
//get positions
UTIL_StringToVector(g_SoundscapePositions[6].Base(), buf);
const char* pos0 = settings->GetString("Position0", "0 0 0");
const char* pos1 = settings->GetString("Position1", "0 0 0");
const char* pos2 = settings->GetString("Position2", "0 0 0");
const char* pos3 = settings->GetString("Position3", "0 0 0");
const char* pos4 = settings->GetString("Position4", "0 0 0");
const char* pos5 = settings->GetString("Position5", "0 0 0");
const char* pos6 = settings->GetString("Position6", "0 0 0");
const char* pos7 = settings->GetString("Position7", "0 0 0");


//do command
//create position text 1
if (cc)
m_TextEntryPos0 = new vgui::TextEntry(this, "PosTextEntry0");
{
m_TextEntryPos0->SetEnabled(true);
CCommand args;
m_TextEntryPos0->SetText(pos0 ? pos0 : "0 0 0");
args.Tokenize(CFmtStr("ssmaker 6 %s 1", buf));
m_TextEntryPos0->SetBounds(5, 30, 230, 20);
cc->Dispatch(args);
m_TextEntryPos0->SetMaximumCharCount(32);
}


return;
//create position 1 button
}
vgui::Button* m_ButtonPos0 = new vgui::Button(this, "PosButton0", "Find Position 0", this, SETTINGS_PANEL_COMMAND_POS1);
m_ButtonPos0->SetBounds(240, 30, 100, 20);


//check focus
//create position text 1
if (m_TextEntryPos7->HasFocus())
m_TextEntryPos1 = new vgui::TextEntry(this, "PosTextEntry1");
{
m_TextEntryPos1->SetEnabled(true);
//get text
m_TextEntryPos1->SetText(pos1 ? pos1 : "0 0 0");
char buf[512];
m_TextEntryPos1->SetBounds(5, 55, 230, 20);
m_TextEntryPos7->GetText(buf, sizeof(buf));
m_TextEntryPos1->SetMaximumCharCount(32);


//convert to vector
//create position 2 button
UTIL_StringToVector(g_SoundscapePositions[7].Base(), buf);
vgui::Button* m_ButtonPos1 = new vgui::Button(this, "PosButton1", "Find Position 1", this, SETTINGS_PANEL_COMMAND_POS2);
m_ButtonPos1->SetBounds(240, 55, 100, 20);


//do command
//create position text 3
if (cc)
m_TextEntryPos2 = new vgui::TextEntry(this, "PosTextEntry0");
{
m_TextEntryPos2->SetEnabled(true);
CCommand args;
m_TextEntryPos2->SetText(pos2 ? pos2 : "0 0 0");
args.Tokenize(CFmtStr("ssmaker 7 %s 1", buf));
m_TextEntryPos2->SetBounds(5, 80, 230, 20);
cc->Dispatch(args);
m_TextEntryPos2->SetMaximumCharCount(32);
}


return;
//create position 1 button
}
vgui::Button* m_ButtonPos2 = new vgui::Button(this, "PosButton2", "Find Position 2", this, SETTINGS_PANEL_COMMAND_POS3);
m_ButtonPos2->SetBounds(240, 80, 100, 20);


}
// create position text 4
m_TextEntryPos3 = new vgui::TextEntry(this, "PosTextEntry3");
m_TextEntryPos3->SetEnabled(true);
m_TextEntryPos3->SetText(pos3 ? pos3 : "0 0 0");
m_TextEntryPos3->SetBounds(5, 105, 230, 20);
m_TextEntryPos3->SetMaximumCharCount(32);


//-----------------------------------------------------------------------------
// create position 4 button
// Purpose: Destructor
vgui::Button* m_ButtonPos3 = new vgui::Button(this, "PosButton3", "Find Position 3", this, SETTINGS_PANEL_COMMAND_POS4);
//-----------------------------------------------------------------------------
m_ButtonPos3->SetBounds(240, 105, 100, 20);
CSoundscapeSettingsPanel::~CSoundscapeSettingsPanel()
{
//save everything
KeyValues* settings = new KeyValues("settings");


//get text's
// create position text 5
char text0[64];
m_TextEntryPos4 = new vgui::TextEntry(this, "PosTextEntry4");
char text1[64];
m_TextEntryPos4->SetEnabled(true);
char text2[64];
m_TextEntryPos4->SetText(pos4 ? pos4 : "0 0 0");
char text3[64];
m_TextEntryPos4->SetBounds(5, 130, 230, 20);
char text4[64];
m_TextEntryPos4->SetMaximumCharCount(32);
char text5[64];
char text6[64];
char text7[64];


m_TextEntryPos0->GetText(text0, sizeof(text0));
// create position 5 button
m_TextEntryPos1->GetText(text1, sizeof(text1));
vgui::Button* m_ButtonPos4 = new vgui::Button(this, "PosButton4", "Find Position 4", this, SETTINGS_PANEL_COMMAND_POS5);
m_TextEntryPos2->GetText(text2, sizeof(text2));
m_ButtonPos4->SetBounds(240, 130, 100, 20);
m_TextEntryPos3->GetText(text3, sizeof(text3));
m_TextEntryPos4->GetText(text4, sizeof(text4));
m_TextEntryPos5->GetText(text5, sizeof(text5));
m_TextEntryPos6->GetText(text6, sizeof(text6));
m_TextEntryPos7->GetText(text7, sizeof(text7));


//save text entries
// create position text 6
settings->SetString("Position0", text0);
m_TextEntryPos5 = new vgui::TextEntry(this, "PosTextEntry5");
settings->SetString("Position1", text1);
m_TextEntryPos5->SetEnabled(true);
settings->SetString("Position2", text2);
m_TextEntryPos5->SetText(pos5 ? pos5 : "0 0 0");
settings->SetString("Position3", text3);
m_TextEntryPos5->SetBounds(5, 155, 230, 20);
settings->SetString("Position4", text4);
m_TextEntryPos5->SetMaximumCharCount(32);
settings->SetString("Position5", text5);
settings->SetString("Position6", text6);
settings->SetString("Position7", text7);


//save check buttons
// create position 6 button
settings->SetBool("ShowSoundscapes", m_ShowSoundscapePositions->IsSelected());
vgui::Button* m_ButtonPos5 = new vgui::Button(this, "PosButton5", "Find Position 5", this, SETTINGS_PANEL_COMMAND_POS6);
m_ButtonPos5->SetBounds(240, 155, 100, 20);


//save to file
// create position text 7
settings->SaveToFile(filesystem, "cfg/soundscape_maker.txt", "MOD");
m_TextEntryPos6 = new vgui::TextEntry(this, "PosTextEntry6");
settings->deleteThis();
m_TextEntryPos6->SetEnabled(true);
}
m_TextEntryPos6->SetText(pos6 ? pos6 : "0 0 0");
m_TextEntryPos6->SetBounds(5, 180, 230, 20);
m_TextEntryPos6->SetMaximumCharCount(32);


//static soundscape settings panel
// create position 7 button
static CSoundscapeSettingsPanel* g_SettingsPanel = nullptr;
vgui::Button* m_ButtonPos6 = new vgui::Button(this, "PosButton6", "Find Position 6", this, SETTINGS_PANEL_COMMAND_POS7);
m_ButtonPos6->SetBounds(240, 180, 100, 20);


// create position text 8
m_TextEntryPos7 = new vgui::TextEntry(this, "PosTextEntry7");
m_TextEntryPos7->SetEnabled(true);
m_TextEntryPos7->SetText(pos7 ? pos7 : "0 0 0");
m_TextEntryPos7->SetBounds(5, 205, 230, 20);
m_TextEntryPos7->SetMaximumCharCount(32);


//button
// create position 8 button
class CSoundscapeButton : public vgui::Button
vgui::Button* m_ButtonPos7 = new vgui::Button(this, "PosButton7", "Find Position 7", this, SETTINGS_PANEL_COMMAND_POS8);
{
m_ButtonPos7->SetBounds(240, 205, 100, 20);
public:
DECLARE_CLASS_SIMPLE(CSoundscapeButton, vgui::Button)


CSoundscapeButton(vgui::Panel* parent, const char* name, const char* text, vgui::Panel* target = nullptr, const char* command = nullptr)
// create show soundscape positions checkbox
: BaseClass(parent, name, text, target, command), m_bIsSelected(false)  
m_ShowSoundscapePositions = new vgui::CheckButton(this, "ShowCheckox", "Show Soundscape Positions");
{
m_ShowSoundscapePositions->SetBounds(75, 225, 200, 20);
m_ColorSelected = Color(200, 200, 200, 200);
m_ShowSoundscapePositions->SetCommand(SETTINGS_PANEL_COMMAND_SHOW);
m_FgColorSelected = Color(0, 0, 0, 255);
m_ShowSoundscapePositions->SetSelected(settings->GetBool("ShowSoundscapes", false));
}


//apply scheme settings
//set convar value
void ApplySchemeSettings(vgui::IScheme* scheme)
ConVar* cv = cvar->FindVar("__ss_draw");
{
if (cv)
BaseClass::ApplySchemeSettings(scheme);
cv->SetValue(m_ShowSoundscapePositions->IsSelected());


m_ColorNotSelected = GetButtonArmedBgColor();
//create divider
m_FgColorNotSelectedd = GetButtonArmedFgColor();
vgui::Divider* div = new vgui::Divider(this, "Divider");
}
div->SetBounds(-2, 247, SETTINGS_PANEL_WIDTH + 4, 2);


//paints the background
//create debug thing
void PaintBackground()
m_ShowSoundscapeDebug = new vgui::Button(this, "DebugInfo", "Show soundscape debug panel");
{
m_ShowSoundscapeDebug->SetBounds(20, 254, SETTINGS_PANEL_WIDTH - 40, 20);
if (m_bIsSelected)
m_ShowSoundscapeDebug->SetCommand(SETTINGS_PANEL_COMMAND_DEBUG);
SetBgColor(m_ColorSelected);
else
SetBgColor(m_ColorNotSelected);


BaseClass::PaintBackground();
//set server positions
}
ConCommand* cc = cvar->FindCommand("__ss_maker_set");
if (cc)
//paints
void Paint()
{
{
if (m_bIsSelected)
CCommand args;
SetFgColor(m_FgColorSelected);
else
SetFgColor(m_FgColorNotSelectedd);
BaseClass::Paint();
}


//is this selected or not
//do pos 0
bool m_bIsSelected;
if (pos0)
static Color m_ColorSelected;
{
static Color m_ColorNotSelected;
args.Tokenize(CFmtStr("ssmaker 0 %s 1", pos0));
static Color m_FgColorSelected;
cc->Dispatch(args);
static Color m_FgColorNotSelectedd;
};


Color CSoundscapeButton::m_ColorSelected = Color();
UTIL_StringToVector(g_SoundscapePositions[0].Base(), pos0);
Color CSoundscapeButton::m_ColorNotSelected = Color();
}
Color CSoundscapeButton::m_FgColorSelected = Color();
Color CSoundscapeButton::m_FgColorNotSelectedd = Color();


//do pos 1
if (pos1)
{
args.Tokenize(CFmtStr("ssmaker 1 %s 1", pos1));
cc->Dispatch(args);


//soundscape combo box
UTIL_StringToVector(g_SoundscapePositions[1].Base(), pos1);
}


class CSoundListComboBox : public vgui::ComboBox
//do pos 2
{
if (pos2)
public:
{
DECLARE_CLASS_SIMPLE(CSoundListComboBox, vgui::ComboBox);
args.Tokenize(CFmtStr("ssmaker 2 %s 1", pos2));
cc->Dispatch(args);


CSoundListComboBox(Panel* parent, const char* panelName, int numLines, bool allowEdit) :
UTIL_StringToVector(g_SoundscapePositions[2].Base(), pos2);
BaseClass(parent, panelName, numLines, allowEdit) {}
}


//on key typed. check for menu item with text inside it and if found then
//do pos 3
//select that item.
if (pos3)
void OnKeyTyped(wchar_t unichar)
{
{
args.Tokenize(CFmtStr("ssmaker 3 %s 1", pos3));
//check for ctrl or shift down
cc->Dispatch(args);
if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL) || unichar == '`')
return;


//open up this combo box
UTIL_StringToVector(g_SoundscapePositions[3].Base(), pos3);
if (unichar == 13)
{
ShowMenu();
return;
}
}


BaseClass::OnKeyTyped(unichar);
//do pos 4
if (pos4)
{
args.Tokenize(CFmtStr("ssmaker 4 %s 1", pos4));
cc->Dispatch(args);


//check for backspace
UTIL_StringToVector(g_SoundscapePositions[4].Base(), pos4);
if (unichar == 8 || unichar == '_')
}
return;


//get text
//do pos 5
char buf[512];
if (pos5)
GetText(buf, sizeof(buf));
{
args.Tokenize(CFmtStr("ssmaker 5 %s 1", pos5));
cc->Dispatch(args);


//start from current index + 1
UTIL_StringToVector(g_SoundscapePositions[5].Base(), pos5);
int start = GetMenu()->GetActiveItem() + 1;
}


//look for sound with same name starting from the start first
//do pos 6
for (int i = start; i < g_SoundDirectories.Count(); i++)
if (pos6)
{
{
if (Q_stristr(g_SoundDirectories[i], buf))
args.Tokenize(CFmtStr("ssmaker 6 %s 1", pos6));
{
cc->Dispatch(args);
GetMenu()->SetCurrentlyHighlightedItem(i);
 
return;
UTIL_StringToVector(g_SoundscapePositions[6].Base(), pos6);
}
}
}
 
//now cheeck from 0 to the start
//do pos 7
for (int i = 0; i < start; i++)
if (pos7)
{
{
if (Q_stristr(g_SoundDirectories[i], buf))
args.Tokenize(CFmtStr("ssmaker 7 %s", pos7));
{
cc->Dispatch(args);
GetMenu()->SetCurrentlyHighlightedItem(i);
 
return;
UTIL_StringToVector(g_SoundscapePositions[7].Base(), pos7);
}
}
}
}
}
};


//delete settings
settings->deleteThis();
}


//sounds list panel
//-----------------------------------------------------------------------------
 
// Purpose: Called on command
#define SOUND_LIST_PANEL_WIDTH 375
//-----------------------------------------------------------------------------
#define SOUND_LIST_PANEL_HEIGHT 255
void CSoundscapeSettingsPanel::OnCommand(const char* pszCommand)
#define SOUND_LIST_PLAY_COMMAND "PlaySound"
#define SOUND_LIST_STOP_COMMAND "StopSound"
#define SOUND_LIST_INSERT_COMMAND "Insert"
#define SOUND_LIST_RELOAD_COMMAND "Reload"
#define SOUND_LIST_SEARCH_COMMAND "Search"
 
class CSoundListPanel : public vgui::Frame
{
{
public:
if (Q_strstr(pszCommand, "GetPos") == pszCommand)
DECLARE_CLASS_SIMPLE(CSoundListPanel, vgui::Frame);
{
//search for number
pszCommand = pszCommand + 6;


CSoundListPanel(vgui::VPANEL parent, const char* name);
//execute command
static ConCommand* cc = cvar->FindCommand("__ss_maker_start");
if (cc)
{
//hide everything first
g_SoundscapeMaker->SetAllVisible(false);


//initalizes sound combo box
CCommand args;
void InitalizeSounds();
args.Tokenize(CFmtStr("ssmaker %d", atoi(pszCommand)));
void InitalizeSoundscapes();
cc->Dispatch(args);
}


//sets if this is currently using the soundscape panel or sound panel
return;
void SetIsUsingSoundPanel(bool bUsing);
}


//other
else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_SHOW))
void OnCommand(const char* pszCommand);
{
void OnClose();
static ConVar* cv = cvar->FindVar("__ss_draw");
if (cv)
cv->SetValue(m_ShowSoundscapePositions->IsSelected());


private:
return;
friend class CSoundscapeMaker;
}


//are we currently in the 'sound' panel or 'soundscape' panel
//handle debug thing
bool bCurrentlyInSoundPanel = true;
else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_DEBUG))
{
g_SoundscapeDebugPanel->SetVisible(true);
g_SoundscapeDebugPanel->RequestFocus();
g_SoundscapeDebugPanel->MoveToFront();
return;
}


CSoundListComboBox* m_SoundsList; //for sounds
BaseClass::OnCommand(pszCommand);
CSoundListComboBox* m_SoundscapesList; //for soundscapes
}
vgui::TextEntry* m_SearchText;
vgui::Button* m_SearchButton;
vgui::Button* m_PlayButton;
vgui::Button* m_StopSoundButton;
vgui::Button* m_InsertButton;
vgui::Button* m_ReloadSounds;
 
//current sound guid
int m_iSongGuid = -1;
};


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Purpose: Constructor
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CSoundListPanel::CSoundListPanel(vgui::VPANEL parent, const char* name)
void CSoundscapeSettingsPanel::SetItem(int index, const Vector& value)
: BaseClass(nullptr, name)
{
{
SetParent(parent);
const char* text = CFmtStr("%.3f %.3f %.3f", value.x, value.y, value.z);


SetKeyBoardInputEnabled(true);
//check index
SetMouseInputEnabled(true);
switch (index)
{
case 0:
m_TextEntryPos0->RequestFocus();
m_TextEntryPos0->SetText(text);
g_SoundscapePositions[0] = value;
break;


SetProportional(false);
case 1:
SetTitleBarVisible(true);
m_TextEntryPos1->RequestFocus();
SetMinimizeButtonVisible(false);
m_TextEntryPos1->SetText(text);
SetMaximizeButtonVisible(false);
g_SoundscapePositions[1] = value;
SetCloseButtonVisible(true);
break;
SetSizeable(false);
SetMoveable(true);
SetVisible(false);


//set the size and pos
case 2:
int ScreenWide, ScreenTall;
m_TextEntryPos2->RequestFocus();
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
m_TextEntryPos2->SetText(text);
g_SoundscapePositions[2] = value;
break;
case 3:
m_TextEntryPos3->RequestFocus();
m_TextEntryPos3->SetText(text);
g_SoundscapePositions[3] = value;
break;


SetTitle("Sounds List", true);
case 4:
SetSize(SOUND_LIST_PANEL_WIDTH, SOUND_LIST_PANEL_HEIGHT);
m_TextEntryPos4->RequestFocus();
SetPos((ScreenWide - SOUND_LIST_PANEL_WIDTH) / 2, (ScreenTall - SOUND_LIST_PANEL_HEIGHT) / 2);
m_TextEntryPos4->SetText(text);
g_SoundscapePositions[4] = value;
break;


//create combo box's
case 5:
m_SoundsList = new CSoundListComboBox(this, "SoundsList", 20, true);
m_TextEntryPos5->RequestFocus();
m_SoundsList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_TextEntryPos5->SetText(text);
m_SoundsList->AddActionSignalTarget(this);
g_SoundscapePositions[5] = value;
m_SoundsList->SetVisible(true);
break;
m_SoundscapesList = new CSoundListComboBox(this, "SoundscapesList", 20, true);
m_SoundscapesList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_SoundscapesList->AddActionSignalTarget(this);
m_SoundscapesList->SetVisible(false);
//make divider
vgui::Divider* divider1 = new vgui::Divider(this, "Divider");
divider1->SetBounds(-5, 48, SOUND_LIST_PANEL_WIDTH + 10, 2);


//create text
case 6:
vgui::Label* label1 = new vgui::Label(this, "FindSound", "Find Sound");
m_TextEntryPos6->RequestFocus();
label1->SetBounds(147, 51, 120, 20);
m_TextEntryPos6->SetText(text);
g_SoundscapePositions[6] = value;
break;


//create text entry
case 7:
m_SearchText = new vgui::TextEntry(this, "SearchTextEntry");
m_TextEntryPos7->RequestFocus();
m_SearchText->SetBounds(5, 75, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_TextEntryPos7->SetText(text);
m_SearchText->SetEnabled(true);
g_SoundscapePositions[7] = value;
m_SearchText->SetText("");
break;
}
}


//create search for button
//-----------------------------------------------------------------------------
m_SearchButton = new vgui::Button(this, "SearchButton", "Search For");
// Purpose: Called on text changed
m_SearchButton->SetBounds(5, 100, SOUND_LIST_PANEL_WIDTH - 15, 20);;
//-----------------------------------------------------------------------------
m_SearchButton->SetEnabled(true);
void CSoundscapeSettingsPanel::OnTextChanged(KeyValues* kv)
m_SearchButton->SetCommand(SOUND_LIST_SEARCH_COMMAND);
{
static ConCommand* cc = cvar->FindCommand("__ss_maker_set");


//make divider
//check focus
vgui::Divider* divider2 = new vgui::Divider(this, "Divider");
if (m_TextEntryPos0->HasFocus())
divider2->SetBounds(-5, 124, SOUND_LIST_PANEL_WIDTH + 10, 2);
{
//get text
char buf[512];
m_TextEntryPos0->GetText(buf, sizeof(buf));


//create text
//convert to vector
vgui::Label* label2 = new vgui::Label(this, "SoundButtons", "Sound Buttons");
UTIL_StringToVector(g_SoundscapePositions[0].Base(), buf);
label2->SetBounds(140, 127, 120, 20);


//create play button
//do command
m_PlayButton = new vgui::Button(this, "PlayButton", "Play Sound", this);
if (cc)
m_PlayButton ->SetBounds(5, 150, SOUND_LIST_PANEL_WIDTH - 15, 20);
{
m_PlayButton->SetCommand(SOUND_LIST_PLAY_COMMAND);
CCommand args;
args.Tokenize(CFmtStr("ssmaker 0 %s 1", buf));
cc->Dispatch(args);
}


//create stop sound button
return;
m_StopSoundButton = new vgui::Button(this, "StopSound", "Stop Sound", this);
}
m_StopSoundButton ->SetBounds(5, 175, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_StopSoundButton->SetCommand(SOUND_LIST_STOP_COMMAND);


//create sound insert button
//check focus
m_InsertButton = new vgui::Button(this, "InsertSound", "Insert Sound", this);
if (m_TextEntryPos1->HasFocus())
m_InsertButton ->SetBounds(5, 200, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_InsertButton->SetCommand(SOUND_LIST_INSERT_COMMAND);
 
//create reload sounds button
m_ReloadSounds = new vgui::Button(this, "ReloadSounds", "Reload Sounds", this);
m_ReloadSounds ->SetBounds(5, 225, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_ReloadSounds->SetCommand(SOUND_LIST_RELOAD_COMMAND);
}
 
//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundListPanel::OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, SOUND_LIST_SEARCH_COMMAND))
{
{
//get text
//get text
char buf[512];
char buf[512];
m_SearchText->GetText(buf, sizeof(buf));
m_TextEntryPos1->GetText(buf, sizeof(buf));


//check for shift key
//convert to vector
bool shift = (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT));
UTIL_StringToVector(g_SoundscapePositions[1].Base(), buf);
//vector of texts
CUtlVector<char*> SoundNames;
CSoundListComboBox* SoundList = bCurrentlyInSoundPanel ? m_SoundsList : m_SoundscapesList;


//if we are in soundscape mode then set the SoundNames to all the soundscapes. else set SoundNames to g_SoundDirectories
//do command
if (!bCurrentlyInSoundPanel)
if (cc)
{
{
for (int i = 0; i < m_SoundscapesList->GetItemCount(); i++)
CCommand args;
{
args.Tokenize(CFmtStr("ssmaker 1 %s 1", buf));
//insert
cc->Dispatch(args);
char* tmpbuf = new char[512];
}
m_SoundscapesList->GetItemText(i, tmpbuf, 512);
 
return;
}
 
//check focus
if (m_TextEntryPos2->HasFocus())
{
//get text
char buf[512];
m_TextEntryPos2->GetText(buf, sizeof(buf));
 
//convert to vector
UTIL_StringToVector(g_SoundscapePositions[2].Base(), buf);


SoundNames.AddToTail(tmpbuf);
//do command
}
if (cc)
}
else
{
{
SoundNames = g_SoundDirectories;
CCommand args;
args.Tokenize(CFmtStr("ssmaker 2 %s 1", buf));
cc->Dispatch(args);
}
}


if (shift)
return;
{
}
//start from current index - 1
int start = SoundList->GetMenu()->GetActiveItem() - 1;


//look for sound with same name starting from the start first and going down
//check focus
for (int i = start; i >= 0; i--)
if (m_TextEntryPos3->HasFocus())
{
{
if (Q_stristr(SoundNames[i], buf))
//get text
{
char buf[512];
//select item
m_TextEntryPos3->GetText(buf, sizeof(buf));
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


//set text
//convert to vector
SoundList->SetText(SoundNames[i]);
UTIL_StringToVector(g_SoundscapePositions[3].Base(), buf);
//delete all soundscapes if we need to
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
delete[] SoundNames[i];


return;
//do command
}
if (cc)
}
{
CCommand args;
args.Tokenize(CFmtStr("ssmaker 3 %s 1", buf));
cc->Dispatch(args);
}


return;
}


//now cheeck from the SoundNames to the start
//check focus
for (int i = SoundNames.Count() - 1; i > start; i--)
if (m_TextEntryPos4->HasFocus())
{
{
if (Q_stristr(SoundNames[i], buf))
//get text
{
char buf[512];
//select item
m_TextEntryPos4->GetText(buf, sizeof(buf));
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


//set text
//convert to vector
SoundList->SetText(SoundNames[i]);
UTIL_StringToVector(g_SoundscapePositions[4].Base(), buf);


//delete all soundscapes if we need to
//do command
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
if (cc)
delete[] SoundNames[i];
{
 
CCommand args;
return;
args.Tokenize(CFmtStr("ssmaker 4 %s 1", buf));
}
cc->Dispatch(args);
}
}
}
else
{
//start from current index + 1
int start = SoundList->GetMenu()->GetActiveItem() + 1;


//look for sound with same name starting from the start first
return;
for (int i = start; i < SoundNames.Count(); i++)
}
{
if (Q_stristr(SoundNames[i], buf))
{
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);
//set text
SoundList->SetText(SoundNames[i]);


//delete all soundscapes if we need to
//check focus
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
if (m_TextEntryPos5->HasFocus())
delete[] SoundNames[i];
{
//get text
char buf[512];
m_TextEntryPos5->GetText(buf, sizeof(buf));


return;
//convert to vector
}
UTIL_StringToVector(g_SoundscapePositions[5].Base(), buf);
}


 
//do command
//now cheeck from 0 to the start
if (cc)
for (int i = 0; i < start; i++)
{
{
CCommand args;
if (Q_stristr(SoundNames[i], buf))
args.Tokenize(CFmtStr("ssmaker 5 %s 1", buf));
{
cc->Dispatch(args);
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);
 
//set text
SoundList->SetText(SoundNames[i]);
 
//delete all soundscapes if we need to
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
delete[] SoundNames[i];
 
return;
}
}
}
}
//delete all soundscapes if we need to
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
delete[] SoundNames[i];


return;
return;
}
}
else if (!Q_strcmp(pszCommand, SOUND_LIST_PLAY_COMMAND))  
 
//check focus
if (m_TextEntryPos6->HasFocus())
{
{
//get the sound
//get text
char buf[512];
char buf[512];
m_SoundsList->GetText(buf, sizeof(buf));
m_TextEntryPos6->GetText(buf, sizeof(buf));
 
//convert to vector
UTIL_StringToVector(g_SoundscapePositions[6].Base(), buf);


//stop the sound
//do command
if (enginesound->IsSoundStillPlaying(m_iSongGuid))
if (cc)
{
{
enginesound->StopSoundByGuid(m_iSongGuid);
CCommand args;
m_iSongGuid = -1;
args.Tokenize(CFmtStr("ssmaker 6 %s 1", buf));
cc->Dispatch(args);
}
}


//precache and play the sound
if (!enginesound->IsSoundPrecached(buf))
enginesound->PrecacheSound(buf);
enginesound->EmitAmbientSound(buf, 1, 100);
m_iSongGuid = enginesound->GetGuidForLastSoundEmitted();
return;
return;
}
}
else if (!Q_strcmp(pszCommand, SOUND_LIST_STOP_COMMAND))  
 
//check focus
if (m_TextEntryPos7->HasFocus())
{
{
//stop the sound
//get text
if (m_iSongGuid != -1 && enginesound->IsSoundStillPlaying(m_iSongGuid))
char buf[512];
m_TextEntryPos7->GetText(buf, sizeof(buf));
 
//convert to vector
UTIL_StringToVector(g_SoundscapePositions[7].Base(), buf);
 
//do command
if (cc)
{
{
enginesound->StopSoundByGuid(m_iSongGuid);
CCommand args;
m_iSongGuid = -1;
args.Tokenize(CFmtStr("ssmaker 7 %s 1", buf));
cc->Dispatch(args);
}
}


return;
return;
}
}
else if (!Q_strcmp(pszCommand, SOUND_LIST_INSERT_COMMAND))
{
//make not visible
SetVisible(false);


//stop the sound
}
if (enginesound->IsSoundStillPlaying(m_iSongGuid))
{
enginesound->StopSoundByGuid(m_iSongGuid);
m_iSongGuid = -1;
}


//get the sound
//-----------------------------------------------------------------------------
char buf[512];
// Purpose: Destructor
m_SoundsList->GetText(buf, sizeof(buf));
//-----------------------------------------------------------------------------
CSoundscapeSettingsPanel::~CSoundscapeSettingsPanel()
{
//save everything
KeyValues* settings = new KeyValues("settings");


//set the sound text
//get text's
g_SoundscapeMaker->SetSoundText(buf);
char text0[64];
return;
char text1[64];
}
char text2[64];
else if (!Q_strcmp(pszCommand, SOUND_LIST_RELOAD_COMMAND))
char text3[64];
{
char text4[64];
if (bCurrentlyInSoundPanel)
char text5[64];
{
char text6[64];
//clear everything for the combo box and reload it
char text7[64];
m_SoundsList->RemoveAll();
InitalizeSounds();
}
else
{
//clear everything for the combo box and reload it
m_SoundscapesList->RemoveAll();


bool bPrev = g_bSSMHack;
m_TextEntryPos0->GetText(text0, sizeof(text0));
g_bSSMHack = true;
m_TextEntryPos1->GetText(text1, sizeof(text1));
m_TextEntryPos2->GetText(text2, sizeof(text2));
m_TextEntryPos3->GetText(text3, sizeof(text3));
m_TextEntryPos4->GetText(text4, sizeof(text4));
m_TextEntryPos5->GetText(text5, sizeof(text5));
m_TextEntryPos6->GetText(text6, sizeof(text6));
m_TextEntryPos7->GetText(text7, sizeof(text7));


//reload all the soundscape files
//save text entries
enginesound->StopAllSounds(true);
settings->SetString("Position0", text0);
 
settings->SetString("Position1", text1);
g_SoundscapeSystem.StartNewSoundscape(nullptr);
settings->SetString("Position2", text2);
g_SoundscapeSystem.RemoveAll();
settings->SetString("Position3", text3);
g_SoundscapeSystem . Init();
settings->SetString("Position4", text4);
settings->SetString("Position5", text5);
settings->SetString("Position6", text6);
settings->SetString("Position7", text7);


g_bSSMHack = bPrev;
//save check buttons
settings->SetBool("ShowSoundscapes", m_ShowSoundscapePositions->IsSelected());


InitalizeSoundscapes();
//save to file
}
settings->SaveToFile(filesystem, "cfg/soundscape_maker.txt", "MOD");
settings->deleteThis();
}


return;
//static soundscape settings panel
}
static CSoundscapeSettingsPanel* g_SettingsPanel = nullptr;


BaseClass::OnCommand(pszCommand);
}


//-----------------------------------------------------------------------------
#define BUTTON_MENU_COMMAND_COPY_CLIPBOARD "CopyClipboard"
// Purpose: Called on panel close
//-----------------------------------------------------------------------------
void CSoundListPanel::OnClose()
{
OnCommand(SOUND_LIST_STOP_COMMAND);
BaseClass::OnClose();
}


//-----------------------------------------------------------------------------
//button
// Purpose: Initalizes the sounds list
class CSoundscapeButton : public vgui::Button
//-----------------------------------------------------------------------------
void CSoundListPanel::InitalizeSounds()
{
{
//get the sound array
public:
GetSoundNames();
DECLARE_CLASS_SIMPLE(CSoundscapeButton, vgui::Button)


//add all the sounds
CSoundscapeButton(vgui::Panel* parent, const char* name, const char* text, vgui::Panel* target = nullptr, const char* command = nullptr, KeyValues* kv = nullptr, SoundscapeClipboardType type = SoundscapeClipboardType::Type_SoundscapeNone)
for (int i = 0; i < g_SoundDirectories.Size(); i++)
: BaseClass(parent, name, text, target, command), m_bIsSelected(false), m_KeyValues(kv), m_KeyValuesType(type)
m_SoundsList->AddItem(g_SoundDirectories[i], nullptr);
{
m_ColorSelected = Color(200, 200, 200, 200);
m_FgColorSelected = Color(0, 0, 0, 255);
}


m_SoundsList->ActivateItem(0);
//apply scheme settings
}
void ApplySchemeSettings(vgui::IScheme* scheme)
{
BaseClass::ApplySchemeSettings(scheme);


//-----------------------------------------------------------------------------
m_ColorNotSelected = GetButtonArmedBgColor();
// Purpose: Initalizes the soundscape list
m_FgColorNotSelected = GetButtonArmedFgColor();
//-----------------------------------------------------------------------------
}
void CSoundListPanel::InitalizeSoundscapes()
{
//add all the soundscapes
for (int i = 0; i < g_SoundscapeSystem.m_soundscapes.Count(); i++)
m_SoundscapesList->AddItem(g_SoundscapeSystem.m_soundscapes[i]->GetName(), nullptr);


m_SoundscapesList->ActivateItem(0);
//paints the background
}
void PaintBackground()
{
if (m_bIsSelected)
SetBgColor(m_ColorSelected);
else
SetBgColor(m_ColorNotSelected);


//-----------------------------------------------------------------------------
BaseClass::PaintBackground();
// Purpose: Sets if this panel is currently the sound panel or soundscape
}
// selector panel.
//-----------------------------------------------------------------------------
void CSoundListPanel::SetIsUsingSoundPanel(bool bUsing)
{
bCurrentlyInSoundPanel = bUsing;


//disable stuff
//paints
if (bUsing)
void Paint()
{
{
//set 'reload' text
if (m_bIsSelected)
m_ReloadSounds->SetText("Reload Sounds");
SetFgColor(m_FgColorSelected);
else
SetFgColor(m_FgColorNotSelected);


m_SoundscapesList->SetVisible(false);
BaseClass::Paint();
m_SoundsList->SetVisible(true);
}


//enable the play button
//mouse release
m_PlayButton->SetEnabled(true);
void OnMouseReleased(vgui::MouseCode code)
m_StopSoundButton->SetEnabled(true);
{
}
if (code != vgui::MouseCode::MOUSE_RIGHT)
else
return BaseClass::OnMouseReleased(code);
{
//set 'reload' text
m_ReloadSounds->SetText("Reload Soundscapes");


m_SoundscapesList->SetVisible(true);
//this should never happen but just in case
m_SoundsList->SetVisible(false);
if (!m_KeyValues)
return;


//disable the play button
//get cursor pos
m_PlayButton->SetEnabled(false);
int x, y;
m_StopSoundButton->SetEnabled(false);
vgui::surface()->SurfaceGetCursorPos(x, y);
}
}


//static sound list instance
//show menu
static CSoundListPanel* g_SoundPanel = nullptr;
vgui::Menu* menu = new vgui::Menu(this, "Clipboard");
menu->AddMenuItem("CopyToClipboard", "Copy", BUTTON_MENU_COMMAND_COPY_CLIPBOARD, this);
menu->SetBounds(x, y, 200, 50);
menu->SetVisible(true);


BaseClass::Paint();
}


//soundscape list
//mouse release
void OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, BUTTON_MENU_COMMAND_COPY_CLIPBOARD))
{
//create copy of keyvalues
switch (m_KeyValuesType)
{
case SoundscapeClipboardType::Type_SoundscapeName:
{
//copy
if (CurrClipboardName.Count() >= MAX_CLIPBOARD_ITEMS)
{
CurrClipboardName[0]->deleteThis();
CurrClipboardName.Remove(0);
}


CurrClipboardName.AddToTail(m_KeyValues->MakeCopy());


#define ADD_SOUNDSCAPE_COMMAND "AddSoundscape"
//debug message
SoundscapePrint(Color(255, 255, 255, 255), "Soundscape: '%s' Coppied to clipboard.\n", m_KeyValues->GetName());
break;
}
case SoundscapeClipboardType::Type_SoundscapeData:
{
//copy
if (CurrClipboardData.Count() >= MAX_CLIPBOARD_ITEMS)
{
CurrClipboardData[0]->deleteThis();
CurrClipboardData.Remove(0);
}


//make copy
CurrClipboardData.AddToTail(m_KeyValues->MakeCopy());


//soundscape list class
//debug message
class CSoundscapeList : public vgui::Divider
SoundscapePrint(Color(255, 255, 255, 255), "Soundscape Data: '%s' Coppied to clipboard.\n", m_KeyValues->GetName());
{
break;
public:
}
DECLARE_CLASS_SIMPLE(CSoundscapeList, vgui::Divider);
case SoundscapeClipboardType::Type_SoundscapeRandomWave:
{
//copy
if (CurrClipboardRandom.Count() >= MAX_CLIPBOARD_ITEMS)
{
CurrClipboardRandom[0]->deleteThis();
CurrClipboardRandom.Remove(0);
}


//constructor
CurrClipboardRandom.AddToTail(m_KeyValues->MakeCopy());
CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height);


//menu item stuff
//debug message
virtual void AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent);
SoundscapePrint(Color(255, 255, 255, 255), "Soundscape Random Wave: '%s' Coppied to clipboard.\n", m_KeyValues->GetString());
virtual void Clear();
break;
}
}
}
}


//other
//is this selected or not
virtual void OnMouseWheeled(int delta);  
bool m_bIsSelected;
virtual void OnMouseReleased(vgui::MouseCode code);
static Color m_ColorSelected;
static Color m_ColorNotSelected;
static Color m_FgColorSelected;
static Color m_FgColorNotSelected;


virtual void OnCommand(const char* pszCommand);
KeyValues* m_KeyValues = nullptr;
virtual void PaintBackground();
SoundscapeClipboardType m_KeyValuesType;
};


virtual void OnKeyCodePressed(vgui::KeyCode code);
Color CSoundscapeButton::m_ColorSelected = Color();
Color CSoundscapeButton::m_ColorNotSelected = Color();
Color CSoundscapeButton::m_FgColorSelected = Color();
Color CSoundscapeButton::m_FgColorNotSelected = Color();


//message funcs
MESSAGE_FUNC_INT(ScrollBarMoved, "ScrollBarSliderMoved", position);


protected:
//soundscape combo box
friend class CSoundscapeMaker;


//keyvalue list.
class CSoundListComboBox : public vgui::ComboBox
KeyValues* m_Keyvalues = nullptr;
{
public:
DECLARE_CLASS_SIMPLE(CSoundListComboBox, vgui::ComboBox);


//says "Soundscapes List"
CSoundListComboBox(Panel* parent, const char* panelName, int numLines, bool allowEdit) :
vgui::Label* m_pLabel;
BaseClass(parent, panelName, numLines, allowEdit) {}
vgui::ScrollBar* m_pSideSlider;


//menu
//on key typed. check for menu item with text inside it and if found then
vgui::Menu* menu;
//select that item.
void OnKeyTyped(wchar_t unichar)
{
//check for ctrl or shift down
if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL) || unichar == '`')
return;


//menu button stuff
//open up this combo box
CUtlVector<CSoundscapeButton*> m_MenuButtons;
if (unichar == 13)
int m_iCurrentY;
{
int m_iMax;
ShowMenu();
int m_AmtAdded;
return;
};
}


//-----------------------------------------------------------------------------
BaseClass::OnKeyTyped(unichar);
// Purpose: Constructor for soundscape list panel
//-----------------------------------------------------------------------------
CSoundscapeList::CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: BaseClass(parent, name)
{
//create the text
m_pLabel = new vgui::Label(this, "ListsText", text);
m_pLabel->SetVisible(true);
m_pLabel->SetBounds(text_x_pos, 2, 150, 20);


//create the side slider
//check for backspace
m_pSideSlider = new vgui::ScrollBar(this, "ListsSlider", true);
if (unichar == 8 || unichar == '_')
m_pSideSlider->SetBounds(width - 20, 0, 20, height - 2);
return;
m_pSideSlider->SetValue(0);
m_pSideSlider->SetEnabled(false);
m_pSideSlider->SetRange(0, 0);
m_pSideSlider->SetButtonPressedScrollValue(1);
m_pSideSlider->SetRangeWindow(0);
m_pSideSlider->AddActionSignalTarget(this);


m_iCurrentY = 22;
//get text
m_iMax = max;
char buf[512];
m_Keyvalues = nullptr;
GetText(buf, sizeof(buf));
}


//-----------------------------------------------------------------------------
//start from current index + 1
// Purpose: adds a button to the soundscape list
int start = GetMenu()->GetActiveItem() + 1;
//-----------------------------------------------------------------------------
void CSoundscapeList::AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent)
{
//create a new button
CSoundscapeButton* button = new CSoundscapeButton(this, name, text, parent, command);
button->SetBounds(5, m_iCurrentY, GetWide() - 30, 20);


//increment current y
//look for sound with same name starting from the start first
m_iCurrentY = m_iCurrentY + 22;
for (int i = start; i < g_SoundDirectories.Count(); i++)
{
if (Q_stristr(g_SoundDirectories[i], buf))
{
GetMenu()->SetCurrentlyHighlightedItem(i);
return;
}
}


//add button to array
//now cheeck from 0 to the start
m_MenuButtons.AddToTail(button);
for (int i = 0; i < start; i++)
 
{
//if the count is more then m_iMax then set slider value
if (Q_stristr(g_SoundDirectories[i], buf))
if (m_MenuButtons.Count() > m_iMax)
{
{
GetMenu()->SetCurrentlyHighlightedItem(i);
int max = m_MenuButtons.Count() - m_iMax;
return;
 
}
m_pSideSlider->SetRange(0, max);
}
m_pSideSlider->SetRangeWindow(1);
m_pSideSlider->SetEnabled(true);
}
}
};


m_AmtAdded++;


//check to see if we need to scroll down
//sounds list panel
if (m_MenuButtons.Count() >= m_iMax)
OnMouseWheeled(-1);
}


//-----------------------------------------------------------------------------
#define SOUND_LIST_PANEL_WIDTH 375
// Purpose: Clears everything for this list
#define SOUND_LIST_PANEL_HEIGHT 255
//-----------------------------------------------------------------------------
#define SOUND_LIST_PLAY_COMMAND "PlaySound"
void CSoundscapeList::Clear()
#define SOUND_LIST_STOP_COMMAND "StopSound"
#define SOUND_LIST_INSERT_COMMAND "Insert"
#define SOUND_LIST_RELOAD_COMMAND "Reload"
#define SOUND_LIST_SEARCH_COMMAND "Search"
 
class CSoundListPanel : public vgui::Frame
{
{
//reset the slider
public:
m_pSideSlider->SetValue(0);
DECLARE_CLASS_SIMPLE(CSoundListPanel, vgui::Frame);
m_pSideSlider->SetEnabled(false);
m_pSideSlider->SetRange(0, 0);
m_pSideSlider->SetButtonPressedScrollValue(1);
m_pSideSlider->SetRangeWindow(0);


//delete and clear the buttons
CSoundListPanel(vgui::VPANEL parent, const char* name);
for (int i = 0; i < m_MenuButtons.Count(); i++)
m_MenuButtons[i]->DeletePanel();


m_MenuButtons.RemoveAll();
//initalizes sound combo box
void InitalizeSounds();
void InitalizeSoundscapes(CUtlVector<const char*>& OtherSoundscapes);


//reset current y
//sets if this is currently using the soundscape panel or sound panel
m_iCurrentY = 22;
void SetIsUsingSoundPanel(bool bUsing);


m_AmtAdded = 0;
//other
}
void OnCommand(const char* pszCommand);
void OnClose();


//-----------------------------------------------------------------------------
private:
// Purpose: Called when a mouse is wheeled
friend class CSoundscapeMaker;
//-----------------------------------------------------------------------------
void CSoundscapeList::OnMouseWheeled(int delta)
{
//check for scroll down
if (delta == -1)
m_pSideSlider->SetValue(m_pSideSlider->GetValue() + 1);


//check for scroll up
//are we currently in the 'sound' panel or 'soundscape' panel
else if (delta == 1)
bool bCurrentlyInSoundPanel = true;
m_pSideSlider->SetValue(m_pSideSlider->GetValue() - 1);
}


//-----------------------------------------------------------------------------
CSoundListComboBox* m_SoundsList; //for sounds
// Purpose: Called when a mouse code is released
CSoundListComboBox* m_SoundscapesList; //for soundscapes
//-----------------------------------------------------------------------------
vgui::TextEntry* m_SearchText;
void CSoundscapeList::OnMouseReleased(vgui::MouseCode code)
vgui::Button* m_SearchButton;
{
vgui::Button* m_PlayButton;
if (code != vgui::MouseCode::MOUSE_RIGHT)
vgui::Button* m_StopSoundButton;
return;
vgui::Button* m_InsertButton;
vgui::Button* m_ReloadSounds;


//get cursor pos
//current sound guid
int x, y;
int m_iSongGuid = -1;
vgui::surface()->SurfaceGetCursorPos(x, y);
};
 
//create menu
menu = new vgui::Menu(this, "Menu");
menu->AddMenuItem("AddSoundscape", "Add Soundscape", ADD_SOUNDSCAPE_COMMAND, this);
menu->SetBounds(x, y, 200, 50);
menu->SetVisible(true);
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called on command
// Purpose: Constructor
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeList::OnCommand(const char* pszCommand)
CSoundListPanel::CSoundListPanel(vgui::VPANEL parent, const char* name)
: BaseClass(nullptr, name)
{
{
if (!Q_strcmp(pszCommand, ADD_SOUNDSCAPE_COMMAND))
SetParent(parent);
{
const char* name = CFmtStr("New Soundscape %d", m_AmtAdded);
AddButton(name, name, name, GetParent());


//add to keyvalues file
SetKeyBoardInputEnabled(true);
KeyValues* kv = new KeyValues(name);
SetMouseInputEnabled(true);
KeyValues* tmp = m_Keyvalues;
 
KeyValues* tmp2 = tmp;
SetProportional(false);
SetTitleBarVisible(true);
SetMinimizeButtonVisible(false);
SetMaximizeButtonVisible(false);
SetCloseButtonVisible(true);
SetSizeable(false);
SetMoveable(true);
SetVisible(false);


//get last subkey
//set the size and pos
while (tmp != nullptr)
int ScreenWide, ScreenTall;
{
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
tmp2 = tmp;
tmp = tmp->GetNextTrueSubKey();
}


//add to last subkey
SetTitle("Sounds List", true);
tmp2->SetNextKey(kv);
SetSize(SOUND_LIST_PANEL_WIDTH, SOUND_LIST_PANEL_HEIGHT);
SetPos((ScreenWide - SOUND_LIST_PANEL_WIDTH) / 2, (ScreenTall - SOUND_LIST_PANEL_HEIGHT) / 2);


GetParent()->OnCommand(name);
//create combo box's
return;
m_SoundsList = new CSoundListComboBox(this, "SoundsList", 20, true);
}
m_SoundsList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_SoundsList->AddActionSignalTarget(this);
m_SoundsList->SetVisible(true);


BaseClass::OnCommand(pszCommand);
m_SoundscapesList = new CSoundListComboBox(this, "SoundscapesList", 20, true);
}
m_SoundscapesList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_SoundscapesList->AddActionSignalTarget(this);
m_SoundscapesList->SetVisible(false);


//-----------------------------------------------------------------------------
//make divider
// Purpose: Paints the background
vgui::Divider* divider1 = new vgui::Divider(this, "Divider");
//-----------------------------------------------------------------------------
divider1->SetBounds(-5, 48, SOUND_LIST_PANEL_WIDTH + 10, 2);
void CSoundscapeList::PaintBackground()
{
//colors
static Color EnabledColor = Color(100, 100, 100, 200);
static Color DisabledColor = Color(60, 60, 60, 200);


//if m_KeyValues then paint the default color
//create text
if (m_Keyvalues)
vgui::Label* label1 = new vgui::Label(this, "FindSound", "Find Sound");
SetBgColor(EnabledColor);
label1->SetBounds(147, 51, 120, 20);
else
SetBgColor(DisabledColor);


BaseClass::PaintBackground();
//create text entry
}
m_SearchText = new vgui::TextEntry(this, "SearchTextEntry");
m_SearchText->SetBounds(5, 75, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_SearchText->SetEnabled(true);
m_SearchText->SetText("");


//-----------------------------------------------------------------------------
//create search for button
// Purpose: Called on keyboard code pressed
m_SearchButton = new vgui::Button(this, "SearchButton", "Search For");
//-----------------------------------------------------------------------------
m_SearchButton->SetBounds(5, 100, SOUND_LIST_PANEL_WIDTH - 15, 20);;
void CSoundscapeList::OnKeyCodePressed(vgui::KeyCode code)
m_SearchButton->SetEnabled(true);
{
m_SearchButton->SetCommand(SOUND_LIST_SEARCH_COMMAND);
//check for arrow
if (code == KEY_UP)
{
//find selected item
for (int i = 0; i < m_MenuButtons.Count(); i++)
{
if (m_MenuButtons[i]->m_bIsSelected)
{
//check for size and to see if we can select item
if (i - 1 < 0)
return;


//select that item
//make divider
GetParent()->OnCommand(m_MenuButtons[i-1]->GetCommand()->GetString("command"));
vgui::Divider* divider2 = new vgui::Divider(this, "Divider");
return;
divider2->SetBounds(-5, 124, SOUND_LIST_PANEL_WIDTH + 10, 2);
}
 
}
//create text
}
vgui::Label* label2 = new vgui::Label(this, "SoundButtons", "Sound Buttons");
label2->SetBounds(140, 127, 120, 20);
//check for arrow
 
if (code == KEY_DOWN)
//create play button
{
m_PlayButton = new vgui::Button(this, "PlayButton", "Play Sound", this);
//find selected item
m_PlayButton->SetBounds(5, 150, SOUND_LIST_PANEL_WIDTH - 15, 20);
for (int i = 0; i < m_MenuButtons.Count(); i++)
m_PlayButton->SetCommand(SOUND_LIST_PLAY_COMMAND);
{
 
if (m_MenuButtons[i]->m_bIsSelected)
//create stop sound button
{
m_StopSoundButton = new vgui::Button(this, "StopSound", "Stop Sound", this);
//check for size and to see if we can select item
m_StopSoundButton->SetBounds(5, 175, SOUND_LIST_PANEL_WIDTH - 15, 20);
if (i + 1 >= m_MenuButtons.Count())
m_StopSoundButton->SetCommand(SOUND_LIST_STOP_COMMAND);
return;
 
//create sound insert button
m_InsertButton = new vgui::Button(this, "InsertSound", "Insert Sound", this);
m_InsertButton->SetBounds(5, 225, SOUND_LIST_PANEL_WIDTH - 15, 20);
m_InsertButton->SetCommand(SOUND_LIST_INSERT_COMMAND);


//select that item
//create reload sounds button
GetParent()->OnCommand(m_MenuButtons[i+1]->GetCommand()->GetString("command"));
m_ReloadSounds = new vgui::Button(this, "ReloadSounds", "Reload Sounds", this);
return;
m_ReloadSounds->SetBounds(5, 200, SOUND_LIST_PANEL_WIDTH - 15, 20);
}
m_ReloadSounds->SetCommand(SOUND_LIST_RELOAD_COMMAND);
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called on scroll bar moved
// Purpose: Called on command
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeList::ScrollBarMoved(int delta)
void CSoundListPanel::OnCommand(const char* pszCommand)
{
{
int position = m_pSideSlider->GetValue();
if (!Q_strcmp(pszCommand, SOUND_LIST_SEARCH_COMMAND))
 
//move everything down (if needed)
for (int i = 0; i < m_MenuButtons.Count(); i++)
{
{
//make not visible if i < position
//get text
if (i < position)
char buf[512];
{
m_SearchText->GetText(buf, sizeof(buf));
m_MenuButtons[i]->SetVisible(false);
continue;
}


m_MenuButtons[i]->SetPos(5, 22 * ((i - position) + 1));
//check for shift key
m_MenuButtons[i]->SetVisible(true);
bool shift = (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT));
}
}


//vector of texts
CUtlVector<char*> SoundNames;
CSoundListComboBox* SoundList = bCurrentlyInSoundPanel ? m_SoundsList : m_SoundscapesList;


//if we are in soundscape mode then set the SoundNames to all the soundscapes. else set SoundNames to g_SoundDirectories
if (!bCurrentlyInSoundPanel)
{
for (int i = 0; i < m_SoundscapesList->GetItemCount(); i++)
{
//insert
char* tmpbuf = new char[512];
m_SoundscapesList->GetItemText(i, tmpbuf, 512);


SoundNames.AddToTail(tmpbuf);
}
}
else
{
SoundNames = g_SoundDirectories;
}


//soundscape data list
if (shift)
{
//start from current index - 1
int start = SoundList->GetMenu()->GetActiveItem() - 1;


//look for sound with same name starting from the start first and going down
for (int i = start; i >= 0; i--)
{
if (Q_stristr(SoundNames[i], buf))
{
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


#define NEW_PLAYLOOPING_COMMAND "NewLooping"
//set text
#define NEW_SOUNDSCAPE_COMMAND "NewSoundscape"
SoundList->SetText(SoundNames[i]);
#define NEW_RANDOM_COMMAND "NewRandom"
 


class CSoundscapeDataList : public CSoundscapeList
//delete all soundscapes if we need to
{
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
public:
delete[] SoundNames[i];
DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);
CSoundscapeDataList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
{}


//override right click functionality
return;
virtual void OnMouseReleased(vgui::MouseCode code);
}
}


void OnCommand(const char* pszCommand);


private:
//now cheeck from the SoundNames to the start
friend class CSoundscapeMaker;
for (int i = SoundNames.Count() - 1; i > start; i--)
};
{
if (Q_stristr(SoundNames[i], buf))
{
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


//set text
SoundList->SetText(SoundNames[i]);


//-----------------------------------------------------------------------------
//delete all soundscapes if we need to
// Purpose: Called when a mouse code is released
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
//-----------------------------------------------------------------------------
delete[] SoundNames[i];
void CSoundscapeDataList::OnMouseReleased(vgui::MouseCode code)
 
{
return;
//if no soundscape is selected or mouse code != right then return
}
if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
}
return;
}
else
{
//start from current index + 1
int start = SoundList->GetMenu()->GetActiveItem() + 1;


//get cursor pos
//look for sound with same name starting from the start first
int x, y;
for (int i = start; i < SoundNames.Count(); i++)
vgui::surface()->SurfaceGetCursorPos(x, y);
{
if (Q_stristr(SoundNames[i], buf))
{
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


//create menu
//set text
menu = new vgui::Menu(this, "Menu");
SoundList->SetText(SoundNames[i]);
menu->AddMenuItem("AddLooping", "Add Looping Sound", NEW_PLAYLOOPING_COMMAND, this);
menu->AddMenuItem("AddSoundscape", "Add Soundscape", NEW_SOUNDSCAPE_COMMAND, this);
menu->AddMenuItem("AddSoundscape", "Add Random Sounds", NEW_RANDOM_COMMAND, this);
menu->SetBounds(x, y, 200, 50);
menu->SetVisible(true);
}


//delete all soundscapes if we need to
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
delete[] SoundNames[i];
return;
}
}


//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeDataList::OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, NEW_PLAYLOOPING_COMMAND))
{
int LoopingNum = 0;
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
{
//store data name
const char* name = data->GetName();


//increment variables based on name
//now cheeck from 0 to the start
if (!Q_strcasecmp(name, "playlooping"))
for (int i = 0; i < start; i++)
LoopingNum++;
{
}
if (Q_stristr(SoundNames[i], buf))
{
//select item
SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
SoundList->ActivateItem(i);


//add the keyvalue to both this and the keyvalues
//set text
AddButton("playlooping", "playlooping", CFmtStr("$playlooping%d", LoopingNum + 1), GetParent());
SoundList->SetText(SoundNames[i]);


//add the keyvalues
//delete all soundscapes if we need to
KeyValues* kv = new KeyValues("playlooping");
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
kv->SetFloat("volume", 1);
delete[] SoundNames[i];
kv->SetInt("pitch", 100);


m_Keyvalues->AddSubKey(kv);
return;
}
}
}


GetParent()->OnCommand(CFmtStr("$playlooping%d", LoopingNum + 1));
//delete all soundscapes if we need to
if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
delete[] SoundNames[i];


return;
return;
}
}
else if (!Q_strcmp(pszCommand, NEW_SOUNDSCAPE_COMMAND))
else if (!Q_strcmp(pszCommand, SOUND_LIST_PLAY_COMMAND))
{
{
int SoundscapeNum = 0;
//get the sound
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
char buf[512];
m_SoundsList->GetText(buf, sizeof(buf));
 
//stop the sound
if (enginesound->IsSoundStillPlaying(m_iSongGuid))
{
{
//store data name
enginesound->StopSoundByGuid(m_iSongGuid);
const char* name = data->GetName();
m_iSongGuid = -1;
 
//increment variables based on name
if (!Q_strcasecmp(name, "playsoundscape"))
SoundscapeNum++;
}
}


AddButton("playsoundscape", "playsoundscape", CFmtStr("$playsoundscape%d", SoundscapeNum + 1), GetParent());
//precache and play the sound
 
if (!enginesound->IsSoundPrecached(buf))
//add the keyvalues
enginesound->PrecacheSound(buf);
KeyValues* kv = new KeyValues("playsoundscape");
kv->SetFloat("volume", 1);
 
//add the keyvalue to both this and the keyvalues
m_Keyvalues->AddSubKey(kv);
 
GetParent()->OnCommand(CFmtStr("$playsoundscape%d", SoundscapeNum + 1));


enginesound->EmitAmbientSound(buf, 1, 100);
m_iSongGuid = enginesound->GetGuidForLastSoundEmitted();
return;
return;
}
}
else if (!Q_strcmp(pszCommand, NEW_RANDOM_COMMAND))
else if (!Q_strcmp(pszCommand, SOUND_LIST_STOP_COMMAND))
{
{
int RandomNum = 0;
//stop the sound
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
if (m_iSongGuid != -1 && enginesound->IsSoundStillPlaying(m_iSongGuid))
{
{
//store data name
enginesound->StopSoundByGuid(m_iSongGuid);
const char* name = data->GetName();
m_iSongGuid = -1;
 
//increment variables based on name
if (!Q_strcasecmp(name, "playrandom"))
RandomNum++;
}
}


AddButton("playrandom", "playrandom", CFmtStr("$playrandom%d", RandomNum + 1), GetParent());
return;
 
}
//add the keyvalues
else if (!Q_strcmp(pszCommand, SOUND_LIST_INSERT_COMMAND))
KeyValues* kv = new KeyValues("playrandom");
{
kv->SetString("volume", "0.5,0.8");
//make not visible
kv->SetInt("pitch", 100);
SetVisible(false);
kv->SetString("time", "10,20");
 
//stop the sound
//make rndwave subkey
if (enginesound->IsSoundStillPlaying(m_iSongGuid))
KeyValues* rndwave = new KeyValues("rndwave");
{
kv->AddSubKey(rndwave);
enginesound->StopSoundByGuid(m_iSongGuid);
m_iSongGuid = -1;
}


//add the keyvalue to both this and the keyvalues
//get the sound
m_Keyvalues->AddSubKey(kv);
char buf[512];


//make the parent show the new item
if (bCurrentlyInSoundPanel)
GetParent()->OnCommand(CFmtStr("$playrandom%d", RandomNum + 1));
m_SoundsList->GetText(buf, sizeof(buf));
else
m_SoundscapesList->GetText(buf, sizeof(buf));


//set the sound text
g_SoundscapeMaker->SetSoundText(buf);
return;
return;
}
}
else if (!Q_strcmp(pszCommand, SOUND_LIST_RELOAD_COMMAND))
{
if (bCurrentlyInSoundPanel)
{
//clear everything for the combo box and reload it
m_SoundsList->RemoveAll();
InitalizeSounds();
}
else
{
//clear everything for the combo box and reload it
m_SoundscapesList->RemoveAll();


BaseClass::OnCommand(pszCommand);
bool bPrev = g_bSSMHack;
}
g_bSSMHack = true;


//reload all the soundscape files
enginesound->StopAllSounds(true);


//soundscape rndwave data list
g_SoundscapeSystem.StartNewSoundscape(nullptr);
g_SoundscapeSystem.RemoveAll();
g_SoundscapeSystem. Init();


g_bSSMHack = bPrev;


#define NEW_RNDWAVE_WAVE_COMMAND "NewRNDWave"
//load all the temporary soundscapes
CUtlVector<const char*> OtherSoundscapes;
for (KeyValues* curr = g_SoundscapeMaker->GetPanelFile(); curr; curr = curr->GetNextKey())
{
if (curr == g_SoundscapeMaker->GetPanelSelected())
continue;


OtherSoundscapes.AddToTail(curr->GetName());
}


class CSoundscapeRndwaveList : public CSoundscapeList
InitalizeSoundscapes(OtherSoundscapes);
{
}
public:
DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);
CSoundscapeRndwaveList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
{}


//override right click functionality
return;
virtual void OnMouseReleased(vgui::MouseCode code);
}


void OnCommand(const char* pszCommand);
BaseClass::OnCommand(pszCommand);
 
}
private:
friend class CSoundscapeMaker;
};


//-----------------------------------------------------------------------------
// Purpose: Called on panel close
//-----------------------------------------------------------------------------
void CSoundListPanel::OnClose()
{
OnCommand(SOUND_LIST_STOP_COMMAND);
BaseClass::OnClose();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called when a mouse code is released
// Purpose: Initalizes the sounds list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeRndwaveList::OnMouseReleased(vgui::MouseCode code)
void CSoundListPanel::InitalizeSounds()
{
{
//if no soundscape is selected or mouse code != right then return
//get the sound array
if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
GetSoundNames();
return;


//get cursor pos
//add all the sounds
int x, y;
for (int i = 0; i < g_SoundDirectories.Size(); i++)
vgui::surface()->SurfaceGetCursorPos(x, y);
m_SoundsList->AddItem(g_SoundDirectories[i], nullptr);


//create menu
m_SoundsList->ActivateItem(0);
menu = new vgui::Menu(this, "Menu");
menu->AddMenuItem("AddRandom", "Add Random Wave", NEW_RNDWAVE_WAVE_COMMAND, this);
menu->SetBounds(x, y, 200, 50);
menu->SetVisible(true);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called on command
// Purpose: Initalizes the soundscape list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeRndwaveList::OnCommand(const char* pszCommand)
void CSoundListPanel::InitalizeSoundscapes(CUtlVector<const char*>& OtherSoundscapes)
{
{
if (!Q_strcmp(pszCommand, NEW_RNDWAVE_WAVE_COMMAND) && m_Keyvalues)
//remove everything
{
m_SoundscapesList->RemoveAll();
//get number of keyvalues
int num = 0;


FOR_EACH_VALUE(m_Keyvalues, kv)
//add all the soundscapes
num++;
for (int i = 0; i < g_SoundscapeSystem.m_soundscapes.Count(); i++)
OtherSoundscapes.AddToTail(g_SoundscapeSystem.m_soundscapes[i]->GetName());
//add keyvalues and button
AddButton("Rndwave", "", CFmtStr("$rndwave%d", num + 1), GetParent());


KeyValues* add = new KeyValues("wave");
OtherSoundscapes.Sort(VectorSortFunc);
add->SetString(nullptr, "");
m_Keyvalues->AddSubKey(add);


//forward command to parent
//quickly remove duplicatesd
GetParent()->OnCommand(CFmtStr("$rndwave%d", num + 1));
for (int i = 1; i < OtherSoundscapes.Count(); )
{
if (!Q_strcmp(OtherSoundscapes[i], OtherSoundscapes[i - 1]))
{
OtherSoundscapes.Remove(i);
continue;
}
i++;
}


return;
for (int i = 0; i < OtherSoundscapes.Size(); i++)
}
m_SoundscapesList->AddItem(OtherSoundscapes[i], nullptr);


BaseClass::OnCommand(pszCommand);
m_SoundscapesList->ActivateItem(0);
}
}


//-----------------------------------------------------------------------------
// Purpose: Sets if this panel is currently the sound panel or soundscape
// selector panel.
//-----------------------------------------------------------------------------
void CSoundListPanel::SetIsUsingSoundPanel(bool bUsing)
{
bCurrentlyInSoundPanel = bUsing;


//disable stuff
if (bUsing)
{
//set 'reload' text
m_ReloadSounds->SetText("Reload Sounds");


//soundscape panel
m_SoundscapesList->SetVisible(false);
m_SoundsList->SetVisible(true);


//enable the play button
m_PlayButton->SetEnabled(true);
m_StopSoundButton->SetEnabled(true);


#define SOUNDSCAPE_PANEL_WIDTH 760
//set texts
#define SOUNDSCAPE_PANEL_HEIGHT 630
m_PlayButton->SetText("Play Sound");
m_StopSoundButton->SetText("Stop Sound");
m_InsertButton->SetText("Insert Sound");


#define NEW_BUTTON_COMMAND "$NewSoundscape"
//set title
#define SAVE_BUTTON_COMMAND "$SaveSoundscape"
SetTitle("Sounds List", true);
#define LOAD_BUTTON_COMMAND "$LoadSoundscape"
}
#define OPTIONS_BUTTON_COMMAND "$ShowOptions"
else
#define EDIT_BUTTON_COMMAND "$Edit"
{
#define RESET_BUTTON_COMMAND "$ResetSoundscapes"
//set 'reload' text
#define SOUNDS_LIST_BUTTON_COMMAND "$ShowSoundsList"
m_ReloadSounds->SetText("Reload Soundscapes");
#define PLAY_SOUNDSCAPE_COMMAND "$PlaySoundscape"
#define RESET_SOUNDSCAPE_BUTTON_COMMAND "$ResetSoundscape"
#define DELETE_CURRENT_ITEM_COMMAND "$DeleteItem"


//static bool to determin if the soundscape panel should show or not
m_SoundscapesList->SetVisible(true);
bool g_ShowSoundscapePanel = false;
m_SoundsList->SetVisible(false);
bool g_IsPlayingSoundscape = false;


//soundscape maker panel
//disable the play button
class CSoundscapeMaker : public vgui::Frame, CAutoGameSystem
m_PlayButton->SetEnabled(false);
{
m_StopSoundButton->SetEnabled(false);
public:
DECLARE_CLASS_SIMPLE(CSoundscapeMaker, vgui::Frame)


CSoundscapeMaker(vgui::VPANEL parent);
//set texts
m_PlayButton->SetText("Play Soundscape");
m_StopSoundButton->SetText("Stop Soundscape");
m_InsertButton->SetText("Insert Soundscape");


//tick functions
//set stuff
void OnTick();
SetTitle("Soundscape List", true);
}
}
 
//static sound list instance
static CSoundListPanel* g_SoundPanel = nullptr;


//other functions
void OnClose();
void OnCommand(const char* pszCommand);


void PlaySelectedSoundscape();
//soundscape list
void LoadFile(KeyValues* file);


void OnKeyCodePressed(vgui::KeyCode code);


void SetSoundText(const char* text);
#define ADD_SOUNDSCAPE_COMMAND "AddSoundscape"
#define PASTE_FROM_CLIBOARD_COMMAND "PasteFromClipboard"
#define OPEN_CLIBOARD_COMMAND "OpenClipboard"


//to play the soundscape on map spawn
void LevelInitPostEntity();


//sets the keyvalue file
//soundscape list class
void Set(const char* buffer);
class CSoundscapeList : public vgui::Divider
{
public:
DECLARE_CLASS_SIMPLE(CSoundscapeList, vgui::Divider);


//message pointer funcs
//constructor
MESSAGE_FUNC_CHARPTR(OnFileSelected, "FileSelected", fullpath);
CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height);
MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);


~CSoundscapeMaker();
//menu item stuff
virtual void AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent, KeyValues* add, SoundscapeClipboardType type);
virtual void Clear();


private:
//other
//the soundscape keyvalues file
virtual void OnMouseWheeled(int delta);
KeyValues* m_KeyValues = nullptr;
virtual void OnMouseReleased(vgui::MouseCode code);


private:
virtual void OnCommand(const char* pszCommand);
void CreateEverything();
virtual void PaintBackground();


private:
virtual void OnKeyCodeReleased(vgui::KeyCode code);
//lists all the soundscapes
CSoundscapeList* m_SoundscapesList;
CSoundscapeDataList* m_pDataList;
CSoundscapeRndwaveList* m_pSoundList;


//buttons
//message funcs
vgui::Button* m_ButtonNew = nullptr;
MESSAGE_FUNC_INT(ScrollBarMoved, "ScrollBarSliderMoved", position);
vgui::Button* m_ButtonSave = nullptr;
vgui::Button* m_ButtonLoad = nullptr;
vgui::Button* m_ButtonOptions = nullptr;
vgui::Button* m_EditButton = nullptr;


//file load and save dialogs
protected:
vgui::FileOpenDialog* m_FileSave = nullptr;
friend class CSoundscapeMaker;
vgui::FileOpenDialog* m_FileLoad = nullptr;
bool m_bWasFileLoad = false;


//text entry for name
//keyvalue list.
vgui::TextEntry* m_TextEntryName;
KeyValues* m_Keyvalues = nullptr;


//combo box for dsp effects
//says "Soundscapes List"
vgui::ComboBox* m_DspEffects;
vgui::Label* m_pLabel;
vgui::ComboBox* m_SoundLevels;
vgui::ScrollBar* m_pSideSlider;


//sound data text entry
//menu
vgui::TextEntry* m_TimeTextEntry;
vgui::Menu* menu;
vgui::TextEntry* m_VolumeTextEntry;
vgui::TextEntry* m_PitchTextEntry;
vgui::TextEntry* m_PositionTextEntry;
vgui::TextEntry* m_SoundNameTextEntry;


//play sound button
//menu button stuff
vgui::Button* m_SoundNamePlay;
CUtlVector<CSoundscapeButton*> m_MenuButtons;
int m_iCurrentY;
int m_iMax;
int m_AmtAdded;
};


//play/reset soundscape buttons
//-----------------------------------------------------------------------------
vgui::CheckButton* m_PlaySoundscapeButton;
// Purpose: Constructor for soundscape list panel
vgui::Button* m_ResetSoundscapeButton;
//-----------------------------------------------------------------------------
vgui::Button* m_DeleteCurrentButton;
CSoundscapeList::CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: BaseClass(parent, name)
{
//create the text
m_pLabel = new vgui::Label(this, "ListsText", text);
m_pLabel->SetVisible(true);
m_pLabel->SetBounds(text_x_pos, 2, 150, 20);


//current selected soundscape
//create the side slider
CSoundscapeButton* m_pCurrentSelected = nullptr;
m_pSideSlider = new vgui::ScrollBar(this, "ListsSlider", true);
KeyValues* m_kvCurrSelected = nullptr;
m_pSideSlider->SetBounds(width - 20, 0, 20, height - 2);
KeyValues* m_kvCurrSound = nullptr;
m_pSideSlider->SetValue(0);
KeyValues* m_kvCurrRndwave = nullptr;
m_pSideSlider->SetEnabled(false);
m_pSideSlider->SetRange(0, 0);
m_pSideSlider->SetButtonPressedScrollValue(1);
m_pSideSlider->SetRangeWindow(0);
m_pSideSlider->AddActionSignalTarget(this);


int m_iCurrRndWave = 0;
m_iCurrentY = 22;
 
m_iMax = max;
//currently in non randomwave thing
m_Keyvalues = nullptr;
SoundscapeMode m_iSoundscapeMode = SoundscapeMode::Mode_Random;
}
 
//temporary added soundscapes
CUtlVector<KeyValues*> m_TmpAddedSoundscapes;
};
 
//user message hook
void _SoundscapeMaker_Recieve(bf_read& bf);


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Constructor for soundscape maker panel
// Purpose: adds a button to the soundscape list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CSoundscapeMaker::CSoundscapeMaker(vgui::VPANEL parent)
void CSoundscapeList::AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent, KeyValues* add, SoundscapeClipboardType type)
: BaseClass(nullptr, "SoundscapeMaker")
{
{
static bool bRegistered = false;
//create a new button
if (!bRegistered)
CSoundscapeButton* button = new CSoundscapeButton(this, name, text, parent, command, add, type);
{
button->SetBounds(5, m_iCurrentY, GetWide() - 30, 20);
usermessages->HookMessage("SoundscapeMaker_Recieve", _SoundscapeMaker_Recieve);
 
bRegistered = true;
//increment current y
}
m_iCurrentY = m_iCurrentY + 22;


//set variables
//add button to array
m_pCurrentSelected = nullptr;
m_MenuButtons.AddToTail(button);


SetParent(parent);
//if the count is more then m_iMax then set slider value
if (m_MenuButtons.Count() > m_iMax)
SetKeyBoardInputEnabled(true);
{
SetMouseInputEnabled(true);
int max = m_MenuButtons.Count() - m_iMax;


SetProportional(false);
m_pSideSlider->SetRange(0, max);
SetTitleBarVisible(true);
m_pSideSlider->SetRangeWindow(1);
SetMinimizeButtonVisible(false);
m_pSideSlider->SetEnabled(true);
SetMaximizeButtonVisible(false);
}
SetCloseButtonVisible(true);
SetSizeable(false);
SetMoveable(true);
SetVisible(g_ShowSoundscapePanel);
int ScreenWide, ScreenTall;
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);


SetTitle("Soundscape Maker (New File)", true);
m_AmtAdded++;
SetSize(SOUNDSCAPE_PANEL_WIDTH, SOUNDSCAPE_PANEL_HEIGHT);
SetPos((ScreenWide - SOUNDSCAPE_PANEL_WIDTH) / 2, (ScreenTall - SOUNDSCAPE_PANEL_HEIGHT) / 2);


//check to see if we need to scroll down
 
if (m_MenuButtons.Count() >= m_iMax)
//add a tick signal for every 50 ms
OnMouseWheeled(-1);
vgui::ivgui()->AddTickSignal(GetVPanel(), 50);
 
CreateEverything();
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Creates everything for this panel
// Purpose: Clears everything for this list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeMaker::CreateEverything()
void CSoundscapeList::Clear()
{
{
//create the divider that will be the outline for the inside of the panel
//reset the slider
vgui::Divider* PanelOutline = new vgui::Divider(this, "InsideOutline");
m_pSideSlider->SetValue(0);
PanelOutline->SetEnabled(false);
m_pSideSlider->SetEnabled(false);
PanelOutline->SetBounds(5, 25, SOUNDSCAPE_PANEL_WIDTH - 10, SOUNDSCAPE_PANEL_HEIGHT - 62);
m_pSideSlider->SetRange(0, 0);
m_pSideSlider->SetButtonPressedScrollValue(1);
m_pSideSlider->SetRangeWindow(0);
 
//delete and clear the buttons
for (int i = 0; i < m_MenuButtons.Count(); i++)
m_MenuButtons[i]->DeletePanel();
 
m_MenuButtons.RemoveAll();


//create the buttons
//reset current y
//create the buttons
m_iCurrentY = 22;
m_ButtonNew = new vgui::Button(this, "NewButton", "New Soundscape File");
m_ButtonNew->SetVisible(true);
m_ButtonNew->SetBounds(7, 600, 145, 25);
m_ButtonNew->SetCommand(NEW_BUTTON_COMMAND);
m_ButtonNew->SetDepressedSound("ui/buttonclickrelease.wav");


m_ButtonSave = new vgui::Button(this, "SaveButton", "Save Soundscapes");
m_AmtAdded = 0;
m_ButtonSave->SetVisible(true);
}
m_ButtonSave->SetBounds(157, 600, 145, 25);
m_ButtonSave->SetCommand(SAVE_BUTTON_COMMAND);
m_ButtonSave->SetDepressedSound("ui/buttonclickrelease.wav");


m_ButtonLoad = new vgui::Button(this, "LoadButton", "Load Soundscapes");
//-----------------------------------------------------------------------------
m_ButtonLoad->SetVisible(true);
// Purpose: Called when a mouse is wheeled
m_ButtonLoad->SetBounds(307, 600, 145, 25);
//-----------------------------------------------------------------------------
m_ButtonLoad->SetCommand(LOAD_BUTTON_COMMAND);
void CSoundscapeList::OnMouseWheeled(int delta)
m_ButtonLoad->SetDepressedSound("ui/buttonclickrelease.wav");
{
//check for scroll down
m_ButtonOptions = new vgui::Button(this, "OptionsButton", "Show Options Panel");
if (delta == -1)
m_ButtonOptions->SetVisible(true);
m_pSideSlider->SetValue(m_pSideSlider->GetValue() + 1);
m_ButtonOptions->SetBounds(457, 600, 145, 25);
m_ButtonOptions->SetCommand(OPTIONS_BUTTON_COMMAND);
m_ButtonOptions->SetDepressedSound("ui/buttonclickrelease.wav");
m_EditButton = new vgui::Button(this, "EditButton", "Show Text Editor");
m_EditButton->SetVisible(true);
m_EditButton->SetBounds(607, 600, 145, 25);
m_EditButton->SetCommand(EDIT_BUTTON_COMMAND);
m_EditButton->SetDepressedSound("ui/buttonclickrelease.wav");


//create the soundscapes menu
//check for scroll up
m_SoundscapesList = new CSoundscapeList(this, "SoundscapesList", "Soundscapes:", 90, 22, 300, 550);
else if (delta == 1)
m_SoundscapesList->SetBounds(15, 35, 300, 550);
m_pSideSlider->SetValue(m_pSideSlider->GetValue() - 1);
m_SoundscapesList->SetVisible(true);
}


//create data list
//-----------------------------------------------------------------------------
m_pDataList = new CSoundscapeDataList(this, "SoudscapeDataList", "Soundscape Data:", 35, 10, 200, 310);
// Purpose: Called when a mouse code is released
m_pDataList->SetBounds(327, 275, 200, 310);
//-----------------------------------------------------------------------------
m_pDataList->SetVisible(true);
void CSoundscapeList::OnMouseReleased(vgui::MouseCode code)
{
//create sound list
if (code != vgui::MouseCode::MOUSE_RIGHT)
m_pSoundList = new CSoundscapeRndwaveList(this, "SoudscapeDataList", "Random Sounds:", 40, 10, 200, 310);
return;
m_pSoundList->SetBounds(542, 275, 200, 310);
m_pSoundList->SetVisible(true);


//name text entry
//get cursor pos
m_TextEntryName = new vgui::TextEntry(this, "NameTextEntry");
int x, y;
m_TextEntryName->SetEnabled(false);
vgui::surface()->SurfaceGetCursorPos(x, y);
m_TextEntryName->SetBounds(325, 40, 295, 20);
m_TextEntryName->SetMaximumCharCount(50);


//dsp effects combo box
//create menu
m_DspEffects = new vgui::ComboBox(this, "DspEffects", sizeof(g_DspEffects) / sizeof(g_DspEffects[0]), false);
menu = new vgui::Menu(this, "Menu");
m_DspEffects->SetEnabled(false);
menu->AddMenuItem("AddSoundscape", "Add Soundscape", ADD_SOUNDSCAPE_COMMAND, this);
m_DspEffects->SetBounds(325, 65, 295, 20);
 
m_DspEffects->SetText("");
//check clipboard item
m_DspEffects->AddActionSignalTarget(this);
if (CurrClipboardName.Count() > 0)
{
menu->AddSeparator();
menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
}
 
menu->SetBounds(x, y, 200, 50);
menu->SetVisible(true);
}


for (int i = 0; i < sizeof(g_DspEffects) / sizeof(g_DspEffects[i]); i++)
//-----------------------------------------------------------------------------
m_DspEffects->AddItem(g_DspEffects[i], nullptr);
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeList::OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, ADD_SOUNDSCAPE_COMMAND))
{
const char* name = CFmtStr("New Soundscape %d", m_AmtAdded);


//time text entry
//add to keyvalues file
m_TimeTextEntry = new vgui::TextEntry(this, "TimeTextEntry");
KeyValues* kv = new KeyValues(name);
m_TimeTextEntry->SetBounds(325, 90, 295, 20);
KeyValues* tmp = m_Keyvalues;
m_TimeTextEntry->SetEnabled(false);
KeyValues* tmp2 = tmp;
m_TimeTextEntry->SetVisible(true);


//volume text entry
AddButton(name, name, name, GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeName);
m_VolumeTextEntry = new vgui::TextEntry(this, "VolumeTextEntry");
m_VolumeTextEntry->SetBounds(325, 115, 295, 20);
m_VolumeTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetVisible(true);


//pitch text entry
//get last subkey
m_PitchTextEntry = new vgui::TextEntry(this, "PitchTextEntry");
while (tmp != nullptr)
m_PitchTextEntry->SetBounds(325, 140, 295, 20);
{
m_PitchTextEntry->SetEnabled(false);
tmp2 = tmp;
m_PitchTextEntry->SetVisible(true);
tmp = tmp->GetNextTrueSubKey();
}
//position text entry
 
m_PositionTextEntry = new vgui::TextEntry(this, "PositionTextEntry");
//add to last subkey
m_PositionTextEntry->SetBounds(325, 165, 295, 20);
tmp2->SetNextKey(kv);
m_PositionTextEntry->SetEnabled(false);
m_PositionTextEntry->SetVisible(true);
//sound levels
m_SoundLevels = new vgui::ComboBox(this, "SoundLevels", sizeof(g_SoundLevels) / sizeof(g_SoundLevels[0]), false);
m_SoundLevels->SetEnabled(false);
m_SoundLevels->SetBounds(325, 190, 295, 20);
m_SoundLevels->SetText("");
m_SoundLevels->AddActionSignalTarget(this);


for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
GetParent()->OnCommand(name);
m_SoundLevels->AddItem(g_SoundLevels[i], nullptr);
return;
}
//sound name
else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
m_SoundNameTextEntry = new vgui::TextEntry(this, "SoundName");
{
m_SoundNameTextEntry->SetBounds(325, 215, 215, 20);
int index = CurrClipboardName.Count() - 1;
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNameTextEntry->SetVisible(true);


//sound list button
const char* name = CFmtStr("%s - (Copy %d)", CurrClipboardName[index]->GetName(), m_AmtAdded);
m_SoundNamePlay = new vgui::Button(this, "SoundPlayButton", "Sounds List");
m_SoundNamePlay->SetBounds(545, 215, 75, 20);
m_SoundNamePlay->SetCommand(SOUNDS_LIST_BUTTON_COMMAND);
m_SoundNamePlay->SetEnabled(false);


//starts the soundscape
//add to keyvalues file
m_PlaySoundscapeButton = new vgui::CheckButton(this, "PlaySoundscape", "Play Soundscape");
KeyValues* kv = new KeyValues(name);
m_PlaySoundscapeButton->SetBounds(330, 243, 125, 20);
CurrClipboardName[index]->CopySubkeys(kv);
m_PlaySoundscapeButton->SetCommand(PLAY_SOUNDSCAPE_COMMAND);
m_PlaySoundscapeButton->SetEnabled(false);
m_PlaySoundscapeButton->SetSelected(false);


//reset soundscape button
KeyValues* tmp = m_Keyvalues;
m_ResetSoundscapeButton = new vgui::Button(this, "ResetSoundscape", "Restart Soundscape");
KeyValues* tmp2 = tmp;
m_ResetSoundscapeButton->SetBounds(465, 243, 125, 20);
m_ResetSoundscapeButton->SetCommand(RESET_SOUNDSCAPE_BUTTON_COMMAND);
m_ResetSoundscapeButton->SetEnabled(false);


//delete this item
AddButton(name, name, name, GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeName);
m_DeleteCurrentButton = new vgui::Button(this, "DeleteItem", "Delete Current Item");
m_DeleteCurrentButton->SetBounds(595, 243, 135, 20);
m_DeleteCurrentButton->SetCommand(DELETE_CURRENT_ITEM_COMMAND);
m_DeleteCurrentButton->SetEnabled(false);


//create the soundscape name text
//get last subkey
vgui::Label* NameLabel = new vgui::Label(this, "NameLabel", "Soundscape Name");
while (tmp != nullptr)
NameLabel->SetBounds(635, 40, 125, 20);
{
tmp2 = tmp;
tmp = tmp->GetNextTrueSubKey();
}


//create the soundscape dsp text
//add to last subkey
vgui::Label* DspLabel = new vgui::Label(this, "DspLabel", "Soundscape Dsp");
tmp2->SetNextKey(kv);
DspLabel->SetBounds(635, 65, 125, 20);
//create the soundscape time text
vgui::Label* TimeLabel = new vgui::Label(this, "TimeLabel", "Sound Time");
TimeLabel->SetBounds(635, 90, 125, 20);
//create the soundscape volumn text
vgui::Label* VolumeLabel = new vgui::Label(this, "VolumeLabel", "Sound Volume");
VolumeLabel->SetBounds(635, 115, 125, 20);


//create the soundscape pitch text
GetParent()->OnCommand(name);
vgui::Label* PitchLabel = new vgui::Label(this, "PitchLabel", "Sound Pitch");
return;
PitchLabel->SetBounds(635, 140, 125, 20);
}
else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
//create the soundscape position text
{
vgui::Label* PositionLabel = new vgui::Label(this, "PositionLabel", "Sound Position");
if (g_SoundscapeClipboard)
PositionLabel->SetBounds(635, 165, 125, 20);
g_SoundscapeClipboard->DeletePanel();


//create the soundscape sound level text
g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeName);
vgui::Label* SoundLevelLabel = new vgui::Label(this, "SoundLevelLabel", "Sound Level");
return;
SoundLevelLabel ->SetBounds(635, 190, 125, 20);
}


//create the soundscape sound name text
BaseClass::OnCommand(pszCommand);
vgui::Label* SoundName = new vgui::Label(this, "SoundName", "Sound Name");
SoundName->SetBounds(635, 215, 125, 20);
 
//create the soundscape keyvalues and load it
m_KeyValues = new KeyValues("Empty Soundscape");
 
LoadFile(m_KeyValues);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called every tick for the soundscape maker
// Purpose: Paints the background
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnTick()
void CSoundscapeList::PaintBackground()
{
{
//set the visibility
//colors
static bool bPrevVisible = g_ShowSoundscapePanel;
static Color EnabledColor = Color(100, 100, 100, 200);
if (g_ShowSoundscapePanel != bPrevVisible)
static Color DisabledColor = Color(60, 60, 60, 200);
SetVisible(g_ShowSoundscapePanel);


//set the old visibility
//if m_KeyValues then paint the default color
bPrevVisible = g_ShowSoundscapePanel;
if (m_Keyvalues)
}
SetBgColor(EnabledColor);
else
SetBgColor(DisabledColor);


//-----------------------------------------------------------------------------
BaseClass::PaintBackground();
// Purpose: Called when the close button is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnClose()
{
//hide the other panels
g_SoundPanel->OnClose();
g_SettingsPanel->OnClose();
g_SoundscapeTextPanel->OnClose();
 
g_ShowSoundscapePanel = false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Play the selected soundscape
// Purpose: Called on keyboard code pressed
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeMaker::PlaySelectedSoundscape()
void CSoundscapeList::OnKeyCodeReleased(vgui::KeyCode code)
{
{
//set debug stuff
//check for arrow
SoundscapePrint(Color(255, 255, 255, 255), "\n\n\n=============== %s %s =================\n\n", m_kvCurrSelected ? "Starting Soundscape: " : "Stopping Current Soundscape", m_kvCurrSelected ? m_kvCurrSelected->GetName() : "");
if (code == KEY_UP)
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Clear();
{
//find selected item
for (int i = 0; i < m_MenuButtons.Count(); i++)
{
if (m_MenuButtons[i]->m_bIsSelected)
{
//check for size and to see if we can select item
if (i - 1 < 0)
return;


g_IsPlayingSoundscape = true;
//select that item
g_bSSMHack = true;
GetParent()->OnCommand(m_MenuButtons[i - 1]->GetCommand()->GetString("command"));
return;
}
}
}


//remove all the temporary soundscapes from the soundscape system
//check for arrow
for (int i = 0; i < m_TmpAddedSoundscapes.Count(); i++)
if (code == KEY_DOWN)
{
{
for (int j = 0; j < g_SoundscapeSystem.m_soundscapes.Count(); j++)
//find selected item
for (int i = 0; i < m_MenuButtons.Count(); i++)
{
{
if (g_SoundscapeSystem.m_soundscapes[j] == m_TmpAddedSoundscapes[i])
if (m_MenuButtons[i]->m_bIsSelected)
{
{
g_SoundscapeSystem.m_soundscapes.Remove(j);
//check for size and to see if we can select item
break;
if (i + 1 >= m_MenuButtons.Count())
return;
 
//select that item
GetParent()->OnCommand(m_MenuButtons[i + 1]->GetCommand()->GetString("command"));
return;
}
}
}
}
}
}
}


m_TmpAddedSoundscapes.RemoveAll();
//-----------------------------------------------------------------------------
// Purpose: Called on scroll bar moved
//-----------------------------------------------------------------------------
void CSoundscapeList::ScrollBarMoved(int delta)
{
int position = m_pSideSlider->GetValue();


//move everything down (if needed)
//change audio params position
for (int i = 0; i < m_MenuButtons.Count(); i++)
g_SoundscapeSystem.m_params.localBits = 0x7f;
for (int i = 0; i < MAX_SOUNDSCAPES - 1; i++)
g_SoundscapeSystem.m_params.localSound.Set(i, g_SoundscapePositions[i]);
 
 
//if m_kvCurrSelected then add all the "playsoundscape" soundscape keyvalues
//into the g_SoundscapeSystem.m_soundscapes array
if (m_kvCurrSelected)
{
{
CUtlVector<const char*> SoundscapeNames;
//make not visible if i < position
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, subkey)
if (i < position)
{
{
//look for playsoundscape file
m_MenuButtons[i]->SetVisible(false);
if (!Q_strcasecmp(subkey->GetName(), "playsoundscape"))
continue;
{
const char* name = subkey->GetString("name", nullptr);
if (!name || !name[0] || SoundscapeNames.Find(name) != SoundscapeNames.InvalidIndex())
continue;
 
SoundscapeNames.AddToTail(name);
}
}
}


//now look for each keyvalue
m_MenuButtons[i]->SetPos(5, 22 * ((i - position) + 1));
for (int i = 0; i < SoundscapeNames.Count(); i++)
m_MenuButtons[i]->SetVisible(true);
{
for (KeyValues* subkey = m_KeyValues; subkey != nullptr; subkey = subkey->GetNextTrueSubKey())
{
//look for playsoundscape file
if (!Q_strcmp(subkey->GetName(), SoundscapeNames[i]))
{
//add it to the soundscape system
m_TmpAddedSoundscapes.AddToTail(subkey);
g_SoundscapeSystem.m_soundscapes.AddToTail(subkey);
}
}
}
}
}
}


//stop all sounds
enginesound->StopAllSounds(true);


//stop the current soundscape and start a new soundscape
g_SoundscapeSystem.StartNewSoundscape(nullptr);
g_SoundscapeSystem.StartNewSoundscape(m_kvCurrSelected);


//start debug graphs
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Start();


g_bSSMHack = false;
//soundscape data list
}
 
 
#define NEW_PLAYLOOPING_COMMAND "NewLooping"
#define NEW_SOUNDSCAPE_COMMAND "NewSoundscape"
#define NEW_RANDOM_COMMAND "NewRandom"
 
 
class CSoundscapeDataList : public CSoundscapeList
{
public:
DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);
 
CSoundscapeDataList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
{}
 
//override right click functionality
virtual void OnMouseReleased(vgui::MouseCode code);
 
void OnCommand(const char* pszCommand);
 
private:
friend class CSoundscapeMaker;
};
 


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Called when a button or something else gets pressed
// Purpose: Called when a mouse code is released
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnCommand(const char* pszCommand)
void CSoundscapeDataList::OnMouseReleased(vgui::MouseCode code)
{
{
 
//if no soundscape is selected or mouse code != right then return
//check for close command first
if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
if (!Q_strcmp(pszCommand, "Close"))
{
BaseClass::OnCommand(pszCommand);
return;
return;
}


//check for the save button command
//get cursor pos
else if (!Q_strcmp(pszCommand, SAVE_BUTTON_COMMAND))
int x, y;
{
vgui::surface()->SurfaceGetCursorPos(x, y);
//initalize the file save dialog
if (!m_FileSave)
{
//get the current game directory
char buf[512];
filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));


//create the save dialog
//create menu
m_FileSave = new vgui::FileOpenDialog(this, "Save Soundscape File", false);
menu = new vgui::Menu(this, "Menu");
m_FileSave->AddFilter("*.txt", "Soundscape Text File", true);
menu->AddMenuItem("AddLooping", "Add Looping Sound", NEW_PLAYLOOPING_COMMAND, this);
m_FileSave->AddFilter("*.*", "All Files (*.*)", false);
menu->AddMenuItem("AddSoundscape", "Add Soundscape", NEW_SOUNDSCAPE_COMMAND, this);
m_FileSave->SetStartDirectory(buf);
menu->AddMenuItem("AddSoundscape", "Add Random Sounds", NEW_RANDOM_COMMAND, this);
m_FileSave->AddActionSignalTarget(this);
}


//show the dialog
//add clipboard thing
m_FileSave->DoModal(false);
if (CurrClipboardData.Count() > 0)
m_FileSave->Activate();
{
menu->AddSeparator();
menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
}


//file wasnt loadad
menu->SetBounds(x, y, 200, 50);
m_bWasFileLoad = false;
menu->SetVisible(true);
}


return;
}


//check for load button command
//-----------------------------------------------------------------------------
else if (!Q_strcmp(pszCommand, LOAD_BUTTON_COMMAND))
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeDataList::OnCommand(const char* pszCommand)
{
if (!Q_strcmp(pszCommand, NEW_PLAYLOOPING_COMMAND))
{
{
//initalize the file save dialog
int LoopingNum = 0;
if (!m_FileLoad)
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
{
{
//get the current game directory
//store data name
char buf[512];
const char* name = data->GetName();
filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));


//create the load dialog
//increment variables based on name
m_FileLoad = new vgui::FileOpenDialog(this, "Load Soundscape File", true);
if (!Q_strcasecmp(name, "playlooping"))
m_FileLoad->AddFilter("*.txt", "Soundscape Text File", true);
LoopingNum++;
m_FileLoad->AddFilter("*.*", "All Files (*.*)", false);
m_FileLoad->SetStartDirectory(buf);
m_FileLoad->AddActionSignalTarget(this);
}
}


//show the file load dialog
//add the keyvalues
m_FileLoad->DoModal(false);
KeyValues* kv = new KeyValues("playlooping");
m_FileLoad->Activate();
kv->SetFloat("volume", 1);
kv->SetInt("pitch", 100);
 
//add the keyvalue to both this and the keyvalues
AddButton("playlooping", "playlooping", CFmtStr("$playlooping%d", LoopingNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);


//file was loadad
m_Keyvalues->AddSubKey(kv);
m_bWasFileLoad = true;


return;
GetParent()->OnCommand(CFmtStr("$playlooping%d", LoopingNum + 1));
}


//check for options panel button
else if (!Q_strcmp(pszCommand, OPTIONS_BUTTON_COMMAND))
{
g_SettingsPanel->SetVisible(true);
g_SettingsPanel->MoveToFront();
g_SettingsPanel->RequestFocus();
return;
return;
}
}
else if (!Q_strcmp(pszCommand, NEW_SOUNDSCAPE_COMMAND))
//check for edit panel button
else if (!Q_strcmp(pszCommand, EDIT_BUTTON_COMMAND))
{
{
g_SoundscapeTextPanel->SetVisible(true);
int SoundscapeNum = 0;
g_SoundscapeTextPanel->MoveToFront();
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
g_SoundscapeTextPanel->RequestFocus();
{
g_SoundscapeTextPanel->Set(m_KeyValues);
//store data name
return;
const char* name = data->GetName();
}


//check for new soundscape
//increment variables based on name
else if (!Q_strcmp(pszCommand, NEW_BUTTON_COMMAND))
if (!Q_strcasecmp(name, "playsoundscape"))
{
SoundscapeNum++;
//make sure you want to create a new soundscape file
}
vgui::QueryBox* popup = new vgui::QueryBox("New File?", "Are you sure you want to create a new soundscape file?", this);
popup->SetOKCommand(new KeyValues("Command", "command", RESET_BUTTON_COMMAND));
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


return;
//add the keyvalues
}
KeyValues* kv = new KeyValues("playsoundscape");
kv->SetFloat("volume", 1);


//check for reset soundscape
AddButton("playsoundscape", "playsoundscape", CFmtStr("$playsoundscape%d", SoundscapeNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);
else if (!Q_strcmp(pszCommand, RESET_BUTTON_COMMAND))
{
m_kvCurrSelected = nullptr;


//stop all soundscapes before deleting the old soundscapes
//add the keyvalue to both this and the keyvalues
if (g_IsPlayingSoundscape)
m_Keyvalues->AddSubKey(kv);
PlaySelectedSoundscape();


m_KeyValues->deleteThis();
GetParent()->OnCommand(CFmtStr("$playsoundscape%d", SoundscapeNum + 1));
m_KeyValues = new KeyValues("Empty Soundscape");
 
//reset title
SetTitle("Soundscape Maker (New File)", true);


LoadFile(m_KeyValues);
return;
return;
}
}
 
else if (!Q_strcmp(pszCommand, NEW_RANDOM_COMMAND))
//check for play sound
else if (!Q_strcmp(pszCommand, SOUNDS_LIST_BUTTON_COMMAND))
{
{
//initalize the sounds
int RandomNum = 0;
static bool g_SoundPanelInitalized = false;
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
if (!g_SoundPanelInitalized)
{
{
g_SoundPanelInitalized = true;
//store data name
g_SoundPanel->InitalizeSounds();
const char* name = data->GetName();
g_SoundPanel->InitalizeSoundscapes();
 
//increment variables based on name
if (!Q_strcasecmp(name, "playrandom"))
RandomNum++;
}
}
//get sound text entry name
char buf[512];
m_SoundNameTextEntry->GetText(buf, sizeof(buf));


//check the current mode
//add the keyvalues
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
KeyValues* kv = new KeyValues("playrandom");
g_SoundPanel->SetIsUsingSoundPanel(false);
else
{
g_SoundPanel->SetIsUsingSoundPanel(true);


//look for item with same name
for (int i = 0; i < g_SoundDirectories.Count(); i++)
{
if (!Q_strcmp(buf, g_SoundDirectories[i]))
{
//select item
g_SoundPanel->m_SoundsList->ActivateItem(i);
g_SoundPanel->m_SoundsList->SetText(buf);


break;
kv->SetString("volume", "0.5,0.8");
}
kv->SetInt("pitch", 100);
}
kv->SetString("time", "10,20");
}


g_SoundPanel->SetVisible(true);
AddButton("playrandom", "playrandom", CFmtStr("$playrandom%d", RandomNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);
g_SoundPanel->MoveToFront();
g_SoundPanel->RequestFocus();
return;
}


//check for play soundscape
//make rndwave subkey
else if (!Q_strcmp(pszCommand, PLAY_SOUNDSCAPE_COMMAND))
KeyValues* rndwave = new KeyValues("rndwave");
kv->AddSubKey(rndwave);
 
//add the keyvalue to both this and the keyvalues
m_Keyvalues->AddSubKey(kv);
 
//make the parent show the new item
GetParent()->OnCommand(CFmtStr("$playrandom%d", RandomNum + 1));
 
return;
}
else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
{
{
if (m_PlaySoundscapeButton->IsSelected())
int index = CurrClipboardData.Count() - 1;
 
const char* type = CurrClipboardData[index]->GetName();
 
//get num of that item
int NumItem = 0;
FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
{
{
//enable the reset soundscape button
//store data name
m_ResetSoundscapeButton->SetEnabled(true);
const char* name = data->GetName();


//play the soundscape
//increment variables based on name
PlaySelectedSoundscape();
if (!Q_strcasecmp(name, type))
NumItem++;
}
}
else
{
//disable the reset soundscape button
m_ResetSoundscapeButton->SetEnabled(false);


g_IsPlayingSoundscape = false;
//add the keyvalues
KeyValues* kv = new KeyValues(type);
CurrClipboardData[index]->CopySubkeys(kv);
 
AddButton(type, type, CFmtStr("$%s%d", type, NumItem + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);


//stop all sounds and soundscapes
//add the keyvalue to both this and the keyvalues
enginesound->StopAllSounds(true);
m_Keyvalues->AddSubKey(kv);
g_SoundscapeSystem.StartNewSoundscape(nullptr);
}


//make the parent show the new item
GetParent()->OnCommand(CFmtStr("$%s%d", type, NumItem + 1));
return;
return;
}
}
else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
{
if (g_SoundscapeClipboard)
g_SoundscapeClipboard->DeletePanel();


//check for play soundscape
g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeData);
else if (!Q_strcmp(pszCommand, RESET_SOUNDSCAPE_BUTTON_COMMAND))
{
PlaySelectedSoundscape();
return;
return;
}
}


//check for delete item
BaseClass::OnCommand(pszCommand);
else if (!Q_strcmp(pszCommand, DELETE_CURRENT_ITEM_COMMAND))
}
{
 
//check for current rndwave
if (m_kvCurrRndwave && m_SoundNameTextEntry->IsEnabled())
{
if (!m_kvCurrRndwave || m_iCurrRndWave <= 0)
return;


//get the keyvalues by the index
//soundscape rndwave data list
int curr = 0;
KeyValues* prev = nullptr;


FOR_EACH_VALUE(m_kvCurrRndwave, keyvalues)
{
if (++curr == m_iCurrRndWave)
{
//delete
if (prev)
prev->SetNextKey(keyvalues->GetNextValue());
else
{
m_kvCurrRndwave->m_pSub = keyvalues->GetNextValue();
m_iCurrRndWave = -1;
}


curr = curr - 1;
#define NEW_RNDWAVE_WAVE_COMMAND "NewRNDWave"


keyvalues->SetNextKey(nullptr);
keyvalues->deleteThis();
break;
}


class CSoundscapeRndwaveList : public CSoundscapeList
{
public:
DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);


CSoundscapeRndwaveList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
{}


prev = keyvalues;
//override right click functionality
}
virtual void OnMouseReleased(vgui::MouseCode code);


//reset everything
void OnCommand(const char* pszCommand);
m_SoundNameTextEntry->SetText("");
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


//store vector
private:
auto& vec = m_pSoundList->m_MenuButtons;
friend class CSoundscapeMaker;
};


//remove it
delete vec[curr];
vec.Remove(curr);


//move everything down
//-----------------------------------------------------------------------------
m_pSoundList->m_iCurrentY = m_pSoundList->m_iCurrentY - 22;
// Purpose: Called when a mouse code is released
m_pSoundList->m_Keyvalues = m_kvCurrRndwave;
//-----------------------------------------------------------------------------
void CSoundscapeRndwaveList::OnMouseReleased(vgui::MouseCode code)
{
//if no soundscape is selected or mouse code != right then return
if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
return;


if (vec.Count() >= m_pSoundList->m_iMax)
//get cursor pos
{
int x, y;
m_pSoundList->OnMouseWheeled(1);
vgui::surface()->SurfaceGetCursorPos(x, y);


int min, max;
//create menu
m_pSoundList->m_pSideSlider->GetRange(min, max);
menu = new vgui::Menu(this, "Menu");
m_pSoundList->m_pSideSlider->SetRange(0, max - 1);
menu->AddMenuItem("AddRandom", "Add Random Wave", NEW_RNDWAVE_WAVE_COMMAND, this);
}


for (int i = curr; i < vec.Count(); i++)
//add clipboard thing
{
if (CurrClipboardRandom.Count() > 0)
//move everything down
{
int x, y = 0;
menu->AddSeparator();
vec[i]->GetPos(x, y);
menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
vec[i]->SetPos(x, y - 22);
menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
}
}


//reset every command
menu->SetBounds(x, y, 200, 50);
int WaveAmount = 0;
menu->SetVisible(true);
for (int i = 0; i < vec.Count(); i++)
}
{
//store data name
const char* name = vec[i]->GetCommand()->GetString("command");


//increment variables based on name
if (Q_stristr(name, "$rndwave") == name)
{
WaveAmount++;
vec[i]->SetCommand(CFmtStr("$rndwave%d", WaveAmount));
}
}


//bounds check
//-----------------------------------------------------------------------------
if (vec.Count() <= 0)
// Purpose: Called on command
{
//-----------------------------------------------------------------------------
m_kvCurrRndwave = nullptr;
void CSoundscapeRndwaveList::OnCommand(const char* pszCommand)
m_pSoundList->m_Keyvalues = nullptr;
{
if (!Q_strcmp(pszCommand, NEW_RNDWAVE_WAVE_COMMAND) && m_Keyvalues)
{
//get number of keyvalues
int num = 0;


//restart soundscape
FOR_EACH_VALUE(m_Keyvalues, kv)
PlaySelectedSoundscape();
num++;


return;
KeyValues* add = new KeyValues("wave");
}
add->SetString(nullptr, "");


//select next item
//add keyvalues and button
if (m_iCurrRndWave <= vec.Count())
AddButton("Rndwave", "", CFmtStr("$rndwave%d", num + 1), GetParent(), add, SoundscapeClipboardType::Type_SoundscapeRandomWave);
OnCommand(CFmtStr("$rndwave%d", curr + 1));
else
OnCommand(CFmtStr("$rndwave%d", curr));
}
else if (m_kvCurrSound)
{
//find keyvalue with same pointer and get the index
int tmpindex = 0;
int index = -1;


KeyValues* prev = nullptr;
m_Keyvalues->AddSubKey(add);
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, keyvalues)
 
{
//forward command to parent
if (m_kvCurrSound == keyvalues)
GetParent()->OnCommand(CFmtStr("$rndwave%d", num + 1));
{
//remove it
if (prev)
prev->SetNextKey(keyvalues->GetNextTrueSubKey());
else
m_kvCurrSelected->m_pSub = keyvalues->GetNextTrueSubKey();


keyvalues->SetNextKey(nullptr);
return;
keyvalues->deleteThis();
}


//get index
else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
index = tmpindex;
{
break;
//get number of keyvalues
}
int num = 0;


prev = keyvalues;
FOR_EACH_VALUE(m_Keyvalues, kv)
num++;


//increment
int index = CurrClipboardRandom.Count() - 1;
tmpindex++;
}


//error
const char* text = CurrClipboardRandom[index]->GetString();
if (index == -1)
return;


//store vector
KeyValues* add = new KeyValues("wave");
auto& vec = m_pDataList->m_MenuButtons;
add->SetString(nullptr, text);


//remove it
//get last / or \ and make the string be that + 1
delete vec[index];
char* fslash = Q_strrchr(text, '/');
vec.Remove(index);
char* bslash = Q_strrchr(text, '\\');


//move everything down
if (fslash > bslash)
m_pDataList->m_iCurrentY = m_pDataList->m_iCurrentY - 22;
text = fslash + 1;
m_pDataList->m_Keyvalues = m_kvCurrSelected;
else if (bslash > fslash)
text = bslash + 1;


for (int i = index; i < vec.Count(); i++)
//add keyvalues and button
{
AddButton("Rndwave", text, CFmtStr("$rndwave%d", num + 1), GetParent(), add, SoundscapeClipboardType::Type_SoundscapeRandomWave);
//move everything down
 
int x, y = 0;
m_Keyvalues->AddSubKey(add);
vec[i]->GetPos(x, y);
vec[i]->SetPos(x, y - 22);
}


if (vec.Count() >= m_pDataList->m_iMax)
//forward command to parent
{
GetParent()->OnCommand(CFmtStr("$rndwave%d", num + 1));
m_pDataList->OnMouseWheeled(1);
return;
}
else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
{
if (g_SoundscapeClipboard)
g_SoundscapeClipboard->DeletePanel();


int min, max;
g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeRandomWave);
m_pDataList->m_pSideSlider->GetRange(min, max);
return;
}
if (max > 0)
m_pDataList->m_pSideSlider->SetRange(0, max - 1);
else
m_pDataList->m_pSideSlider->SetRange(0, 0);
}


//reset the names of each button
BaseClass::OnCommand(pszCommand);
int RandomNum = 0;
}
int LoopingNum = 0;
int SoundscapeNum = 0;


//change the commands of the buttons
for (int i = 0; i < vec.Count(); i++)
{
//store data name
const char* name = vec[i]->GetCommand()->GetString("command");


//increment variables based on name
//soundscape panel
if (Q_stristr(name, "$playrandom") == name)
{
RandomNum++;
vec[i]->SetCommand(CFmtStr("$playrandom%d", RandomNum));
}


if (Q_stristr(name, "$playlooping") == name)
{
LoopingNum++;
vec[i]->SetCommand(CFmtStr("$playlooping%d", LoopingNum));
}


if (Q_stristr(name, "$playsoundscape") == name)
#define SOUNDSCAPE_PANEL_WIDTH 760
{
#define SOUNDSCAPE_PANEL_HEIGHT 630
SoundscapeNum++;
vec[i]->SetCommand(CFmtStr("$playsoundscape%d", SoundscapeNum));
}
}


//reset everything
#define NEW_BUTTON_COMMAND "$NewSoundscape"
m_SoundLevels->SetText("");
#define SAVE_BUTTON_COMMAND "$SaveSoundscape"
m_SoundNameTextEntry->SetText("");
#define LOAD_BUTTON_COMMAND "$LoadSoundscape"
m_TimeTextEntry->SetText("");
#define OPTIONS_BUTTON_COMMAND "$ShowOptions"
m_PitchTextEntry->SetText("");
#define EDIT_BUTTON_COMMAND "$Edit"
m_PositionTextEntry->SetText("");
#define RESET_BUTTON_COMMAND "$ResetSoundscapes"
m_VolumeTextEntry->SetText("");
#define SOUNDS_LIST_BUTTON_COMMAND "$ShowSoundsList"
#define PLAY_SOUNDSCAPE_COMMAND "$PlaySoundscape"
#define RESET_SOUNDSCAPE_BUTTON_COMMAND "$ResetSoundscape"
#define DELETE_CURRENT_ITEM_COMMAND "$DeleteItem"


m_SoundLevels->SetEnabled(false);
//static bool to determin if the soundscape panel should show or not
m_SoundNameTextEntry->SetEnabled(false);
bool g_ShowSoundscapePanel = false;
m_TimeTextEntry->SetEnabled(false);
bool g_IsPlayingSoundscape = false;
m_PitchTextEntry->SetEnabled(false);
m_PositionTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


m_pSoundList->Clear();
//soundscape maker panel
class CSoundscapeMaker : public vgui::Frame, CAutoGameSystem
{
public:
DECLARE_CLASS_SIMPLE(CSoundscapeMaker, vgui::Frame)


m_kvCurrSound = nullptr;
CSoundscapeMaker(vgui::VPANEL parent);
m_pSoundList->m_Keyvalues = nullptr;


//bounds checking
//tick functions
if (index >= vec.Count())
void OnTick();
index = vec.Count() - 1; // fix bounds more safely


//select the button
//other functions
if (index >= 0)
void OnClose();
OnCommand(vec[index]->GetCommand()->GetString("command"));
void OnCommand(const char* pszCommand);
}
void Paste(SoundscapeClipboardType type);
else if (m_kvCurrSelected)
{
if (m_KeyValues == m_kvCurrSelected)
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//show an error
void PlaySelectedSoundscape();
vgui::QueryBox* popup = new vgui::QueryBox("Error", "Can not delete base soundscape!", this);
void LoadFile(KeyValues* file);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


return;
void OnKeyCodePressed(vgui::KeyCode code);
}


//find keyvalue with same pointer and get the index
void SetSoundText(const char* text);
int tmpindex = 0;
int index = -1;


KeyValues* prev = nullptr;
//to play the soundscape on map spawn
for (KeyValues* keyvalues = m_KeyValues; keyvalues != nullptr; keyvalues = keyvalues->GetNextTrueSubKey())
void LevelInitPostEntity();
{
if (m_kvCurrSelected == keyvalues)
{
//remove it
if (!prev)
break;


prev->SetNextKey(keyvalues->GetNextTrueSubKey());
//sets the keyvalue file
keyvalues->SetNextKey(nullptr);
void Set(const char* buffer);
keyvalues->deleteThis();


//get index
//message pointer funcs
index = tmpindex;
MESSAGE_FUNC_CHARPTR(OnFileSelected, "FileSelected", fullpath);
break;
MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);
}


prev = keyvalues;
~CSoundscapeMaker();


//increment
public:
tmpindex++;
}


//error
//the soundscape keyvalues file
if (index == -1)
KeyValues* m_KeyValues = nullptr;
return;


//store vector
private:
auto& vec = m_SoundscapesList->m_MenuButtons;
void CreateEverything();


//remove it
private:
delete vec[index];
//lists all the soundscapes
vec.Remove(index);
CSoundscapeList* m_SoundscapesList;
CSoundscapeDataList* m_pDataList;
CSoundscapeRndwaveList* m_pSoundList;


//move everything down
//buttons
m_SoundscapesList->m_iCurrentY = m_SoundscapesList->m_iCurrentY - 22;
vgui::Button* m_ButtonNew = nullptr;
vgui::Button* m_ButtonSave = nullptr;
vgui::Button* m_ButtonLoad = nullptr;
vgui::Button* m_ButtonOptions = nullptr;
vgui::Button* m_EditButton = nullptr;


for (int i = index; i < vec.Count(); i++)
//file load and save dialogs
{
vgui::FileOpenDialog* m_FileSave = nullptr;
//move everything down
vgui::FileOpenDialog* m_FileLoad = nullptr;
int x, y = 0;
bool m_bWasFileLoad = false;
vec[i]->GetPos(x, y);
vec[i]->SetPos(x, y - 22);
}


if (vec.Count() >= m_SoundscapesList->m_iMax)
//text entry for name
{
vgui::TextEntry* m_TextEntryName;
m_SoundscapesList->OnMouseWheeled(1);


int min, max;
//combo box for dsp effects
m_SoundscapesList->m_pSideSlider->GetRange(min, max);
vgui::ComboBox* m_DspEffects;
m_SoundscapesList->m_pSideSlider->SetRange(0, max - 1);
vgui::ComboBox* m_SoundLevels;
}


//reset everything
//sound data text entry
m_DspEffects->SetText("");
vgui::TextEntry* m_TimeTextEntry;
m_SoundLevels->SetText("");
vgui::TextEntry* m_VolumeTextEntry;
m_TextEntryName->SetText("");
vgui::TextEntry* m_PitchTextEntry;
m_SoundNameTextEntry->SetText("");
vgui::TextEntry* m_PositionTextEntry;
m_TimeTextEntry->SetText("");
vgui::TextEntry* m_SoundNameTextEntry;
m_PitchTextEntry->SetText("");
m_PositionTextEntry->SetText("");
m_VolumeTextEntry->SetText("");
m_DspEffects->SetEnabled(false);
m_SoundLevels->SetEnabled(false);
m_TextEntryName->SetEnabled(false);
m_SoundNameTextEntry->SetEnabled(false);
m_TimeTextEntry->SetEnabled(false);
m_PitchTextEntry->SetEnabled(false);
m_PositionTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


m_pDataList->Clear();
//play sound button
m_pSoundList->Clear();
vgui::Button* m_SoundNamePlay;
 
//play/reset soundscape buttons
vgui::CheckButton* m_PlaySoundscapeButton;
vgui::Button* m_ResetSoundscapeButton;
vgui::Button* m_DeleteCurrentButton;
 
//current selected soundscape
CSoundscapeButton* m_pCurrentSelected = nullptr;


m_kvCurrSound = nullptr;
public:
m_pDataList->m_Keyvalues = nullptr;
KeyValues* m_kvCurrSelected = nullptr;


//go to next soundscape
private:
if (!prev)
KeyValues* m_kvCurrSound = nullptr;
{
KeyValues* m_kvCurrRndwave = nullptr;
//restart soundscape
PlaySelectedSoundscape();
return;
}


if (prev->GetNextTrueSubKey())
int m_iCurrRndWave = 0;
OnCommand(prev->GetNextTrueSubKey()->GetName());
else
OnCommand(prev->GetName());
}


//restart soundscape
//currently in non randomwave thing
PlaySelectedSoundscape();
SoundscapeMode m_iSoundscapeMode = SoundscapeMode::Mode_Random;
return;
}


//check for "playrandom", "playsoundscape" or "playlooping"
//temporary added soundscapes
if (Q_stristr(pszCommand, "$playrandom") == pszCommand)
CUtlVector<KeyValues*> m_TmpAddedSoundscapes;
{
};
//get the selected number
 
char* str_number = (char*)(pszCommand + 11);
//user message hook
int number = atoi(str_number);
void _SoundscapeMaker_Recieve(bf_read& bf);
if (number != 0)
 
{
//-----------------------------------------------------------------------------
//look for button with same command
// Purpose: Constructor for soundscape maker panel
auto& vec = m_pDataList->m_MenuButtons;
//-----------------------------------------------------------------------------
for (int i = 0; i < vec.Count(); i++)
CSoundscapeMaker::CSoundscapeMaker(vgui::VPANEL parent)
{
: BaseClass(nullptr, "SoundscapeMaker")
//if the button doesnt have the same command then de-select it. else select it
{
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
static bool bRegistered = false;
vec[i]->m_bIsSelected = true;
if (!bRegistered)
else
{
vec[i]->m_bIsSelected = false;
usermessages->HookMessage("SoundscapeMaker_Recieve", _SoundscapeMaker_Recieve);
}
bRegistered = true;
}


//set variables
m_pCurrentSelected = nullptr;


//clear the m_pSoundList
SetParent(parent);
m_pSoundList->Clear();
m_pSoundList->m_Keyvalues = nullptr;


//store variables
SetKeyBoardInputEnabled(true);
KeyValues* data = nullptr;
SetMouseInputEnabled(true);
int curr = 0;


//get subkey
SetProportional(false);
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
SetTitleBarVisible(true);
{
SetMinimizeButtonVisible(false);
if (Q_strcasecmp(sounds->GetName(), "playrandom"))
SetMaximizeButtonVisible(false);
continue;
SetCloseButtonVisible(true);
SetSizeable(false);
if (++curr == number)
SetMoveable(true);
{
SetVisible(g_ShowSoundscapePanel);
data = sounds;
 
break;
int ScreenWide, ScreenTall;
}
vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);
}


//no data
SetTitle("Soundscape Maker (New File)", true);
if (!data)
SetSize(SOUNDSCAPE_PANEL_WIDTH, SOUNDSCAPE_PANEL_HEIGHT);
return;
SetPos((ScreenWide - SOUNDSCAPE_PANEL_WIDTH) / 2, (ScreenTall - SOUNDSCAPE_PANEL_HEIGHT) / 2);


m_kvCurrSound = data;
m_kvCurrRndwave = nullptr;


//set the random times
m_TimeTextEntry->SetText(data->GetString("time", "10,20"));
m_VolumeTextEntry->SetText(data->GetString("volume", "0.5,0.8"));
m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
m_PositionTextEntry->SetText(data->GetString("position", ""));
m_SoundNameTextEntry->SetText("");


//get snd level index
//add a tick signal for every 50 ms
int index = 8; //8 = SNDLVL_NORM
vgui::ivgui()->AddTickSignal(GetVPanel(), 50);
const char* name = data->GetString("soundlevel", nullptr);


//check for the name
CreateEverything();
if (name)
}
{


//loop through the sound levels to find the right one
//-----------------------------------------------------------------------------
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
// Purpose: Creates everything for this panel
{
//-----------------------------------------------------------------------------
if (!Q_strcmp(name, g_SoundLevels[i]))
void CSoundscapeMaker::CreateEverything()
{
{
index = i;
//create the divider that will be the outline for the inside of the panel
break;
vgui::Divider* PanelOutline = new vgui::Divider(this, "InsideOutline");
}
PanelOutline->SetEnabled(false);
}
PanelOutline->SetBounds(5, 25, SOUNDSCAPE_PANEL_WIDTH - 10, SOUNDSCAPE_PANEL_HEIGHT - 62);
}


//select the index
//create the buttons
m_SoundLevels->ActivateItem(index);
//create the buttons
m_ButtonNew = new vgui::Button(this, "NewButton", "New Soundscape File");
m_ButtonNew->SetVisible(true);
m_ButtonNew->SetBounds(7, 600, 145, 25);
m_ButtonNew->SetCommand(NEW_BUTTON_COMMAND);
m_ButtonNew->SetDepressedSound("ui/buttonclickrelease.wav");


//enable the text entries
m_ButtonSave = new vgui::Button(this, "SaveButton", "Save Soundscapes");
m_TimeTextEntry->SetEnabled(true);
m_ButtonSave->SetVisible(true);
m_VolumeTextEntry->SetEnabled(true);
m_ButtonSave->SetBounds(157, 600, 145, 25);
m_PitchTextEntry->SetEnabled(true);
m_ButtonSave->SetCommand(SAVE_BUTTON_COMMAND);
m_PositionTextEntry->SetEnabled(true);
m_ButtonSave->SetDepressedSound("ui/buttonclickrelease.wav");
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
m_ButtonLoad = new vgui::Button(this, "LoadButton", "Load Soundscapes");
g_SoundPanel->SetVisible(false);
m_ButtonLoad->SetVisible(true);
m_ButtonLoad->SetBounds(307, 600, 145, 25);
m_ButtonLoad->SetCommand(LOAD_BUTTON_COMMAND);
m_ButtonLoad->SetDepressedSound("ui/buttonclickrelease.wav");


//check for randomwave subkey
m_ButtonOptions = new vgui::Button(this, "OptionsButton", "Show Options Panel");
if ((data = data->FindKey("rndwave")) == nullptr)
m_ButtonOptions->SetVisible(true);
return;
m_ButtonOptions->SetBounds(457, 600, 145, 25);
m_ButtonOptions->SetCommand(OPTIONS_BUTTON_COMMAND);
m_ButtonOptions->SetDepressedSound("ui/buttonclickrelease.wav");


m_kvCurrRndwave = data;
m_EditButton = new vgui::Button(this, "EditButton", "Show Text Editor");
m_pSoundList->m_Keyvalues = data;
m_EditButton->SetVisible(true);
m_EditButton->SetBounds(607, 600, 145, 25);
m_EditButton->SetCommand(EDIT_BUTTON_COMMAND);
m_EditButton->SetDepressedSound("ui/buttonclickrelease.wav");


//add all the data
//create the soundscapes menu
int i = 0;
m_SoundscapesList = new CSoundscapeList(this, "SoundscapesList", "Soundscapes:", 90, 22, 300, 550);
FOR_EACH_VALUE(data, sound)
m_SoundscapesList->SetBounds(15, 35, 300, 550);
{
m_SoundscapesList->SetVisible(true);
const char* name = sound->GetName();


//get real text
//create data list
const char* text = sound->GetString();
m_pDataList = new CSoundscapeDataList(this, "SoudscapeDataList", "Soundscape Data:", 35, 10, 200, 310);
m_pDataList->SetBounds(327, 275, 200, 310);
m_pDataList->SetVisible(true);


//get last / or \ and make the string be that + 1
//create sound list
char* fslash = Q_strrchr(text, '/');
m_pSoundList = new CSoundscapeRndwaveList(this, "SoudscapeDataList", "Random Sounds:", 40, 10, 200, 310);
char* bslash = Q_strrchr(text, '\\');
m_pSoundList->SetBounds(542, 275, 200, 310);
m_pSoundList->SetVisible(true);


//no forward slash and no back slash
//name text entry
if (!fslash && !bslash)
m_TextEntryName = new vgui::TextEntry(this, "NameTextEntry");
{
m_TextEntryName->SetEnabled(false);
text = text;
m_TextEntryName->SetBounds(325, 40, 295, 20);
}
m_TextEntryName->SetMaximumCharCount(256);
else
{
if (fslash > bslash)
text = fslash + 1;


else if (bslash > fslash)
//dsp effects combo box
text = bslash + 1;
m_DspEffects = new vgui::ComboBox(this, "DspEffects", sizeof(g_DspEffects) / sizeof(g_DspEffects[0]), false);
}
m_DspEffects->SetEnabled(false);
m_DspEffects->SetBounds(325, 65, 295, 20);
m_DspEffects->SetText("");
m_DspEffects->AddActionSignalTarget(this);


m_pSoundList->AddButton(name, text, CFmtStr("$rndwave%d", ++i), this);
for (int i = 0; i < sizeof(g_DspEffects) / sizeof(g_DspEffects[i]); i++)
}
m_DspEffects->AddItem(g_DspEffects[i], nullptr);


m_iSoundscapeMode = SoundscapeMode::Mode_Random;
//time text entry
return;
m_TimeTextEntry = new vgui::TextEntry(this, "TimeTextEntry");
}
m_TimeTextEntry->SetBounds(325, 90, 295, 20);
}
m_TimeTextEntry->SetEnabled(false);
else if (Q_stristr(pszCommand, "$playlooping") == pszCommand)
m_TimeTextEntry->SetVisible(true);
{
//get the selected number
char* str_number = (char*)(pszCommand + 12);
int number = atoi(str_number);
if (number != 0)
{
//look for button with same command
auto& vec = m_pDataList->m_MenuButtons;
for (int i = 0; i < vec.Count(); i++)
{
//if the button doesnt have the same command then de-select it. else select it
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
vec[i]->m_bIsSelected = true;
else
vec[i]->m_bIsSelected = false;
}


//volume text entry
m_VolumeTextEntry = new vgui::TextEntry(this, "VolumeTextEntry");
m_VolumeTextEntry->SetBounds(325, 115, 295, 20);
m_VolumeTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetVisible(true);


//clear the m_pSoundList
//pitch text entry
m_pSoundList->Clear();
m_PitchTextEntry = new vgui::TextEntry(this, "PitchTextEntry");
m_pSoundList->m_Keyvalues = nullptr;
m_PitchTextEntry->SetBounds(325, 140, 295, 20);
 
m_PitchTextEntry->SetEnabled(false);
//store variables
m_PitchTextEntry->SetVisible(true);
KeyValues* data = nullptr;
int curr = 0;


//get subkey
//position text entry
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
m_PositionTextEntry = new vgui::TextEntry(this, "PositionTextEntry");
{
m_PositionTextEntry->SetBounds(325, 165, 295, 20);
if (Q_strcasecmp(sounds->GetName(), "playlooping"))
m_PositionTextEntry->SetEnabled(false);
continue;
m_PositionTextEntry->SetVisible(true);


if (++curr == number)
//sound levels
{
m_SoundLevels = new vgui::ComboBox(this, "SoundLevels", sizeof(g_SoundLevels) / sizeof(g_SoundLevels[0]), false);
data = sounds;
m_SoundLevels->SetEnabled(false);
break;
m_SoundLevels->SetBounds(325, 190, 295, 20);
}
m_SoundLevels->SetText("");
}
m_SoundLevels->AddActionSignalTarget(this);


//no data
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
if (!data)
m_SoundLevels->AddItem(g_SoundLevels[i], nullptr);
return;


m_kvCurrSound = data;
//sound name
m_kvCurrRndwave = nullptr;
m_SoundNameTextEntry = new vgui::TextEntry(this, "SoundName");
m_SoundNameTextEntry->SetBounds(325, 215, 215, 20);
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNameTextEntry->SetVisible(true);
 
//sound list button
m_SoundNamePlay = new vgui::Button(this, "SoundPlayButton", "Sounds List");
m_SoundNamePlay->SetBounds(545, 215, 75, 20);
m_SoundNamePlay->SetCommand(SOUNDS_LIST_BUTTON_COMMAND);
m_SoundNamePlay->SetEnabled(false);


//set the random times
//starts the soundscape
m_TimeTextEntry->SetText("");
m_PlaySoundscapeButton = new vgui::CheckButton(this, "PlaySoundscape", "Play Soundscape");
m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
m_PlaySoundscapeButton->SetBounds(330, 243, 125, 20);
m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
m_PlaySoundscapeButton->SetCommand(PLAY_SOUNDSCAPE_COMMAND);
m_PositionTextEntry->SetText(data->GetString("position", ""));
m_PlaySoundscapeButton->SetEnabled(false);
m_SoundNameTextEntry->SetText(data->GetString("wave", ""));
m_PlaySoundscapeButton->SetSelected(false);


//get snd level index
//reset soundscape button
int index = 8; //8 = SNDLVL_NORM
m_ResetSoundscapeButton = new vgui::Button(this, "ResetSoundscape", "Restart Soundscape");
const char* name = data->GetString("soundlevel", nullptr);
m_ResetSoundscapeButton->SetBounds(465, 243, 125, 20);
m_ResetSoundscapeButton->SetCommand(RESET_SOUNDSCAPE_BUTTON_COMMAND);
m_ResetSoundscapeButton->SetEnabled(false);


//check for the name
//delete this item
if (name)
m_DeleteCurrentButton = new vgui::Button(this, "DeleteItem", "Delete Current Item");
{
m_DeleteCurrentButton->SetBounds(595, 243, 135, 20);
m_DeleteCurrentButton->SetCommand(DELETE_CURRENT_ITEM_COMMAND);
m_DeleteCurrentButton->SetEnabled(false);


//loop through the sound levels to find the right one
//create the soundscape name text
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
vgui::Label* NameLabel = new vgui::Label(this, "NameLabel", "Soundscape Name");
{
NameLabel->SetBounds(635, 40, 125, 20);
if (!Q_strcmp(name, g_SoundLevels[i]))
 
{
//create the soundscape dsp text
index = i;
vgui::Label* DspLabel = new vgui::Label(this, "DspLabel", "Soundscape Dsp");
break;
DspLabel->SetBounds(635, 65, 125, 20);
}
 
}
//create the soundscape time text
}
vgui::Label* TimeLabel = new vgui::Label(this, "TimeLabel", "Sound Time");
TimeLabel->SetBounds(635, 90, 125, 20);


//select the index
//create the soundscape volumn text
m_SoundLevels->ActivateItem(index);
vgui::Label* VolumeLabel = new vgui::Label(this, "VolumeLabel", "Sound Volume");
VolumeLabel->SetBounds(635, 115, 125, 20);


//enable the text entries
//create the soundscape pitch text
m_TimeTextEntry->SetEnabled(false);
vgui::Label* PitchLabel = new vgui::Label(this, "PitchLabel", "Sound Pitch");
m_VolumeTextEntry->SetEnabled(true);
PitchLabel->SetBounds(635, 140, 125, 20);
m_PitchTextEntry->SetEnabled(true);
m_PositionTextEntry->SetEnabled(true);
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(true);
m_SoundNamePlay->SetEnabled(true);
g_SoundPanel->SetVisible(false);


m_iSoundscapeMode = SoundscapeMode::Mode_Looping;
//create the soundscape position text
return;
vgui::Label* PositionLabel = new vgui::Label(this, "PositionLabel", "Sound Position");
}
PositionLabel->SetBounds(635, 165, 125, 20);
}
else if (Q_stristr(pszCommand, "$playsoundscape") == pszCommand)
{
//get the selected number
char* str_number = (char*)(pszCommand + 15);
int number = atoi(str_number);
if (number != 0)
{
//look for button with same command
auto& vec = m_pDataList->m_MenuButtons;
for (int i = 0; i < vec.Count(); i++)
{
//if the button doesnt have the same command then de-select it. else select it
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
vec[i]->m_bIsSelected = true;
else
vec[i]->m_bIsSelected = false;
}


//create the soundscape sound level text
vgui::Label* SoundLevelLabel = new vgui::Label(this, "SoundLevelLabel", "Sound Level");
SoundLevelLabel->SetBounds(635, 190, 125, 20);


//clear the m_pSoundList
//create the soundscape sound name text
m_pSoundList->Clear();
vgui::Label* SoundName = new vgui::Label(this, "SoundName", "Sound Name");
m_pSoundList->m_Keyvalues = nullptr;
SoundName->SetBounds(635, 215, 125, 20);


//store variables
//create the soundscape keyvalues and load it
KeyValues* data = nullptr;
m_KeyValues = new KeyValues("Empty Soundscape");
int curr = 0;


//get subkey
LoadFile(m_KeyValues);
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
}
{
if (Q_strcasecmp(sounds->GetName(), "playsoundscape"))
continue;


if (++curr == number)
//-----------------------------------------------------------------------------
{
// Purpose: Called every tick for the soundscape maker
data = sounds;
//-----------------------------------------------------------------------------
break;
void CSoundscapeMaker::OnTick()
}
{
}
//set the visibility
static bool bPrevVisible = g_ShowSoundscapePanel;
if (g_ShowSoundscapePanel != bPrevVisible)
SetVisible(g_ShowSoundscapePanel);


//no data
//set the old visibility
if (!data)
bPrevVisible = g_ShowSoundscapePanel;
return;
}


m_kvCurrSound = data;
//-----------------------------------------------------------------------------
m_kvCurrRndwave = nullptr;
// Purpose: Called when the close button is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnClose()
{
//hide the other panels
g_SoundPanel->OnClose();
g_SettingsPanel->OnClose();
g_SoundscapeTextPanel->OnClose();


//set the random times
g_ShowSoundscapePanel = false;
m_TimeTextEntry->SetText("");
}
m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
m_PositionTextEntry->SetText(data->GetString("positionoverride", ""));
m_SoundNameTextEntry->SetText(data->GetString("name", ""));
m_PitchTextEntry->SetText("");


//get snd level index
//-----------------------------------------------------------------------------
int index = 8; //8 = SNDLVL_NORM
// Purpose: Play the selected soundscape
const char* name = data->GetString("soundlevel", nullptr);
//-----------------------------------------------------------------------------
void CSoundscapeMaker::PlaySelectedSoundscape()
{
//set debug stuff
SoundscapePrint(Color(255, 255, 255, 255), "\n\n\n=============== %s %s =================\n\n", m_kvCurrSelected ? "Starting Soundscape: " : "Stopping Current Soundscape", m_kvCurrSelected ? m_kvCurrSelected->GetName() : "");
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Clear();


//check for the name
g_IsPlayingSoundscape = true;
if (name)
g_bSSMHack = true;
{


//loop through the sound levels to find the right one
//remove all the temporary soundscapes from the soundscape system
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
for (int i = 0; i < m_TmpAddedSoundscapes.Count(); i++)
{
{
if (!Q_strcmp(name, g_SoundLevels[i]))
for (int j = 0; j < g_SoundscapeSystem.m_soundscapes.Count(); j++)
{
{
index = i;
if (g_SoundscapeSystem.m_soundscapes[j] == m_TmpAddedSoundscapes[i])
break;
{
}
g_SoundscapeSystem.m_soundscapes.Remove(j);
}
break;
}
}
}
}


//select the index
m_TmpAddedSoundscapes.RemoveAll();
m_SoundLevels->ActivateItem(index);


//enable the text entries
//change audio params position
m_TimeTextEntry->SetEnabled(true);
g_SoundscapeSystem.m_params.localBits = 0x7f;
m_VolumeTextEntry->SetEnabled(true);
for (int i = 0; i < MAX_SOUNDSCAPES - 1; i++)
m_PitchTextEntry->SetEnabled(false);
g_SoundscapeSystem.m_params.localSound.Set(i, g_SoundscapePositions[i]);
m_PositionTextEntry->SetEnabled(true);
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(true);
m_TimeTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(true);


g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
g_SoundPanel->SetVisible(false);


m_iSoundscapeMode = SoundscapeMode::Mode_Soundscape;
//if m_kvCurrSelected then add all the "playsoundscape" soundscape keyvalues
return;
//into the g_SoundscapeSystem.m_soundscapes array
}
if (m_kvCurrSelected)
}
else if (Q_stristr(pszCommand, "$rndwave") == pszCommand)
{
{
if (!m_kvCurrRndwave)
CUtlVector<const char*> SoundscapeNames;
return;
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, subkey)
 
//get the selected number
char* str_number = (char*)(pszCommand + 8);
m_iCurrRndWave = atoi(str_number);
if (m_iCurrRndWave != 0)
{
{
//look for button with same command
//look for playsoundscape file
auto& vec = m_pSoundList->m_MenuButtons;
if (!Q_strcasecmp(subkey->GetName(), "playsoundscape"))
for (int i = 0; i < vec.Count(); i++)
{
{
//if the button doesnt have the same command then de-select it. else select it
const char* name = subkey->GetString("name", nullptr);
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
if (!name || !name[0] || SoundscapeNames.Find(name) != SoundscapeNames.InvalidIndex())
vec[i]->m_bIsSelected = true;
continue;
else
 
vec[i]->m_bIsSelected = false;
SoundscapeNames.AddToTail(name);
}
}
}


 
//now look for each keyvalue
int i = 0;
for (int i = 0; i < SoundscapeNames.Count(); i++)
 
{
//get value
for (KeyValues* subkey = m_KeyValues; subkey != nullptr; subkey = subkey->GetNextTrueSubKey())
KeyValues* curr = nullptr;
FOR_EACH_VALUE(m_kvCurrRndwave, wave)
{
{
if (++i == m_iCurrRndWave)
//look for playsoundscape file
if (!Q_strcmp(subkey->GetName(), SoundscapeNames[i]))
{
{
curr = wave;
//add it to the soundscape system
break;
m_TmpAddedSoundscapes.AddToTail(subkey);
g_SoundscapeSystem.m_soundscapes.AddToTail(subkey);
}
}
}
}
}
}


//if no curr then throw an error
//stop all sounds
if (!curr)
enginesound->StopAllSounds(true);
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//show error
//stop the current soundscape and start a new soundscape
char buf[1028];
g_SoundscapeSystem.StartNewSoundscape(nullptr);
Q_snprintf(buf, sizeof(buf), "Failed to get rndwave '%d' for subkey \"%s\"\nfor current soundscape file!", i, m_kvCurrSelected->GetName());
g_SoundscapeSystem.StartNewSoundscape(m_kvCurrSelected);


//show an error
//start debug graphs
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Start();
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
return;
}


m_SoundNameTextEntry->SetEnabled(true);
g_bSSMHack = false;
m_SoundNameTextEntry->SetText(curr->GetString());
}


m_SoundNamePlay->SetEnabled(true);
//-----------------------------------------------------------------------------
 
// Purpose: Called when a button or something else gets pressed
m_iSoundscapeMode = SoundscapeMode::Mode_Random;
//-----------------------------------------------------------------------------
return;
void CSoundscapeMaker::OnCommand(const char* pszCommand)
}
{
//check for close command first
if (!Q_strcmp(pszCommand, "Close"))
{
BaseClass::OnCommand(pszCommand);
return;
}
}


//look for button with the same name as the command
//check for the save button command
else if (!Q_strcmp(pszCommand, SAVE_BUTTON_COMMAND))
{
{
//store vars
//initalize the file save dialog
CUtlVector<CSoundscapeButton*>& array = m_SoundscapesList->m_MenuButtons;
if (!m_FileSave)
{
//get the current game directory
char buf[512];
filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));


//de-select button
//create the save dialog
if (m_pCurrentSelected)
m_FileSave = new vgui::FileOpenDialog(this, "Save Soundscape File", false);
m_pCurrentSelected->m_bIsSelected = false;
m_FileSave->AddFilter("*.txt", "Soundscape Text File", true);
 
m_FileSave->AddFilter("*.*", "All Files (*.*)", false);
//check for name
m_FileSave->SetStartDirectory(buf);
for (int i = 0; i < m_SoundscapesList->m_MenuButtons.Size(); i++)
m_FileSave->AddActionSignalTarget(this);
{
//check button name
if (!Q_strcmp(array[i]->GetCommand()->GetString("command"), pszCommand))
{
//found it
m_pCurrentSelected = array[i];
break;
}
}
}


//find selected keyvalue
//show the dialog
m_FileSave->DoModal(false);
m_FileSave->Activate();


//set needed stuff
//file wasnt loadad
if (m_pCurrentSelected)
m_bWasFileLoad = false;
{
//select button
m_pCurrentSelected->m_bIsSelected = true;


m_DeleteCurrentButton->SetEnabled(false);
return;
}


//reset the selected kv
//check for load button command
m_kvCurrSelected = nullptr;
else if (!Q_strcmp(pszCommand, LOAD_BUTTON_COMMAND))
 
{
//find selected keyvalues
//initalize the file save dialog
if (!m_FileLoad)
{
//get the current game directory
char buf[512];
filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));


for (KeyValues* kv = m_KeyValues; kv != nullptr; kv = kv->GetNextTrueSubKey())
//create the load dialog
{
m_FileLoad = new vgui::FileOpenDialog(this, "Load Soundscape File", true);
if (!Q_strcmp(kv->GetName(), pszCommand))
m_FileLoad->AddFilter("*.txt", "Soundscape Text File", true);
{
m_FileLoad->AddFilter("*.*", "All Files (*.*)", false);
m_kvCurrSelected = kv;
m_FileLoad->SetStartDirectory(buf);
break;
m_FileLoad->AddActionSignalTarget(this);
}
}
}


//set
//show the file load dialog
m_kvCurrSound = nullptr;
m_FileLoad->DoModal(false);
m_kvCurrRndwave = nullptr;
m_FileLoad->Activate();


m_TimeTextEntry->SetEnabled(false);
//file was loadad
m_TimeTextEntry->SetText("");
m_bWasFileLoad = true;


m_VolumeTextEntry->SetEnabled(false);
return;
m_VolumeTextEntry->SetText("");
}


m_PitchTextEntry->SetEnabled(false);
//check for options panel button
m_PitchTextEntry->SetText("");
else if (!Q_strcmp(pszCommand, OPTIONS_BUTTON_COMMAND))
{
g_SettingsPanel->SetVisible(true);
g_SettingsPanel->MoveToFront();
g_SettingsPanel->RequestFocus();
return;
}


m_PositionTextEntry->SetEnabled(false);
//check for edit panel button
m_PositionTextEntry->SetText("");
else if (!Q_strcmp(pszCommand, EDIT_BUTTON_COMMAND))
{
m_SoundLevels->SetEnabled(false);
g_SoundscapeTextPanel->SetVisible(true);
m_SoundLevels->SetText("");
g_SoundscapeTextPanel->MoveToFront();
g_SoundscapeTextPanel->RequestFocus();
g_SoundscapeTextPanel->Set(m_KeyValues);
return;
}


m_SoundNameTextEntry->SetEnabled(false);
//check for new soundscape
m_SoundNameTextEntry->SetText("");
else if (!Q_strcmp(pszCommand, NEW_BUTTON_COMMAND))
{
//make sure you want to create a new soundscape file
vgui::QueryBox* popup = new vgui::QueryBox("New File?", "Are you sure you want to create a new soundscape file?", this);
popup->SetOKCommand(new KeyValues("Command", "command", RESET_BUTTON_COMMAND));
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


m_SoundNamePlay->SetEnabled(false);
return;
}


if (g_SoundPanel)
//check for reset soundscape
{
else if (!Q_strcmp(pszCommand, RESET_BUTTON_COMMAND))
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
{
g_SoundPanel->SetVisible(false);
m_kvCurrSelected = nullptr;
}


//check for current keyvalues. should never bee nullptr but could be
//stop all soundscapes before deleting the old soundscapes
if (!m_kvCurrSelected)
if (g_IsPlayingSoundscape)
{
PlaySelectedSoundscape();
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//show error
m_KeyValues->deleteThis();
char buf[1028];
m_KeyValues = new KeyValues("Empty Soundscape");
Q_snprintf(buf, sizeof(buf), "Failed to find KeyValue subkey \"%s\"\nfor current soundscape file!", pszCommand);


//show an error
//reset title
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
SetTitle("Soundscape Maker (New File)", true);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


//reset vars
LoadFile(m_KeyValues);
m_pCurrentSelected = nullptr;
return;
}


m_TextEntryName->SetEnabled(false);
//check for play sound
m_TextEntryName->SetText("");
else if (!Q_strcmp(pszCommand, SOUNDS_LIST_BUTTON_COMMAND))
{
//initalize the sounds
static bool g_SoundPanelInitalized = false;
if (!g_SoundPanelInitalized)
{
g_SoundPanelInitalized = true;
g_SoundPanel->InitalizeSounds();
}


m_DspEffects->SetEnabled(false);
//get sound text entry name
m_DspEffects->SetText("");
char buf[512];
return;
m_SoundNameTextEntry->GetText(buf, sizeof(buf));
}


if (g_IsPlayingSoundscape)
//check the current mode
PlaySelectedSoundscape();
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
{
g_SoundPanel->SetIsUsingSoundPanel(false);


m_DeleteCurrentButton->SetEnabled(true);
//load all the temporary soundscapes
CUtlVector<const char*> OtherSoundscapes;
for (KeyValues* curr = m_KeyValues; curr; curr = curr->GetNextKey())
{
if (curr == m_kvCurrSelected)
continue;


//set current soundscape name
OtherSoundscapes.AddToTail(curr->GetName());
m_TextEntryName->SetText(pszCommand);
}
m_TextEntryName->SetEnabled(true);
m_pDataList->m_Keyvalues = m_kvCurrSelected;


//set dsp effect
g_SoundPanel->InitalizeSoundscapes(OtherSoundscapes);
int dsp = Clamp<int>(m_kvCurrSelected->GetInt("dsp"), 0, 29);
}
else
{
g_SoundPanel->SetIsUsingSoundPanel(true);


m_PlaySoundscapeButton->SetEnabled(true);
//look for item with same name
for (int i = 0; i < g_SoundDirectories.Count(); i++)
{
if (!Q_strcmp(buf, g_SoundDirectories[i]))
{
//select item
g_SoundPanel->m_SoundsList->ActivateItem(i);
g_SoundPanel->m_SoundsList->SetText(buf);


m_DspEffects->SetEnabled(true);
break;
m_DspEffects->ActivateItem(dsp);
}
}
}


//clear these
g_SoundPanel->SetVisible(true);
m_pDataList->Clear();
g_SoundPanel->MoveToFront();
m_pSoundList->Clear();
g_SoundPanel->RequestFocus();
m_pSoundList->m_Keyvalues = nullptr;
return;
}


//set variables
//check for play soundscape
int RandomNum = 0;
else if (!Q_strcmp(pszCommand, PLAY_SOUNDSCAPE_COMMAND))
int LoopingNum = 0;
{
int SoundscapeNum = 0;
if (m_PlaySoundscapeButton->IsSelected())
 
{
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, data)
//enable the reset soundscape button
{
m_ResetSoundscapeButton->SetEnabled(true);
//store data name
 
const char* name = data->GetName();
//play the soundscape
PlaySelectedSoundscape();
}
else
{
//disable the reset soundscape button
m_ResetSoundscapeButton->SetEnabled(false);
 
g_IsPlayingSoundscape = false;


//increment variables based on name
//stop all sounds and soundscapes
if (!Q_strcasecmp(name, "playrandom"))
enginesound->StopAllSounds(true);
{
g_SoundscapeSystem.StartNewSoundscape(nullptr);
RandomNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playrandom%d", RandomNum), this);
}
if (!Q_strcasecmp(name, "playlooping"))
{
LoopingNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playlooping%d", LoopingNum), this);
}
if (!Q_strcasecmp(name, "playsoundscape"))
{
SoundscapeNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playsoundscape%d", SoundscapeNum), this);
}
}
}
}
return;
}
//check for play soundscape
else if (!Q_strcmp(pszCommand, RESET_SOUNDSCAPE_BUTTON_COMMAND))
{
PlaySelectedSoundscape();
return;
}
}


BaseClass::OnCommand(pszCommand);
}


//-----------------------------------------------------------------------------
//check for delete item
// Purpose: Function to recursivly write keyvalues to keyvalue files. the keyvalues
else if (!Q_strcmp(pszCommand, DELETE_CURRENT_ITEM_COMMAND))
// class does have a function to do this BUT this function writes every single
{
// item one after another. this function does that but writes the keys
//check for current rndwave
// first then the subkeys so the order is good.
if (m_kvCurrRndwave && m_SoundNameTextEntry->IsEnabled())
//-----------------------------------------------------------------------------
{
void RecursivlyWriteKeyvalues(KeyValues* prev, CUtlBuffer& buffer, int& indent)
if (!m_kvCurrRndwave || m_iCurrRndWave <= 0)
{
return;
//write \t indent
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');


//write name
//get the keyvalues by the index
buffer.PutChar('"');
int curr = 0;
buffer.PutString(prev->GetName());
KeyValues* prev = nullptr;
buffer.PutString("\"\n");


//write {
FOR_EACH_VALUE(m_kvCurrRndwave, keyvalues)
for (int i = 0; i < indent; i++)
{
buffer.PutChar('\t');
if (++curr == m_iCurrRndWave)
{
//delete
if (prev)
prev->SetNextKey(keyvalues->GetNextValue());
else
{
m_kvCurrRndwave->m_pSub = keyvalues->GetNextValue();
m_iCurrRndWave = -1;
}


buffer.PutString("{\n");
curr = curr - 1;


//increment indent
keyvalues->SetNextKey(nullptr);
indent++;
keyvalues->deleteThis();
break;
}


//write all the keys first
FOR_EACH_VALUE(prev, value)
{
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');


//write name and value
buffer.PutChar('"');
buffer.PutString(value->GetName());
buffer.PutString("\"\t");
buffer.PutChar('"');
buffer.PutString(value->GetString());
buffer.PutString("\"\n");
}
//write all the subkeys now
FOR_EACH_TRUE_SUBKEY(prev, value)
{
//increment indent
RecursivlyWriteKeyvalues(value, buffer, indent);
if (value->GetNextTrueSubKey())
buffer.PutChar('\n');
}


//decrement indent
prev = keyvalues;
indent--;
}


//write ending }
//reset everything
for (int i = 0; i < indent; i++)
m_SoundNameTextEntry->SetText("");
buffer.PutChar('\t');
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


buffer.PutString("}\n");
//store vector
}
auto& vec = m_pSoundList->m_MenuButtons;


//-----------------------------------------------------------------------------
//remove it
// Purpose: Called when a file gets opened/closed
delete vec[curr];
//-----------------------------------------------------------------------------
vec.Remove(curr);
void CSoundscapeMaker::OnFileSelected(const char* pszFileName)
{
//check for null or empty string
if (!pszFileName || pszFileName[0] == '\0')
return;


//check for file save
//move everything down
if (!m_bWasFileLoad)
m_pSoundList->m_iCurrentY = m_pSoundList->m_iCurrentY - 22;
{
m_pSoundList->m_Keyvalues = m_kvCurrRndwave;
//save the file
if (m_KeyValues)
{
//write everything into a buffer
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
buf.PutString("//------------------------------------------------------------------------------------\n");
buf.PutString("//\n");
buf.PutString("// Auto-generated soundscape file created with modbases soundscape tool'\n");
buf.PutString("//\n");
buf.PutString("//------------------------------------------------------------------------------------\n");


//now write the keyvalues
if (vec.Count() >= m_pSoundList->m_iMax)
KeyValues* pCurrent = m_KeyValues;
while (pCurrent)
{
{
int indent = 0;
m_pSoundList->OnMouseWheeled(1);
RecursivlyWriteKeyvalues(pCurrent, buf, indent);
//put a newline
if (pCurrent->GetNextTrueSubKey())
buf.PutChar('\n');


//get next
int min, max;
pCurrent = pCurrent->GetNextTrueSubKey();
m_pSoundList->m_pSideSlider->GetRange(min, max);
}  
m_pSoundList->m_pSideSlider->SetRange(0, max - 1);
}


if (!g_pFullFileSystem->WriteFile(pszFileName, "MOD", buf))
for (int i = curr; i < vec.Count(); i++)
{
{
//play an error sound
//move everything down
vgui::surface()->PlaySound("resource/warning.wav");
int x, y = 0;
 
vec[i]->GetPos(x, y);
//get the error first
vec[i]->SetPos(x, y - 22);
char buf[1028];
}
Q_snprintf(buf, sizeof(buf), "Failed to save soundscape to file \"%s\"", pszFileName);
 
//reset every command
int WaveAmount = 0;
for (int i = 0; i < vec.Count(); i++)
{
//store data name
const char* name = vec[i]->GetCommand()->GetString("command");


//show an error
//increment variables based on name
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
if (Q_stristr(name, "$rndwave") == name)
popup->SetOKButtonText("Ok");
{
popup->SetCancelButtonVisible(false);
WaveAmount++;
popup->AddActionSignalTarget(this);
vec[i]->SetCommand(CFmtStr("$rndwave%d", WaveAmount));
popup->DoModal(this);
}
return;
}
}
}


//bounds check
if (vec.Count() <= 0)
{
m_kvCurrRndwave = nullptr;
m_pSoundList->m_Keyvalues = nullptr;


//store vars
//restart soundscape
const char* last = pszFileName;
PlaySelectedSoundscape();
const char* tmp = nullptr;


//get the last /
return;
while ((last = Q_strstr(last, "\\")) != nullptr)
}
tmp = ++last; //move past the backslash


//check tmp
//select next item
if (!tmp || !*tmp)
if (m_iCurrRndWave <= vec.Count())
tmp = pszFileName;
OnCommand(CFmtStr("$rndwave%d", curr + 1));
else
OnCommand(CFmtStr("$rndwave%d", curr));
}
else if (m_kvCurrSound)
{
//find keyvalue with same pointer and get the index
int tmpindex = 0;
int index = -1;


//set new title
KeyValues* prev = nullptr;
char buf[1028];
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, keyvalues)
Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);
{
if (m_kvCurrSound == keyvalues)
{
//remove it
if (prev)
prev->SetNextKey(keyvalues->GetNextTrueSubKey());
else
m_kvCurrSelected->m_pSub = keyvalues->GetNextTrueSubKey();


SetTitle(buf, true);
keyvalues->SetNextKey(nullptr);
keyvalues->deleteThis();


//create copy of pszFileName
//get index
char manifest[1028];
index = tmpindex;
Q_strncpy(manifest, pszFileName, sizeof(manifest));
break;
}


//get last /
prev = keyvalues;
char* lastSlash = Q_strrchr(manifest, '\\');
if (lastSlash == nullptr || *lastSlash == '\0')
return;


//append 'soundscapes_manifest.txt'
//increment
lastSlash[1] = '\0';
tmpindex++;
strcat(manifest, "soundscapes_manifest.txt");
}


//see if we can open manifest file
//error
KeyValues* man_file = new KeyValues("manifest");
if (index == -1)
if (!man_file->LoadFromFile(g_pFullFileSystem, manifest))
return;
{
//cant open manifest file
man_file->deleteThis();
return;
}


//get real filename
//store vector
pszFileName = Q_strrchr(pszFileName, '\\');
auto& vec = m_pDataList->m_MenuButtons;
if (!pszFileName || !*pszFileName)
{
man_file->deleteThis();
return;
}


pszFileName = pszFileName + 1;
//remove it
delete vec[index];
vec.Remove(index);


//create name to be added to the manifest file
//move everything down
char add_file[1028];
m_pDataList->m_iCurrentY = m_pDataList->m_iCurrentY - 22;
Q_snprintf(add_file, sizeof(add_file), "scripts/%s", pszFileName);
m_pDataList->m_Keyvalues = m_kvCurrSelected;


//add filename to manifest file if not found
for (int i = index; i < vec.Count(); i++)
FOR_EACH_VALUE(man_file, value)
{
if (!Q_strcmp(value->GetString(), add_file))
{
{
man_file->deleteThis();
//move everything down
return;
int x, y = 0;
vec[i]->GetPos(x, y);
vec[i]->SetPos(x, y - 22);
}
}
}


if (vec.Count() >= m_pDataList->m_iMax)
{
m_pDataList->OnMouseWheeled(1);


//add to manifest file
int min, max;
KeyValues* kv = new KeyValues("file");
m_pDataList->m_pSideSlider->GetRange(min, max);
kv->SetString(nullptr, add_file);
man_file->AddSubKey(kv);


//write to file
if (max > 0)
man_file->SaveToFile(g_pFullFileSystem, manifest);
m_pDataList->m_pSideSlider->SetRange(0, max - 1);
else
m_pDataList->m_pSideSlider->SetRange(0, 0);
}


man_file->deleteThis();
//reset the names of each button
return;
int RandomNum = 0;
}
int LoopingNum = 0;
int SoundscapeNum = 0;


//try and load the keyvalues file first
//change the commands of the buttons
KeyValues* temp = new KeyValues("SoundscapeFile");
for (int i = 0; i < vec.Count(); i++)
if (!temp->LoadFromFile(filesystem, pszFileName))
{
{
//store data name
//play an error sound
const char* name = vec[i]->GetCommand()->GetString("command");
vgui::surface()->PlaySound("resource/warning.wav");
//get the error first
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Failed to open keyvalues file \"%s\"", pszFileName);


//show an error
//increment variables based on name
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
if (Q_stristr(name, "$playrandom") == name)
popup->SetOKButtonText("Ok");
{
popup->SetCancelButtonVisible(false);
RandomNum++;
popup->AddActionSignalTarget(this);
vec[i]->SetCommand(CFmtStr("$playrandom%d", RandomNum));
popup->DoModal(this);
}


temp->deleteThis();
if (Q_stristr(name, "$playlooping") == name)
return;
{
}
LoopingNum++;
vec[i]->SetCommand(CFmtStr("$playlooping%d", LoopingNum));
}


//set the new title
if (Q_stristr(name, "$playsoundscape") == name)
{
{
//store vars
SoundscapeNum++;
const char* last = pszFileName;
vec[i]->SetCommand(CFmtStr("$playsoundscape%d", SoundscapeNum));
const char* tmp = nullptr;
}
}


//get the last /
//reset everything
while ((last = Q_strstr(last, "\\")) != nullptr)
m_SoundLevels->SetText("");
tmp = ++last; //move past the backslash
m_SoundNameTextEntry->SetText("");
m_TimeTextEntry->SetText("");
m_PitchTextEntry->SetText("");
m_PositionTextEntry->SetText("");
m_VolumeTextEntry->SetText("");


//check tmp
m_SoundLevels->SetEnabled(false);
if (!tmp || !*tmp)
m_SoundNameTextEntry->SetEnabled(false);
tmp = pszFileName;
m_TimeTextEntry->SetEnabled(false);
m_PitchTextEntry->SetEnabled(false);
m_PositionTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


//create the new new title
m_pSoundList->Clear();
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);


SetTitle(buf, true);
m_kvCurrSound = nullptr;
}
m_pSoundList->m_Keyvalues = nullptr;


//stop all soundscapes before deleting the old soundscapes
//bounds checking
m_kvCurrSelected = nullptr;
if (index >= vec.Count())
index = vec.Count() - 1; // fix bounds more safely


if (g_IsPlayingSoundscape)
//select the button
PlaySelectedSoundscape();
if (index >= 0)
OnCommand(vec[index]->GetCommand()->GetString("command"));
}
else if (m_kvCurrSelected)
{
if (m_KeyValues == m_kvCurrSelected)
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//delete and set the old keyvalues
//show an error
if (m_KeyValues)
vgui::QueryBox* popup = new vgui::QueryBox("Error", "Can not delete base soundscape!", this);
m_KeyValues->deleteThis();
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);


m_KeyValues = temp;
return;
}


//load the file
//find keyvalue with same pointer and get the index
LoadFile(m_KeyValues);
int tmpindex = 0;
}
int index = -1;


//-----------------------------------------------------------------------------
KeyValues* prev = nullptr;
// Purpose: Called when a text thing changes
for (KeyValues* keyvalues = m_KeyValues; keyvalues != nullptr; keyvalues = keyvalues->GetNextTrueSubKey())
//-----------------------------------------------------------------------------
{
void CSoundscapeMaker::OnTextChanged(KeyValues* keyvalues)
if (m_kvCurrSelected == keyvalues)
{
{
//check for these things
//remove it
if (!m_pCurrentSelected || !m_kvCurrSelected)
if (!prev)
return;
break;


//check to see if the current focus is the text text entry
prev->SetNextKey(keyvalues->GetNextTrueSubKey());
if (m_TextEntryName->HasFocus())
keyvalues->SetNextKey(nullptr);
{
keyvalues->deleteThis();
//get text
char buf[50];
m_TextEntryName->GetText(buf, sizeof(buf));


//set current text and keyvalue name
//get index
m_kvCurrSelected->SetName(buf);
index = tmpindex;
m_pCurrentSelected->SetText(buf);
break;
m_pCurrentSelected->SetCommand(buf);
}
return;
}


//set dsp
prev = keyvalues;
m_kvCurrSelected->SetInt("dsp", Clamp<int>(m_DspEffects->GetActiveItem(), 0, 28));
//if the m_kvCurrSound is nullptr then dont do the rest of the stuff
if (!m_kvCurrSound)
return;


//set the curr sound and stuff
//increment
if (m_TimeTextEntry->HasFocus())
tmpindex++;
{
}
//get text
char buf[38];
m_TimeTextEntry->GetText(buf, sizeof(buf));


m_kvCurrSound->SetString("time", buf);
//error
return;
if (index == -1)
}
return;
else if (m_VolumeTextEntry->HasFocus())
{
//get text
char buf[38];
m_VolumeTextEntry->GetText(buf, sizeof(buf));


m_kvCurrSound->SetString("volume", buf);
//store vector
return;
auto& vec = m_SoundscapesList->m_MenuButtons;
}
else if (m_PitchTextEntry->HasFocus())
{
//dont add to soundscaep
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
return;


//get text
//remove it
char buf[38];
delete vec[index];
m_PitchTextEntry->GetText(buf, sizeof(buf));
vec.Remove(index);


m_kvCurrSound->SetString("pitch", buf);
//move everything down
return;
m_SoundscapesList->m_iCurrentY = m_SoundscapesList->m_iCurrentY - 22;
}
else if (m_PositionTextEntry->HasFocus())
{
//get text
char buf[38];
m_PositionTextEntry->GetText(buf, sizeof(buf));


//if the string is empty then remove the position instead
for (int i = index; i < vec.Count(); i++)
if (!buf[0])
{
{
//move everything down
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
int x, y = 0;
m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("positionoverride"));
vec[i]->GetPos(x, y);
else
vec[i]->SetPos(x, y - 22);
m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("position"));
}
}
else
{
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
m_kvCurrSound->SetString("positionoverride", buf);
else
m_kvCurrSound->SetString("position", buf);


}
if (vec.Count() >= m_SoundscapesList->m_iMax)
{
m_SoundscapesList->OnMouseWheeled(1);


return;
int min, max;
}
m_SoundscapesList->m_pSideSlider->GetRange(min, max);
m_SoundscapesList->m_pSideSlider->SetRange(0, max - 1);
}


//get the sound level amount
//reset everything
int sndlevel = Clamp<int>(m_SoundLevels->GetActiveItem(), 0, 20);
m_DspEffects->SetText("");
m_kvCurrSound->SetString("soundlevel", g_SoundLevels[sndlevel]);
m_SoundLevels->SetText("");
m_TextEntryName->SetText("");
m_SoundNameTextEntry->SetText("");
m_TimeTextEntry->SetText("");
m_PitchTextEntry->SetText("");
m_PositionTextEntry->SetText("");
m_VolumeTextEntry->SetText("");


//set soundscape name/wave
m_DspEffects->SetEnabled(false);
if (m_SoundNameTextEntry->HasFocus())
m_SoundLevels->SetEnabled(false);
{
m_TextEntryName->SetEnabled(false);
//get text
m_SoundNameTextEntry->SetEnabled(false);
char buf[512];
m_TimeTextEntry->SetEnabled(false);
m_SoundNameTextEntry->GetText(buf, sizeof(buf));
m_PitchTextEntry->SetEnabled(false);
m_PositionTextEntry->SetEnabled(false);
if (m_iSoundscapeMode == SoundscapeMode::Mode_Looping)
m_VolumeTextEntry->SetEnabled(false);
m_kvCurrSound->SetString("wave", buf);
m_SoundNamePlay->SetEnabled(false);
else if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
 
m_kvCurrSound->SetString("name", buf);
m_pDataList->Clear();
else if (m_iSoundscapeMode == SoundscapeMode::Mode_Random && m_kvCurrRndwave)
m_pSoundList->Clear();
{
 
//get value
m_kvCurrSound = nullptr;
int i = 0;
m_pDataList->m_Keyvalues = nullptr;


FOR_EACH_VALUE(m_kvCurrRndwave, wave)
//go to next soundscape
if (!prev)
{
{
if (++i == m_iCurrRndWave)
//restart soundscape
{
PlaySelectedSoundscape();
wave->SetStringValue(buf);
return;
}


//set text on the sounds panel
if (prev->GetNextTrueSubKey())
vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
OnCommand(prev->GetNextTrueSubKey()->GetName());
if (button)
else
{
OnCommand(prev->GetName());
//get last / or \ and make the string be that + 1
}
char* fslash = Q_strrchr(buf, '/');
char* bslash = Q_strrchr(buf, '\\');


//no forward slash and no back slash
//restart soundscape
if (!fslash && !bslash)
PlaySelectedSoundscape();
{
return;
button->SetText(buf);
}
return;
}
 
if (fslash > bslash)
{
button->SetText(fslash + 1);
return;
}
else if (bslash > fslash)
{
button->SetText(bslash + 1);
return;
}
}


break;
//check for "playrandom", "playsoundscape" or "playlooping"
}
if (Q_stristr(pszCommand, "$playrandom") == pszCommand)
{
//get the selected number
char* str_number = (char*)(pszCommand + 11);
int number = atoi(str_number);
if (number != 0)
{
//look for button with same command
auto& vec = m_pDataList->m_MenuButtons;
for (int i = 0; i < vec.Count(); i++)
{
//if the button doesnt have the same command then de-select it. else select it
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
vec[i]->m_bIsSelected = true;
else
vec[i]->m_bIsSelected = false;
}
}
}


return;
}
}


//-----------------------------------------------------------------------------
//clear the m_pSoundList
// Purpose: Loads the keyvalues
m_pSoundList->Clear();
//-----------------------------------------------------------------------------
m_pSoundList->m_Keyvalues = nullptr;
void CSoundscapeMaker::LoadFile(KeyValues* file)
 
{
//store variables
//clear all the text's
KeyValues* data = nullptr;
m_TextEntryName->SetEnabled(false);
int curr = 0;
m_TextEntryName->SetText("");


m_DspEffects->SetEnabled(false);
//get subkey
m_DspEffects->SetText("");
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
{
if (Q_strcasecmp(sounds->GetName(), "playrandom"))
continue;


m_TimeTextEntry->SetEnabled(false);
if (++curr == number)
m_TimeTextEntry->SetText("");
{
data = sounds;
m_VolumeTextEntry->SetEnabled(false);
break;
m_VolumeTextEntry->SetText("");
}
}
m_PitchTextEntry->SetEnabled(false);
m_PitchTextEntry->SetText("");
m_PositionTextEntry->SetEnabled(false);
m_PositionTextEntry->SetText("");
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNameTextEntry->SetText("");


m_SoundNamePlay->SetEnabled(false);
//no data
if (!data)
if (g_SoundPanel)
return;
{
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
g_SoundPanel->SetVisible(false);
}


m_PlaySoundscapeButton->SetEnabled(false);
m_kvCurrSound = data;
m_PlaySoundscapeButton->SetSelected(false);
m_kvCurrRndwave = nullptr;


m_ResetSoundscapeButton->SetEnabled(false);
//set the random times
m_DeleteCurrentButton->SetEnabled(false);
m_TimeTextEntry->SetText(data->GetString("time", "10,20"));
m_VolumeTextEntry->SetText(data->GetString("volume", "0.5,0.8"));
m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
m_PositionTextEntry->SetText(data->GetString("position", ""));
m_SoundNameTextEntry->SetText("");


//clear current file
//get snd level index
m_pCurrentSelected = nullptr;
int index = 8; //8 = SNDLVL_NORM
m_kvCurrSelected = nullptr;
const char* name = data->GetString("soundlevel", nullptr);
m_kvCurrSound = nullptr;
m_kvCurrRndwave = nullptr;


//clear the menu items
//check for the name
m_SoundscapesList->Clear();
if (name)
m_pSoundList->Clear();
{
m_pDataList->Clear();
m_SoundscapesList->m_Keyvalues = file;
m_pDataList->m_Keyvalues = nullptr;
m_pSoundList->m_Keyvalues = nullptr;


g_IsPlayingSoundscape = false;
//loop through the sound levels to find the right one
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
{
if (!Q_strcmp(name, g_SoundLevels[i]))
{
index = i;
break;
}
}
}


//temp soundscapes list
//select the index
CUtlVector<const char*> Added;
m_SoundLevels->ActivateItem(index);


//add all the menu items
//enable the text entries
for (KeyValues* soundscape = file; soundscape != nullptr; soundscape = soundscape->GetNextTrueSubKey())
m_TimeTextEntry->SetEnabled(true);
{
m_VolumeTextEntry->SetEnabled(true);
//add the menu buttons
m_PitchTextEntry->SetEnabled(true);
const char* name = soundscape->GetName();
m_PositionTextEntry->SetEnabled(true);
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(false);


//check for the soundscape first
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
if (Added.Find(name) != Added.InvalidIndex())
g_SoundPanel->SetVisible(false);
{
ConWarning("CSoundscapePanel: Failed to add repeated soundscape '%s'\n", name);
continue;
}


Added.AddToTail(name);
//check for randomwave subkey
m_SoundscapesList->AddButton(name, name, name, this);
if ((data = data->FindKey("rndwave")) == nullptr)
}
return;


m_SoundscapesList->m_pSideSlider->SetValue(0);
m_kvCurrRndwave = data;
m_SoundscapesList->ScrollBarMoved(0);
m_pSoundList->m_Keyvalues = data;


//
//add all the data
OnCommand(file->GetName());
int i = 0;
}
FOR_EACH_VALUE(data, sound)
{
const char* name = sound->GetName();


//-----------------------------------------------------------------------------
//get real text
// Purpose: Sets the sounds text
const char* text = sound->GetString();
//-----------------------------------------------------------------------------
void CSoundscapeMaker::SetSoundText(const char* text)
{
m_SoundNameTextEntry->SetText(text);


//set soundscape name/wave
//get last / or \ and make the string be that + 1
if (m_iSoundscapeMode != SoundscapeMode::Mode_Random)
char* fslash = Q_strrchr(text, '/');
{
char* bslash = Q_strrchr(text, '\\');
m_kvCurrSound->SetString("wave", text);
}
else
{
//get value
int i = 0;


FOR_EACH_VALUE(m_kvCurrRndwave, wave)
//no forward slash and no back slash
{
if (!fslash && !bslash)
if (++i == m_iCurrRndWave)
{
{
text = text;
wave->SetStringValue(text);
}
 
else
//set text on the sounds panel
vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
if (button)
{
{
//get last / or \ and make the string be that + 1
char* fslash = Q_strrchr(text, '/');
char* bslash = Q_strrchr(text, '\\');
//no forward slash and no back slash
if (!fslash && !bslash)
{
button->SetText(text);
return;
}
if (fslash > bslash)
if (fslash > bslash)
{
text = fslash + 1;
button->SetText(fslash + 1);
return;
}


else if (bslash > fslash)
else if (bslash > fslash)
{
text = bslash + 1;
button->SetText(bslash + 1);
return;
}
}
}


break;
m_pSoundList->AddButton(name, text, CFmtStr("$rndwave%d", ++i), this, sound, SoundscapeClipboardType::Type_SoundscapeRandomWave);
}
}
}
}


return;
m_iSoundscapeMode = SoundscapeMode::Mode_Random;
}
return;
 
//-----------------------------------------------------------------------------
// Purpose: Called when a keyboard key is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnKeyCodePressed(vgui::KeyCode code)
{
//check for ctrl o or ctrl s
if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) ||
vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL))
{
if (code == vgui::KeyCode::KEY_O)
OnCommand(LOAD_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_S)
OnCommand(SAVE_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_N)
OnCommand(NEW_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_P)
{
//show settings
g_SettingsPanel->SetVisible(true);
g_SettingsPanel->RequestFocus();
g_SettingsPanel->MoveToFront();
}
}
else if (code == vgui::KeyCode::KEY_D)
OnCommand(DELETE_CURRENT_ITEM_COMMAND);
//check for ctrl+alt+a
else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LALT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RALT)) && m_pSoundList && m_pSoundList->m_Keyvalues)
m_pSoundList->OnCommand(NEW_RNDWAVE_WAVE_COMMAND);
//check for just ctrl+shift+a
else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT)) && m_SoundscapesList)
m_SoundscapesList->OnCommand(ADD_SOUNDSCAPE_COMMAND);
return;
}
}
 
else if (Q_stristr(pszCommand, "$playlooping") == pszCommand)
//check for arrow keys
if (code == KEY_DOWN || code == KEY_UP)
{
{
if (m_kvCurrRndwave)
//get the selected number
char* str_number = (char*)(pszCommand + 12);
int number = atoi(str_number);
if (number != 0)
{
{
m_pSoundList->OnKeyCodePressed(code);
//look for button with same command
}
auto& vec = m_pDataList->m_MenuButtons;
else if (m_kvCurrSelected && m_pDataList->m_MenuButtons.Count())
for (int i = 0; i < vec.Count(); i++)
{
{
m_pDataList->OnKeyCodePressed(code);
//if the button doesnt have the same command then de-select it. else select it
}
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
else
vec[i]->m_bIsSelected = true;
{
else
m_SoundscapesList->OnKeyCodePressed(code);
vec[i]->m_bIsSelected = false;
}
}


return;
}


//get key bound to this
//clear the m_pSoundList
const char* key = engine->Key_LookupBinding("modbase_soundscape_panel");
m_pSoundList->Clear();
if (!key)
m_pSoundList->m_Keyvalues = nullptr;
return;


//convert the key to a keyboard code
//store variables
const char* keystring = KeyCodeToString(code);
KeyValues* data = nullptr;
int curr = 0;
//remove the KEY_ if found
if (Q_strstr(keystring, "KEY_") == keystring)
keystring = keystring + 4;


//check both strings
//get subkey
if (!Q_strcasecmp(key, keystring))
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
OnClose();
{
}
if (Q_strcasecmp(sounds->GetName(), "playlooping"))
continue;


//-----------------------------------------------------------------------------
if (++curr == number)
// Purpose: Starts the soundscape on map spawn
{
//-----------------------------------------------------------------------------
data = sounds;
void CSoundscapeMaker::LevelInitPostEntity()
break;
{
}
if (g_IsPlayingSoundscape)
}
PlaySelectedSoundscape();
}


//-----------------------------------------------------------------------------
//no data
// Purpose: Sets keyvalues from text
if (!data)
//-----------------------------------------------------------------------------
return;
void CSoundscapeMaker::Set(const char* buffer)
{
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
buf.PutString(buffer);


//try and load the keyvalues file first
m_kvCurrSound = data;
KeyValues* temp = new KeyValues("SoundscapeFile");
m_kvCurrRndwave = nullptr;
if (!temp->LoadFromBuffer("Text Editor Panel Buffer", buf))
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");


//show an error
//set the random times
vgui::QueryBox* popup = new vgui::QueryBox("Error", "Failed to open keyvalues data from \"Text Editor Panel\"", this);
m_TimeTextEntry->SetText("");
popup->SetOKButtonText("Ok");
m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
popup->SetCancelButtonVisible(false);
m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
popup->AddActionSignalTarget(this);
m_PositionTextEntry->SetText(data->GetString("position", ""));
popup->DoModal(this);
m_SoundNameTextEntry->SetText(data->GetString("wave", ""));


temp->deleteThis();
//get snd level index
return;
int index = 8; //8 = SNDLVL_NORM
}
const char* name = data->GetString("soundlevel", nullptr);


//stop all soundscapes before deleting the old soundscapes
//check for the name
m_kvCurrSelected = nullptr;
if (name)
{


if (g_IsPlayingSoundscape)
//loop through the sound levels to find the right one
PlaySelectedSoundscape();
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
{
if (!Q_strcmp(name, g_SoundLevels[i]))
{
index = i;
break;
}
}
}


//delete and set the old keyvalues
//select the index
if (m_KeyValues)
m_SoundLevels->ActivateItem(index);
m_KeyValues->deleteThis();


m_KeyValues = temp;
//enable the text entries
m_TimeTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetEnabled(true);
m_PitchTextEntry->SetEnabled(true);
m_PositionTextEntry->SetEnabled(true);
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(true);
m_SoundNamePlay->SetEnabled(true);
g_SoundPanel->SetVisible(false);
 
m_iSoundscapeMode = SoundscapeMode::Mode_Looping;
return;
}
}
else if (Q_stristr(pszCommand, "$playsoundscape") == pszCommand)
{
//get the selected number
char* str_number = (char*)(pszCommand + 15);
int number = atoi(str_number);
if (number != 0)
{
//look for button with same command
auto& vec = m_pDataList->m_MenuButtons;
for (int i = 0; i < vec.Count(); i++)
{
//if the button doesnt have the same command then de-select it. else select it
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
vec[i]->m_bIsSelected = true;
else
vec[i]->m_bIsSelected = false;
}


//load the file
LoadFile(m_KeyValues);
}


//-----------------------------------------------------------------------------
//clear the m_pSoundList
// Purpose: Destructor for soundscape maker panel
m_pSoundList->Clear();
//-----------------------------------------------------------------------------
m_pSoundList->m_Keyvalues = nullptr;
CSoundscapeMaker::~CSoundscapeMaker()
{
//delete the keyvalue files if needed
if (m_KeyValues)
m_KeyValues->deleteThis();
}


//static panel instance
//store variables
static CSoundscapeMaker* g_SSMakerPanel = nullptr;
KeyValues* data = nullptr;
int curr = 0;


//interface class
//get subkey
class CSoundscapeMakerInterface : public ISoundscapeMaker
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
{
{
public:
if (Q_strcasecmp(sounds->GetName(), "playsoundscape"))
void Create(vgui::VPANEL parent)
continue;
{
 
g_SSMakerPanel = new CSoundscapeMaker(parent);
if (++curr == number)
g_SoundPanel = new CSoundListPanel(parent, "SoundscapeSoundListPanel");
{
g_SettingsPanel = new CSoundscapeSettingsPanel(parent, "SoundscapeSettingsPanel");
data = sounds;
g_SoundscapeTextPanel = new CSoundscapeTextPanel(parent, "SoundscapeTextPanel");
break;
g_SoundscapeDebugPanel = new CSoundscapeDebugPanel(parent, "SoundscapeDebugPanel");
}
}
}
 
//no data
if (!data)
return;
 
m_kvCurrSound = data;
m_kvCurrRndwave = nullptr;
 
//set the random times
m_TimeTextEntry->SetText("");
m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
m_PositionTextEntry->SetText(data->GetString("positionoverride", ""));
m_SoundNameTextEntry->SetText(data->GetString("name", ""));
m_PitchTextEntry->SetText("");
 
//get snd level index
int index = 8; //8 = SNDLVL_NORM
const char* name = data->GetString("soundlevel", nullptr);


void SetVisible(bool bVisible)
//check for the name
{
if (name)
if (g_SSMakerPanel)
{
g_SSMakerPanel->SetVisible(bVisible);
}


void Destroy()
//loop through the sound levels to find the right one
for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
{
if (!Q_strcmp(name, g_SoundLevels[i]))
{
index = i;
break;
}
}
}
 
//select the index
m_SoundLevels->ActivateItem(index);
 
//enable the text entries
m_TimeTextEntry->SetEnabled(true);
m_VolumeTextEntry->SetEnabled(true);
m_PitchTextEntry->SetEnabled(false);
m_PositionTextEntry->SetEnabled(true);
m_SoundLevels->SetEnabled(true);
m_SoundNameTextEntry->SetEnabled(true);
m_TimeTextEntry->SetEnabled(false);
m_SoundNamePlay->SetEnabled(true);
 
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
g_SoundPanel->SetVisible(false);
 
m_iSoundscapeMode = SoundscapeMode::Mode_Soundscape;
return;
}
}
else if (Q_stristr(pszCommand, "$rndwave") == pszCommand)
{
{
if (g_SSMakerPanel)
if (!m_kvCurrRndwave)
g_SSMakerPanel->DeletePanel();
return;
 
 
if (g_SoundPanel)
//get the selected number
g_SoundPanel->DeletePanel();
char* str_number = (char*)(pszCommand + 8);
 
m_iCurrRndWave = atoi(str_number);
if (g_SettingsPanel)
if (m_iCurrRndWave != 0)
g_SettingsPanel->DeletePanel();
{
 
//look for button with same command
if (g_SoundscapeTextPanel)
auto& vec = m_pSoundList->m_MenuButtons;
g_SoundscapeTextPanel->DeletePanel();
for (int i = 0; i < vec.Count(); i++)
{
if (g_SoundscapeDebugPanel)
//if the button doesnt have the same command then de-select it. else select it
g_SoundscapeDebugPanel->DeletePanel();
if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
 
vec[i]->m_bIsSelected = true;
g_SSMakerPanel = nullptr;
else
g_SoundPanel = nullptr;
vec[i]->m_bIsSelected = false;
g_SettingsPanel = nullptr;
}
g_SoundscapeTextPanel = nullptr;
 
g_SoundscapeDebugPanel = nullptr;
 
int i = 0;
 
//get value
KeyValues* curr = nullptr;
FOR_EACH_VALUE(m_kvCurrRndwave, wave)
{
if (++i == m_iCurrRndWave)
{
curr = wave;
break;
}
}
 
//if no curr then throw an error
if (!curr)
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");
 
//show error
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Failed to get rndwave '%d' for subkey \"%s\"\nfor current soundscape file!", i, m_kvCurrSelected->GetName());
 
//show an error
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
return;
}
 
m_SoundNameTextEntry->SetEnabled(true);
m_SoundNameTextEntry->SetText(curr->GetString());
 
m_SoundNamePlay->SetEnabled(true);
 
m_iSoundscapeMode = SoundscapeMode::Mode_Random;
return;
}
}
 
//look for button with the same name as the command
{
//store vars
CUtlVector<CSoundscapeButton*>& array = m_SoundscapesList->m_MenuButtons;
 
//de-select button
if (m_pCurrentSelected)
m_pCurrentSelected->m_bIsSelected = false;
 
//check for name
for (int i = 0; i < m_SoundscapesList->m_MenuButtons.Size(); i++)
{
//check button name
if (!Q_strcmp(array[i]->GetCommand()->GetString("command"), pszCommand))
{
//found it
m_pCurrentSelected = array[i];
break;
}
}
 
//set needed stuff
if (m_pCurrentSelected)
{
//select button
m_pCurrentSelected->m_bIsSelected = true;
m_DeleteCurrentButton->SetEnabled(false);
 
//reset the selected kv
m_kvCurrSelected = nullptr;
 
//find selected keyvalues
for (KeyValues* kv = m_KeyValues; kv != nullptr; kv = kv->GetNextTrueSubKey())
{
if (!Q_strcmp(kv->GetName(), pszCommand))
{
m_kvCurrSelected = kv;
break;
}
}
 
//set
m_kvCurrSound = nullptr;
m_kvCurrRndwave = nullptr;
 
m_TimeTextEntry->SetEnabled(false);
m_TimeTextEntry->SetText("");
 
m_VolumeTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetText("");
 
m_PitchTextEntry->SetEnabled(false);
m_PitchTextEntry->SetText("");
 
m_PositionTextEntry->SetEnabled(false);
m_PositionTextEntry->SetText("");
 
m_SoundLevels->SetEnabled(false);
m_SoundLevels->SetText("");
 
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNameTextEntry->SetText("");
 
m_SoundNamePlay->SetEnabled(false);
 
if (g_SoundPanel)
{
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
g_SoundPanel->SetVisible(false);
}
 
//check for current keyvalues. should never bee nullptr but could be
if (!m_kvCurrSelected)
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");
 
//show error
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Failed to find KeyValue subkey \"%s\"\nfor current soundscape file!", pszCommand);
 
//show an error
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
 
//reset vars
m_pCurrentSelected = nullptr;
 
m_TextEntryName->SetEnabled(false);
m_TextEntryName->SetText("");
 
m_DspEffects->SetEnabled(false);
m_DspEffects->SetText("");
return;
}
 
if (g_IsPlayingSoundscape)
PlaySelectedSoundscape();
 
m_DeleteCurrentButton->SetEnabled(true);
 
//set current soundscape name
m_TextEntryName->SetText(pszCommand);
m_TextEntryName->SetEnabled(true);
m_pDataList->m_Keyvalues = m_kvCurrSelected;
 
//set dsp effect
int dsp = Clamp<int>(m_kvCurrSelected->GetInt("dsp"), 0, 29);
 
m_PlaySoundscapeButton->SetEnabled(true);
 
m_DspEffects->SetEnabled(true);
m_DspEffects->ActivateItem(dsp);
 
//clear these
m_pDataList->Clear();
m_pSoundList->Clear();
m_pSoundList->m_Keyvalues = nullptr;
 
//set variables
int RandomNum = 0;
int LoopingNum = 0;
int SoundscapeNum = 0;
 
FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, data)
{
//store data name
const char* name = data->GetName();
 
//increment variables based on name
if (!Q_strcasecmp(name, "playrandom"))
{
RandomNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playrandom%d", RandomNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
}
 
if (!Q_strcasecmp(name, "playlooping"))
{
LoopingNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playlooping%d", LoopingNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
}
 
if (!Q_strcasecmp(name, "playsoundscape"))
{
SoundscapeNum++;
m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playsoundscape%d", SoundscapeNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
}
}
}
}
 
BaseClass::OnCommand(pszCommand);
}
 
//-----------------------------------------------------------------------------
// Purpose: Paste item from clipboard
//-----------------------------------------------------------------------------
void CSoundscapeMaker::Paste(SoundscapeClipboardType type)
{
switch (type)
{
case SoundscapeClipboardType::Type_SoundscapeName:
m_SoundscapesList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
break;
case SoundscapeClipboardType::Type_SoundscapeData:
m_pDataList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
break;
case SoundscapeClipboardType::Type_SoundscapeRandomWave:
m_pSoundList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
break;
}
}
 
//-----------------------------------------------------------------------------
// Purpose: Function to recursivly write keyvalues to keyvalue files. the keyvalues
// class does have a function to do this BUT this function writes every single
// item one after another. this function does that but writes the keys
// first then the subkeys so the order is good.
//-----------------------------------------------------------------------------
void RecursivlyWriteKeyvalues(KeyValues* prev, CUtlBuffer& buffer, int& indent)
{
//write \t indent
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');
 
//write name
buffer.PutChar('"');
buffer.PutString(prev->GetName());
buffer.PutString("\"\n");
 
//write {
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');
 
buffer.PutString("{\n");
 
//increment indent
indent++;
 
//write all the keys first
FOR_EACH_VALUE(prev, value)
{
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');
 
//write name and value
buffer.PutChar('"');
buffer.PutString(value->GetName());
buffer.PutString("\"\t");
 
buffer.PutChar('"');
buffer.PutString(value->GetString());
buffer.PutString("\"\n");
}
 
//write all the subkeys now
FOR_EACH_TRUE_SUBKEY(prev, value)
{
//increment indent
RecursivlyWriteKeyvalues(value, buffer, indent);
 
if (value->GetNextTrueSubKey())
buffer.PutChar('\n');
}
 
//decrement indent
indent--;
 
//write ending }
for (int i = 0; i < indent; i++)
buffer.PutChar('\t');
 
buffer.PutString("}\n");
}
 
//-----------------------------------------------------------------------------
// Purpose: Called when a file gets opened/closed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnFileSelected(const char* pszFileName)
{
//check for null or empty string
if (!pszFileName || pszFileName[0] == '\0')
return;
 
//check for file save
if (!m_bWasFileLoad)
{
//save the file
if (m_KeyValues)
{
//write everything into a buffer
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
buf.PutString("//------------------------------------------------------------------------------------\n");
buf.PutString("//\n");
buf.PutString("// Auto-generated soundscape file created with modbases soundscape tool'\n");
buf.PutString("//\n");
buf.PutString("//------------------------------------------------------------------------------------\n");
 
//now write the keyvalues
KeyValues* pCurrent = m_KeyValues;
while (pCurrent)
{
int indent = 0;
RecursivlyWriteKeyvalues(pCurrent, buf, indent);
 
//put a newline
if (pCurrent->GetNextTrueSubKey())
buf.PutChar('\n');
 
//get next
pCurrent = pCurrent->GetNextTrueSubKey();
}
 
if (!g_pFullFileSystem->WriteFile(pszFileName, "MOD", buf))
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");
 
//get the error first
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Failed to save soundscape to file \"%s\"", pszFileName);
 
//show an error
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
return;
}
}
 
 
//store vars
const char* last = pszFileName;
const char* tmp = nullptr;
 
//get the last /
while ((last = Q_strstr(last, "\\")) != nullptr)
tmp = ++last; //move past the backslash
 
//check tmp
if (!tmp || !*tmp)
tmp = pszFileName;
 
//set new title
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);
 
SetTitle(buf, true);
 
//create copy of pszFileName
char manifest[1028];
Q_strncpy(manifest, pszFileName, sizeof(manifest));
 
//get last /
char* lastSlash = Q_strrchr(manifest, '\\');
if (lastSlash == nullptr || *lastSlash == '\0')
return;
 
//append 'soundscapes_manifest.txt'
lastSlash[1] = '\0';
strcat(manifest, "soundscapes_manifest.txt");
 
//see if we can open manifest file
KeyValues* man_file = new KeyValues("manifest");
if (!man_file->LoadFromFile(g_pFullFileSystem, manifest))
{
//cant open manifest file
man_file->deleteThis();
return;
}
 
//get real filename
pszFileName = Q_strrchr(pszFileName, '\\');
if (!pszFileName || !*pszFileName)
{
man_file->deleteThis();
return;
}
 
pszFileName = pszFileName + 1;
 
//create name to be added to the manifest file
char add_file[1028];
Q_snprintf(add_file, sizeof(add_file), "scripts/%s", pszFileName);
 
//add filename to manifest file if not found
FOR_EACH_VALUE(man_file, value)
{
if (!Q_strcmp(value->GetString(), add_file))
{
man_file->deleteThis();
return;
}
}
 
 
//add to manifest file
KeyValues* kv = new KeyValues("file");
kv->SetString(nullptr, add_file);
man_file->AddSubKey(kv);
 
//write to file
man_file->SaveToFile(g_pFullFileSystem, manifest);
 
man_file->deleteThis();
return;
}
 
//try and load the keyvalues file first
KeyValues* temp = new KeyValues("SoundscapeFile");
if (!temp->LoadFromFile(filesystem, pszFileName))
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");
 
//get the error first
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Failed to open keyvalues file \"%s\"", pszFileName);
 
//show an error
vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
 
temp->deleteThis();
return;
}
 
//set the new title
{
//store vars
const char* last = pszFileName;
const char* tmp = nullptr;
 
//get the last /
while ((last = Q_strstr(last, "\\")) != nullptr)
tmp = ++last; //move past the backslash
 
//check tmp
if (!tmp || !*tmp)
tmp = pszFileName;
 
//create the new new title
char buf[1028];
Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);
 
SetTitle(buf, true);
}
 
//stop all soundscapes before deleting the old soundscapes
m_kvCurrSelected = nullptr;
 
if (g_IsPlayingSoundscape)
PlaySelectedSoundscape();
 
//delete and set the old keyvalues
if (m_KeyValues)
m_KeyValues->deleteThis();
 
m_KeyValues = temp;
 
//load the file
LoadFile(m_KeyValues);
}
 
//-----------------------------------------------------------------------------
// Purpose: Called when a text thing changes
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnTextChanged(KeyValues* keyvalues)
{
//check for these things
if (!m_pCurrentSelected || !m_kvCurrSelected)
return;
 
//check to see if the current focus is the text text entry
if (m_TextEntryName->HasFocus())
{
//get text
char buf[50];
m_TextEntryName->GetText(buf, sizeof(buf));
 
//set current text and keyvalue name
m_kvCurrSelected->SetName(buf);
m_pCurrentSelected->SetText(buf);
m_pCurrentSelected->SetCommand(buf);
return;
}
 
//set dsp
m_kvCurrSelected->SetInt("dsp", Clamp<int>(m_DspEffects->GetActiveItem(), 0, 28));
 
//if the m_kvCurrSound is nullptr then dont do the rest of the stuff
if (!m_kvCurrSound)
return;
 
//set the curr sound and stuff
if (m_TimeTextEntry->HasFocus())
{
//get text
char buf[38];
m_TimeTextEntry->GetText(buf, sizeof(buf));
 
m_kvCurrSound->SetString("time", buf);
return;
}
else if (m_VolumeTextEntry->HasFocus())
{
//get text
char buf[38];
m_VolumeTextEntry->GetText(buf, sizeof(buf));
 
m_kvCurrSound->SetString("volume", buf);
return;
}
else if (m_PitchTextEntry->HasFocus())
{
//dont add to soundscaep
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
return;
 
//get text
char buf[38];
m_PitchTextEntry->GetText(buf, sizeof(buf));
 
m_kvCurrSound->SetString("pitch", buf);
return;
}
else if (m_PositionTextEntry->HasFocus())
{
//get text
char buf[38];
m_PositionTextEntry->GetText(buf, sizeof(buf));
 
//if the string is empty then remove the position instead
if (!buf[0])
{
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("positionoverride"));
else
m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("position"));
}
else
{
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
m_kvCurrSound->SetString("positionoverride", buf);
else
m_kvCurrSound->SetString("position", buf);
 
}
 
return;
}
 
//get the sound level amount
int sndlevel = Clamp<int>(m_SoundLevels->GetActiveItem(), 0, 20);
m_kvCurrSound->SetString("soundlevel", g_SoundLevels[sndlevel]);
 
//set soundscape name/wave
if (m_SoundNameTextEntry->HasFocus())
{
//get text
char buf[512];
m_SoundNameTextEntry->GetText(buf, sizeof(buf));
 
if (m_iSoundscapeMode == SoundscapeMode::Mode_Looping)
m_kvCurrSound->SetString("wave", buf);
else if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
m_kvCurrSound->SetString("name", buf);
else if (m_iSoundscapeMode == SoundscapeMode::Mode_Random && m_kvCurrRndwave)
{
//get value
int i = 0;
 
FOR_EACH_VALUE(m_kvCurrRndwave, wave)
{
if (++i == m_iCurrRndWave)
{
wave->SetStringValue(buf);
 
//set text on the sounds panel
vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
if (button)
{
//get last / or \ and make the string be that + 1
char* fslash = Q_strrchr(buf, '/');
char* bslash = Q_strrchr(buf, '\\');
 
//no forward slash and no back slash
if (!fslash && !bslash)
{
button->SetText(buf);
return;
}
 
if (fslash > bslash)
{
button->SetText(fslash + 1);
return;
}
 
else if (bslash > fslash)
{
button->SetText(bslash + 1);
return;
}
}
 
break;
}
}
}
 
return;
}
}
 
//-----------------------------------------------------------------------------
// Purpose: Loads the keyvalues
//-----------------------------------------------------------------------------
void CSoundscapeMaker::LoadFile(KeyValues* file)
{
//clear all the text's
m_TextEntryName->SetEnabled(false);
m_TextEntryName->SetText("");
 
m_DspEffects->SetEnabled(false);
m_DspEffects->SetText("");
 
m_TimeTextEntry->SetEnabled(false);
m_TimeTextEntry->SetText("");
 
m_VolumeTextEntry->SetEnabled(false);
m_VolumeTextEntry->SetText("");
 
m_PitchTextEntry->SetEnabled(false);
m_PitchTextEntry->SetText("");
 
m_PositionTextEntry->SetEnabled(false);
m_PositionTextEntry->SetText("");
 
m_SoundNameTextEntry->SetEnabled(false);
m_SoundNameTextEntry->SetText("");
 
m_SoundNamePlay->SetEnabled(false);
 
if (g_SoundPanel)
{
g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
g_SoundPanel->SetVisible(false);
}
 
m_PlaySoundscapeButton->SetEnabled(false);
m_PlaySoundscapeButton->SetSelected(false);
 
m_ResetSoundscapeButton->SetEnabled(false);
m_DeleteCurrentButton->SetEnabled(false);
 
//clear current file
m_pCurrentSelected = nullptr;
m_kvCurrSelected = nullptr;
m_kvCurrSound = nullptr;
m_kvCurrRndwave = nullptr;
 
//clear the menu items
m_SoundscapesList->Clear();
m_pSoundList->Clear();
m_pDataList->Clear();
 
m_SoundscapesList->m_Keyvalues = file;
m_pDataList->m_Keyvalues = nullptr;
m_pSoundList->m_Keyvalues = nullptr;
 
g_IsPlayingSoundscape = false;
 
//temp soundscapes list
CUtlVector<const char*> Added;
 
//add all the menu items
for (KeyValues* soundscape = file; soundscape != nullptr; soundscape = soundscape->GetNextTrueSubKey())
{
//add the menu buttons
const char* name = soundscape->GetName();
 
//check for the soundscape first
if (Added.Find(name) != Added.InvalidIndex())
{
ConWarning("CSoundscapePanel: Failed to add repeated soundscape '%s'\n", name);
continue;
}
 
Added.AddToTail(name);
m_SoundscapesList->AddButton(name, name, name, this, soundscape, SoundscapeClipboardType::Type_SoundscapeName);
}
 
m_SoundscapesList->m_pSideSlider->SetValue(0);
m_SoundscapesList->ScrollBarMoved(0);
 
//
OnCommand(file->GetName());
}
 
//-----------------------------------------------------------------------------
// Purpose: Sets the sounds text
//-----------------------------------------------------------------------------
void CSoundscapeMaker::SetSoundText(const char* text)
{
m_SoundNameTextEntry->SetText(text);
 
//set soundscape name/wave
if (m_iSoundscapeMode != SoundscapeMode::Mode_Random)
{
if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
m_kvCurrSound->SetString("name", text);
else
m_kvCurrSound->SetString("wave", text);
}
else
{
//get value
int i = 0;
 
FOR_EACH_VALUE(m_kvCurrRndwave, wave)
{
if (++i == m_iCurrRndWave)
{
wave->SetStringValue(text);
 
//set text on the sounds panel
vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
if (button)
{
//get last / or \ and make the string be that + 1
char* fslash = Q_strrchr(text, '/');
char* bslash = Q_strrchr(text, '\\');
 
//no forward slash and no back slash
if (!fslash && !bslash)
{
button->SetText(text);
return;
}
 
if (fslash > bslash)
{
button->SetText(fslash + 1);
return;
}
 
else if (bslash > fslash)
{
button->SetText(bslash + 1);
return;
}
}
 
break;
}
}
}
 
return;
}
 
//-----------------------------------------------------------------------------
// Purpose: Called when a keyboard key is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnKeyCodePressed(vgui::KeyCode code)
{
//check for ctrl o or ctrl s
if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) ||
vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL))
{
if (code == vgui::KeyCode::KEY_O)
OnCommand(LOAD_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_S)
OnCommand(SAVE_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_N)
OnCommand(NEW_BUTTON_COMMAND);
else if (code == vgui::KeyCode::KEY_P)
{
//show settings
g_SettingsPanel->SetVisible(true);
g_SettingsPanel->RequestFocus();
g_SettingsPanel->MoveToFront();
}
 
else if (code == vgui::KeyCode::KEY_D)
OnCommand(DELETE_CURRENT_ITEM_COMMAND);
 
//check for ctrl+alt+a
else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LALT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RALT)) && m_pSoundList && m_pSoundList->m_Keyvalues)
m_pSoundList->OnCommand(NEW_RNDWAVE_WAVE_COMMAND);
 
//check for just ctrl+shift+a
else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT)) && m_SoundscapesList)
m_SoundscapesList->OnCommand(ADD_SOUNDSCAPE_COMMAND);
 
return;
}
 
//check for arrow keys
if (code == KEY_DOWN || code == KEY_UP)
{
if (m_kvCurrRndwave)
{
m_pSoundList->OnKeyCodePressed(code);
}
else if (m_kvCurrSelected && m_pDataList->m_MenuButtons.Count())
{
m_pDataList->OnKeyCodePressed(code);
}
else
{
m_SoundscapesList->OnKeyCodePressed(code);
}
 
return;
}
 
//get key bound to this
const char* key = engine->Key_LookupBinding("modbase_soundscape_panel");
if (!key)
return;
 
//convert the key to a keyboard code
const char* keystring = KeyCodeToString(code);
 
//remove the KEY_ if found
if (Q_strstr(keystring, "KEY_") == keystring)
keystring = keystring + 4;
 
//check both strings
if (!Q_strcasecmp(key, keystring))
OnClose();
}
 
//-----------------------------------------------------------------------------
// Purpose: Starts the soundscape on map spawn
//-----------------------------------------------------------------------------
void CSoundscapeMaker::LevelInitPostEntity()
{
if (g_IsPlayingSoundscape)
PlaySelectedSoundscape();
}
 
//-----------------------------------------------------------------------------
// Purpose: Sets keyvalues from text
//-----------------------------------------------------------------------------
void CSoundscapeMaker::Set(const char* buffer)
{
CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
buf.PutString(buffer);
 
//try and load the keyvalues file first
KeyValues* temp = new KeyValues("SoundscapeFile");
if (!temp->LoadFromBuffer("Text Editor Panel Buffer", buf))
{
//play an error sound
vgui::surface()->PlaySound("resource/warning.wav");
 
//show an error
vgui::QueryBox* popup = new vgui::QueryBox("Error", "Failed to open keyvalues data from \"Text Editor Panel\"", this);
popup->SetOKButtonText("Ok");
popup->SetCancelButtonVisible(false);
popup->AddActionSignalTarget(this);
popup->DoModal(this);
 
temp->deleteThis();
return;
}
 
//stop all soundscapes before deleting the old soundscapes
m_kvCurrSelected = nullptr;
 
if (g_IsPlayingSoundscape)
PlaySelectedSoundscape();
 
//delete and set the old keyvalues
if (m_KeyValues)
m_KeyValues->deleteThis();
 
m_KeyValues = temp;
 
//load the file
LoadFile(m_KeyValues);
}
 
//-----------------------------------------------------------------------------
// Purpose: Destructor for soundscape maker panel
//-----------------------------------------------------------------------------
CSoundscapeMaker::~CSoundscapeMaker()
{
//delete the keyvalue files if needed
if (m_KeyValues)
m_KeyValues->deleteThis();
}
 
//static panel instance
static CSoundscapeMaker* g_SSMakerPanel = nullptr;
 
//interface class
class CSoundscapeMakerInterface : public ISoundscapeMaker
{
public:
void Create(vgui::VPANEL parent)
{
g_SSMakerPanel = new CSoundscapeMaker(parent);
g_SoundPanel = new CSoundListPanel(parent, "SoundscapeSoundListPanel");
g_SettingsPanel = new CSoundscapeSettingsPanel(parent, "SoundscapeSettingsPanel");
g_SoundscapeTextPanel = new CSoundscapeTextPanel(parent, "SoundscapeTextPanel");
g_SoundscapeDebugPanel = new CSoundscapeDebugPanel(parent, "SoundscapeDebugPanel");
}
 
void SetVisible(bool bVisible)
{
if (g_SSMakerPanel)
g_SSMakerPanel->SetVisible(bVisible);
}
 
void Destroy()
{
if (g_SSMakerPanel)
g_SSMakerPanel->DeletePanel();
 
if (g_SoundPanel)
g_SoundPanel->DeletePanel();
 
if (g_SettingsPanel)
g_SettingsPanel->DeletePanel();
 
if (g_SoundscapeTextPanel)
g_SoundscapeTextPanel->DeletePanel();
 
if (g_SoundscapeDebugPanel)
g_SoundscapeDebugPanel->DeletePanel();
 
if (g_SoundscapeClipboard)
g_SoundscapeClipboard->DeletePanel();
 
g_SSMakerPanel = nullptr;
g_SoundPanel = nullptr;
g_SettingsPanel = nullptr;
g_SoundscapeTextPanel = nullptr;
g_SoundscapeDebugPanel = nullptr;
g_SoundscapeClipboard = nullptr;
}
 
void SetSoundText(const char* text)
{
if (!g_SSMakerPanel)
return;
 
g_SSMakerPanel->SetSoundText(text);
g_SSMakerPanel->RequestFocus();
g_SSMakerPanel->MoveToFront();
}
 
void SetAllVisible(bool bVisible)
{
g_ShowSoundscapePanel = false;
 
if (g_SSMakerPanel)
g_SSMakerPanel->SetVisible(bVisible);
 
if (g_SoundPanel)
g_SoundPanel->SetVisible(bVisible);
 
if (g_SettingsPanel)
g_SettingsPanel->SetVisible(bVisible);
 
if (g_SoundscapeTextPanel)
g_SoundscapeTextPanel->SetVisible(bVisible);
 
if (g_SoundscapeDebugPanel)
g_SoundscapeDebugPanel->SetVisible(bVisible);
}
 
void SetBuffer(const char* text)
{
if (g_SSMakerPanel)
g_SSMakerPanel->Set(text);
}
}


void SetSoundText(const char* text)
KeyValues* GetPanelFile()
{
{
if (!g_SSMakerPanel)
return g_SSMakerPanel->m_KeyValues;
return;
 
g_SSMakerPanel->SetSoundText(text);
g_SSMakerPanel->RequestFocus();
g_SSMakerPanel->MoveToFront();
}
}


void SetAllVisible(bool bVisible)
KeyValues* GetPanelSelected()
{
{
g_ShowSoundscapePanel = false;
return g_SSMakerPanel->m_kvCurrSelected;
 
if (g_SSMakerPanel)
g_SSMakerPanel->SetVisible(bVisible);
 
if (g_SoundPanel)
g_SoundPanel->SetVisible(bVisible);
 
if (g_SettingsPanel)
g_SettingsPanel->SetVisible(bVisible);
 
if (g_SoundscapeTextPanel)
g_SoundscapeTextPanel->SetVisible(bVisible);
if (g_SoundscapeDebugPanel)
g_SoundscapeDebugPanel->SetVisible(bVisible);
}
}


void SetBuffer(const char* text)
void PasteFromClipboard(int type)
{
{
if (g_SSMakerPanel)
g_SSMakerPanel->Paste((SoundscapeClipboardType)type);
g_SSMakerPanel->Set(text);
}
}
};
};

Latest revision as of 18:40, 25 June 2025

vgui_soundscape_maker.cpp file needed for vgui soundscape maker

//========= Created by Waddelz. https://www.youtube.com/@WadDeIz_Real. ============//
//
// Purpose: a vgui panel that allows you to create and test soundscapes in game
//
// $NoKeywords: $
//
//=================================================================================//
#include "cbase.h"
#include "c_soundscape.h"
#include "vgui_soundscape_maker.h"
#include <vgui_controls/Frame.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Divider.h>
#include <vgui_controls/FileOpenDialog.h>
#include <vgui_controls/QueryBox.h>
#include <vgui_controls/Label.h>
#include <vgui_controls/ScrollBar.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/TextEntry.h>
#include <vgui_controls/ComboBox.h>
#include <vgui_controls/CheckButton.h>
#include <vgui_controls/Menu.h>
#include <vgui_controls/MenuItem.h>
#include <vgui_controls/RichText.h>
#include <vgui/ISystem.h>
#include <engine/IEngineSound.h>
#include <vgui/IVGui.h>
#include <vgui/IInput.h>
#include <vgui/ISurface.h>
#include <ienginevgui.h>
#include <filesystem.h>
#include <usermessages.h>
#include <fmtstr.h>

//graph panel for debugging

class CGraphPanel : public vgui::Panel
{
public:
	DECLARE_CLASS_SIMPLE(CGraphPanel, vgui::Panel);

	CGraphPanel(Panel* parent, const char* panelName);

	//think and paint
	virtual void OnThink();
	virtual void Paint();

	//start and stop functions
	virtual void Start();
	virtual void Stop();
	virtual void Restart();
	virtual void Clear();

	//Adding/doing stuff to lines functions
	virtual void AddLine(bool ascending, unsigned char r, unsigned char g, unsigned char b, float speed, float flGraphWidthFraction = 1.0f);
	virtual void RemoveLine(int index);

	//set functions
	virtual void SetDuration(float seconds) { m_flDuration = seconds; }
	virtual void SetHorizontalLinesMax(float seconds) { m_nHorizontalLinesMax = seconds; }
	virtual void SetMaxTextValue(float maxvalue) { m_flMaxValue = maxvalue; }

	//other
	virtual void ApplySchemeSettings(vgui::IScheme* scheme);

	//sets the font
	void SetFont(vgui::HFont font) { m_Font = font; }
	inline vgui::HFont GetFont() { return m_Font; };

	//gets the number of lines
	int GetNumLines() { return m_Lines.Count(); }

private:

	//line information
	struct LineInfo
	{
		float startTime;
		float offset;
		float elapsedWhenStopped;
		float m_flGraphWidthFraction;
		bool ascending;
		unsigned char r, g, b;
		float speed;
	};

	//lines and other stuff
	CUtlVector<LineInfo> m_Lines;
	float m_flDuration;
	float m_flTimeOffset;
	bool m_bRunning;

	//mad horizontal lines
	int m_nHorizontalLinesMax = 2;
	float m_flMaxValue = 1.0f;

	vgui::HFont m_Font;
};

extern vgui::ILocalize* g_pVGuiLocalize;

//-----------------------------------------------------------------------------
// Purpose: Graph panel
//-----------------------------------------------------------------------------
CGraphPanel::CGraphPanel(Panel* parent, const char* panelName)
	: BaseClass(parent, panelName)
{
	SetPaintBackgroundEnabled(false);
	m_flDuration = 2.0f;
	m_bRunning = false;
	m_flTimeOffset = 0.0f;
	m_Font = vgui::INVALID_FONT;
	SetBgColor(Color(0, 0, 0, 255));
}

//-----------------------------------------------------------------------------
// Purpose: Called when this panel thinks
//-----------------------------------------------------------------------------
void CGraphPanel::OnThink()
{
	if (m_bRunning)
		Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Called when this panel paints
//-----------------------------------------------------------------------------
void CGraphPanel::Paint()
{
	//get size
	int w, h;
	GetSize(w, h);

	//set fill background
	vgui::surface()->DrawSetColor(GetBgColor());
	vgui::surface()->DrawFilledRect(0, 0, w, h);

	//draw horizontal grid lines
	if (m_nHorizontalLinesMax > 1)
	{
		vgui::surface()->DrawSetColor(100, 100, 100, 128); //grey lines

		//draw lines
		for (int i = 0; i < m_nHorizontalLinesMax; ++i)
		{
			float frac = (float)i / (m_nHorizontalLinesMax - 1);
			int y = h - (int)(frac * h);

			//draw the line
			vgui::surface()->DrawLine(0, y, w, y);

			float labelValue = frac * m_flMaxValue;

			char buf[32];
			Q_snprintf(buf, sizeof(buf), "%.2f", labelValue);

			vgui::surface()->DrawSetTextFont(GetFont());
			vgui::surface()->DrawSetTextColor(255, 255, 255, 255);

			//set text pos
			if (labelValue == m_flMaxValue)
				vgui::surface()->DrawSetTextPos(5, y);
			else if (labelValue <= 0.0f)
				vgui::surface()->DrawSetTextPos(5, y - 14);
			else
				vgui::surface()->DrawSetTextPos(5, y - 8);

			wchar_t wbuf[32];
			g_pVGuiLocalize->ConvertANSIToUnicode(buf, wbuf, sizeof(wbuf));
			vgui::surface()->DrawPrintText(wbuf, wcslen(wbuf));
		}
	}

	//get now time
	float now = vgui::system()->GetFrameTime();

	//now draw the graphs
	for (int i = 0; i < m_Lines.Count(); ++i)
	{
		//get line info
		const LineInfo& line = m_Lines[i];

		//set color
		vgui::surface()->DrawSetColor(line.r, line.g, line.b, 255);

		//do stuff
		float elapsed = m_bRunning ? (now - line.startTime - line.offset) : line.elapsedWhenStopped;
		if (elapsed < 0.0f)
			continue;

		float effectiveDuration = m_flDuration / line.speed;

		if (elapsed > effectiveDuration)
			elapsed = effectiveDuration;

		float progress = elapsed / effectiveDuration;

		int lastX = -1;
		int lastY = -1;

		// Calculate the maximum possible width fraction for this line considering the offset
		float maxWidthForLine = line.m_flGraphWidthFraction - line.offset;
		if (maxWidthForLine < 0.0f)
			maxWidthForLine = 0.0f; // prevent negative width

		//make 128 line points
		for (int j = 0; j < 128; ++j)
		{
			float t = (float)j / 127.0f;

			// Stop drawing if t > progress for this line
			if (t > progress)
				break;

			float curve;
			if (line.ascending)
				curve = t * t * t;
			else
				curve = 1.0f - t * t;

			// Calculate the X position using offset + scaled max width for this line
			float combinedPos = line.offset + t * maxWidthForLine;

			// Stop if combinedPos is beyond max graph width fraction (to not draw outside graph area)
			if (combinedPos > line.m_flGraphWidthFraction)
				break;

			int x = (int)(w * combinedPos);
			int y = (int)(h - curve * h);

			//draw the line
			if (lastX >= 0 && lastY >= 0)
				vgui::surface()->DrawLine(lastX, lastY, x, y);

			lastX = x;
			lastY = y;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Starts drawing the graphs
//-----------------------------------------------------------------------------
void CGraphPanel::Start()
{
	//check for not running
	if (!m_bRunning)
	{
		float now = vgui::system()->GetFrameTime();
		for (int i = 0; i < m_Lines.Count(); ++i)
		{
			if (m_Lines[i].elapsedWhenStopped < m_flDuration / m_Lines[i].speed)
				m_Lines[i].startTime = now - m_Lines[i].elapsedWhenStopped;
		}

		//start running
		m_bRunning = true;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Stops drawing the graphs
//-----------------------------------------------------------------------------
void CGraphPanel::Stop()
{
	//check for running
	if (m_bRunning)
	{
		float now = vgui::system()->GetFrameTime();
		for (int i = 0; i < m_Lines.Count(); ++i)
			m_Lines[i].elapsedWhenStopped = now - m_Lines[i].startTime;

		//stop running
		m_bRunning = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Clears the line graph
//-----------------------------------------------------------------------------
void CGraphPanel::Clear()
{
	m_Lines.RemoveAll();
	m_flTimeOffset = 0.0f;
	m_bRunning = false;
}

//-----------------------------------------------------------------------------
// Purpose: Resets the lines
//-----------------------------------------------------------------------------
void CGraphPanel::Restart()
{
	for (int i = 0; i < m_Lines.Count(); i++)
	{
		m_Lines[i].startTime = vgui::system()->GetFrameTime();
		m_Lines[i].elapsedWhenStopped = 0.0f;
	}

	m_flTimeOffset += 0.1f;
}

//-----------------------------------------------------------------------------
// Purpose: Adds a line to the line graph
//-----------------------------------------------------------------------------
void CGraphPanel::AddLine(bool ascending, unsigned char r, unsigned char g, unsigned char b, float speed, float flGraphWidthFraction)
{
	//check speed
	if (speed <= 0.0f)
		speed = 1.0f;

	//create line info
	LineInfo line;
	line.startTime = vgui::system()->GetFrameTime();
	line.ascending = ascending;
	line.m_flGraphWidthFraction = flGraphWidthFraction;
	line.offset = m_flTimeOffset;
	line.elapsedWhenStopped = 0.0f;
	line.r = r;
	line.g = g;
	line.b = b;
	line.speed = speed;

	//add to lines array
	m_Lines.AddToTail(line);
	m_flTimeOffset += 0.1f;
}

//-----------------------------------------------------------------------------
// Purpose: Removes a line graph
//-----------------------------------------------------------------------------
void CGraphPanel::RemoveLine(int index)
{
	//bounds check
	if (index >= m_Lines.Count() || index < 0)
		return;

	//remove the line
	m_Lines.Remove(index);

	//move everything down
	m_flTimeOffset = 0.0f;
	for (int i = 0; i < m_Lines.Count(); ++i)
	{
		m_Lines[i].offset = m_flTimeOffset;
		m_flTimeOffset += 0.1f;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called when scheme settings are set
//-----------------------------------------------------------------------------
void CGraphPanel::ApplySchemeSettings(vgui::IScheme* scheme)
{
	SetFont(scheme->GetFont("Default", IsProportional()));
}

//selected text mode
enum class SoundscapeMode
{
	Mode_Random,
	Mode_Soundscape,
	Mode_Looping,
};

//soundscape clipboard type
enum class SoundscapeClipboardType
{
	Type_SoundscapeNone,
	Type_SoundscapeName,
	Type_SoundscapeData,
	Type_SoundscapeRandomWave,
};

//dsp effects
static const char* g_DspEffects[] = {
	"Normal (off)",
	"Generic",
	"Metal Small",
	"Metal Medium",
	"Metal Large",
	"Tunnel Small",
	"Tunnel Medium",
	"Tunnel Large",
	"Chamber Small",
	"Chamber Medium",
	"Chamber Large",
	"Bright Small",
	"Bright Medium",
	"Bright Large",
	"Water 1",
	"Water 2",
	"Water 3",
	"Concrete Small",
	"Concrete Medium",
	"Concrete Large",
	"Big 1",
	"Big 2",
	"Big 3",
	"Cavern Small",
	"Cavern Medium",
	"Cavern Large",
	"Weirdo 1",
	"Weirdo 2",
	"Weirdo 3",
};

//sound levels
static const char* g_SoundLevels[] = {
	"SNDLVL_50dB",
	"SNDLVL_55dB",
	"SNDLVL_IDLE",
	"SNDLVL_TALKING",
	"SNDLVL_60dB",
	"SNDLVL_65dB",
	"SNDLVL_STATIC",
	"SNDLVL_70dB",
	"SNDLVL_NORM",
	"SNDLVL_75dB",
	"SNDLVL_80dB",
	"SNDLVL_85dB",
	"SNDLVL_90dB",
	"SNDLVL_95dB",
	"SNDLVL_100dB",
	"SNDLVL_105dB",
	"SNDLVL_120dB",
	"SNDLVL_130dB",
	"SNDLVL_GUNFIRE",
	"SNDLVL_140dB",
	"SNDLVL_150dB"
};

bool g_bSSMHack = false;

//max clipboard size
#define MAX_CLIPBOARD_ITEMS 10

//current clipboard stuff
static CUtlVector<KeyValues*> CurrClipboardName;		//for soundscape name
static CUtlVector<KeyValues*> CurrClipboardData;		//for soundscape data
static CUtlVector<KeyValues*> CurrClipboardRandom;		//for random wave

//-----------------------------------------------------------------------------
// Purpose: Helper funciton to compare vector and string
//-----------------------------------------------------------------------------
int Q_vecstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
{
	//check for null substring
	if (!substr || !*substr)
		return -1;

	//store variables
	int substrLen = Q_strlen(substr);

	//search for match
	for (int i = startindex; i <= endindex; ++i)
	{
		bool match = true;
		for (int j = 0; j < substrLen; ++j)
		{
			wchar_t wc = vec[i + j];
			char ch = substr[j];
			if (wc != ch)
			{
				match = false;
				break;
			}
		}

		//found match
		if (match)
			return i;
	}

	//didnt find match
	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: Helper funciton to compare vector and string but reversed
//-----------------------------------------------------------------------------
int Q_vecrstr(const CUtlVector<wchar_t>& vec, int startindex, int endindex, const char* substr)
{
	//check for null substring
	if (!substr || !*substr)
		return -1;

	//store variables
	int substrLen = Q_strlen(substr);

	//search for match
	for (int i = endindex - substrLen + 1; i >= startindex; --i)
	{
		bool match = true;
		for (int j = 0; j < substrLen; ++j)
		{
			wchar_t wc = vec[i + j];
			char ch = substr[j];
			if (wc != ch)
			{
				match = false;
				break;
			}
		}

		//found match
		if (match)
			return i;
	}

	//didnt find match
	return -1;
}

//holds all the sound names
static CUtlVector<char*> g_SoundDirectories;

//-----------------------------------------------------------------------------
// Purpose: Sort function for utl vector
//-----------------------------------------------------------------------------
static int VectorSortFunc(char* const* p1, char* const* p2)
{
	return Q_stricmp(*p1, *p2);
}

//-----------------------------------------------------------------------------
// Purpose: Sort function for utl vector
//-----------------------------------------------------------------------------
static int VectorSortFunc(const char* const* p1, const char* const* p2)
{
	return Q_stricmp(*p1, *p2);
}

//-----------------------------------------------------------------------------
// Purpose: Gets all the sound names and stores them into g_SoundDirectories
//-----------------------------------------------------------------------------
static void GetSoundNames()
{
	//first off clear the sound array first
	for (int i = 0; i < g_SoundDirectories.Count(); i++)
		free(g_SoundDirectories[i]);

	g_SoundDirectories.RemoveAll();

	//directories to search
	CUtlVector<char*> directoriesToSearch;
	directoriesToSearch.AddToTail(strdup("sound"));

	//loop until all directories have been processed
	while (directoriesToSearch.Count() > 0)
	{
		//take the last added directory (depth-first search)
		char* currentDir = directoriesToSearch[directoriesToSearch.Count() - 1];
		directoriesToSearch.Remove(directoriesToSearch.Count() - 1);

		//create a wildcard path to search all files and subdirs
		char searchPath[MAX_PATH];
		Q_snprintf(searchPath, sizeof(searchPath), "%s/*", currentDir);

		FileFindHandle_t findHandle;
		const char* filename = g_pFullFileSystem->FindFirst(searchPath, &findHandle);

		while (filename)
		{
			//ignore special directories
			if (Q_strcmp(filename, ".") != 0 && Q_strcmp(filename, "..") != 0)
			{
				char fullPath[MAX_PATH];
				Q_snprintf(fullPath, sizeof(fullPath), "%s/%s", currentDir, filename);

				//if it's a directory, add it to the list for later processing
				if (g_pFullFileSystem->FindIsDirectory(findHandle))
				{
					directoriesToSearch.AddToTail(strdup(fullPath));
				}
				else
				{
					//check file extension and print if it's .wav or .mp3
					const char* ext = V_GetFileExtension(filename);
					if (ext && (!Q_stricmp(ext, "wav") || !Q_stricmp(ext, "mp3")))
					{
						g_SoundDirectories.AddToTail(strdup(fullPath + 6));
					}
				}
			}

			// Move to next file
			filename = g_pFullFileSystem->FindNext(findHandle);
		}

		//free the memory
		g_pFullFileSystem->FindClose(findHandle);
		free(currentDir);
	}

	//
	g_SoundDirectories.Sort(VectorSortFunc);
}


//text entry for text edit panel


class CTextPanelTextEntry : public vgui::TextEntry
{
public:
	DECLARE_CLASS_SIMPLE(CTextPanelTextEntry, vgui::TextEntry)

	//constructor
	CTextPanelTextEntry(vgui::Panel* parent, const char* panelName)
		: TextEntry(parent, panelName)
	{
		SetMultiline(true);
	}

	//called on keycode typed
	virtual void OnKeyCodeTyped(vgui::KeyCode code)
	{
		//check for enter or enter
		if (code == KEY_ENTER || code == KEY_PAD_ENTER)
			InsertString("\n");

		//check for tab
		else if (code == KEY_TAB)
			InsertString("    ");

		//do other key code
		else
			BaseClass::OnKeyCodeTyped(code);
	}

	//called on keycode pressed
	void OnKeyCodePressed(vgui::KeyCode code)
	{
		if (code == KEY_ENTER || code == KEY_PAD_ENTER
			|| code == KEY_TAB)
			return;

		BaseClass::OnKeyCodePressed(code);
	}

	//called on keycode insert
	void OnKeyTyped(wchar_t c)
	{
		//if (c == '{')
		//{
		//	//insert:
		//	//{
		//	//
		//	//}
		//	//and set cursor in the middle
		//	BaseClass::OnKeyTyped(c);
		//	InsertString("\n\n}    ");
		//	GotoLeft();
		//	GotoUp();
		//	SelectNoText();
		//}
		if (c == '"')
		{
			//check next item
			if (_cursorPos < m_TextStream.Count())
			{

				//check for " so you dont insert string inside string
				if (m_TextStream[_cursorPos] == '"')
				{
					GotoRight();
					SelectNone();
					return;
				}

			}

			//insert:
			//""
			//and set cursor in the middle
			BaseClass::OnKeyTyped(c);
			InsertString("\"");
			GotoLeft();
			SelectNone();
		}
		else
		{
			BaseClass::OnKeyTyped(c);
		}
	}
};



//simple clipboard panel
class CSoundscapeClipboard : public vgui::Frame
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeClipboard, vgui::Frame)

	CSoundscapeClipboard(SoundscapeClipboardType type);

	//creates all the buttons
	void CreateButtons();

	//other
	void OnCommand(const char* pszCommand);
	void OnClose();

private:
	SoundscapeClipboardType m_Type;
};

//static soundscape clipboard panel
static CSoundscapeClipboard* g_SoundscapeClipboard;

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundscapeClipboard::CSoundscapeClipboard(SoundscapeClipboardType type)
	: BaseClass(nullptr, "SoundscapeMakerClipboard"), m_Type(type)
{
	//get the size of the panel
	int tall = 30;

	switch (type)
	{
	case SoundscapeClipboardType::Type_SoundscapeName:
		tall += 29 * CurrClipboardName.Count();
		break;
	case SoundscapeClipboardType::Type_SoundscapeData:
		tall += 29 * CurrClipboardData.Count();
		break;
	case SoundscapeClipboardType::Type_SoundscapeRandomWave:
		tall += 29 * CurrClipboardRandom.Count();
		break;
	}

	SetParent(enginevgui->GetPanel(VGuiPanel_t::PANEL_TOOLS));
	SetCloseButtonVisible(true);
	SetSize(300, tall);
	MoveToCenterOfScreen();
	SetTitle("Soundscape Clipboard", true);
	SetSizeable(false);
	SetDeleteSelfOnClose(true);

	SetVisible(true);
	RequestFocus();
	MoveToFront();

	CreateButtons();
}

//-----------------------------------------------------------------------------
// Purpose: Creates all the clipboard buttons
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::CreateButtons()
{
	switch (m_Type)
	{
	case SoundscapeClipboardType::Type_SoundscapeName:
	{
		//add all the buttons
		for (int i = 0; i < CurrClipboardName.Count(); i++)
		{
			vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), CFmtStr("%.50s", CurrClipboardName[i]->GetName()));
			button->SetBounds(10, 29 + (i * 27), 280, 25);
			button->SetCommand(CFmtStr("$PASTE%d", i));
		}
		break;
	}
	case SoundscapeClipboardType::Type_SoundscapeData:
	{
		//add all the buttons
		for (int i = 0; i < CurrClipboardData.Count(); i++)
		{
			vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), "");

			//set text
			const char* name = CurrClipboardData[i]->GetName();
			if (!Q_stricmp(name, "playrandom"))
			{
				button->SetText("playrandom");
			}
			else if (!Q_stricmp(name, "playlooping"))
			{
				const char* looping = CurrClipboardData[i]->GetString("wave");
				if (strlen(looping) > 25)
					looping += strlen(looping) - 25;

				button->SetText(CFmtStr("%s : '%s'", name, looping));
			}
			else
			{
				const char* looping = CurrClipboardData[i]->GetString("name");
				if (strlen(looping) > 25)
					looping += strlen(looping) - 25;

				button->SetText(CFmtStr("%s : '%s'", name, looping));
			}

			//set other stuff
			button->SetBounds(10, 29 + (i * 27), 280, 25);
			button->SetCommand(CFmtStr("$PASTE%d", i));
		}
		break;
	}
	case SoundscapeClipboardType::Type_SoundscapeRandomWave:
	{
		//add all the buttons
		for (int i = 0; i < CurrClipboardRandom.Count(); i++)
		{
			vgui::Button* button = new vgui::Button(this, CFmtStr("PasteButton%d", i), CurrClipboardRandom[i]->GetString());
			button->SetBounds(10, 29 + (i * 27), 280, 25);
			button->SetCommand(CFmtStr("$PASTE%d", i));
		}
		break;
	}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called when focus is killed
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::OnClose()
{
	g_SoundscapeClipboard = nullptr;

	BaseClass::OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeClipboard::OnCommand(const char* command)
{
	if (Q_stristr(command, "$PASTE") == command)
	{
		//get index
		int index = atoi(command + 6);

		//so this is what i am gonna do:
		//	1. copy KeyValue from index <index> to the top of the clipboard
		//	2. call g_SoundscapeMaker.PasteFromClipboard((int)m_Type);
		//	3. remove keyvalues at last index of clipboard CUtlVector
		switch (m_Type)
		{
		case SoundscapeClipboardType::Type_SoundscapeName:
			if (index >= CurrClipboardName.Count() || CurrClipboardName.Count() <= 0)
				return;

			CurrClipboardName.AddToTail(CurrClipboardName[index]);
			g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
			CurrClipboardName.Remove(CurrClipboardName.Count() - 1);
			break;
		case SoundscapeClipboardType::Type_SoundscapeData:
			if (index >= CurrClipboardData.Count() || CurrClipboardData.Count() <= 0)
				return;

			CurrClipboardData.AddToTail(CurrClipboardData[index]);
			g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
			CurrClipboardData.Remove(CurrClipboardData.Count() - 1);
			break;
		case SoundscapeClipboardType::Type_SoundscapeRandomWave:
			if (index >= CurrClipboardRandom.Count() || CurrClipboardRandom.Count() <= 0)
				return;

			CurrClipboardRandom.AddToTail(CurrClipboardRandom[index]);
			g_SoundscapeMaker->PasteFromClipboard((int)m_Type);
			CurrClipboardRandom.Remove(CurrClipboardRandom.Count() - 1);
			break;
		}
	}

	BaseClass::OnCommand(command);
}



//soundscape maker text editor panel
#define TEXT_PANEL_WIDTH 760
#define TEXT_PANEL_HEIGHT 630

#define TEXT_PANEL_COMMAND_SET "Set"
#define TEXT_PANEL_COMMAND_SET_OK "SetOk"
#define TEXT_PANEL_COMMAND_FIND "FInd"

class CSoundscapeTextPanel : public vgui::Frame
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeTextPanel, vgui::Frame);

	CSoundscapeTextPanel(vgui::VPANEL parent, const char* name);

	//sets the keyvalues
	void Set(KeyValues* keyvalues);
	void RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent);

	//other
	void OnCommand(const char* pszCommand);
	void PerformLayout();
	void OnClose() { BaseClass::OnClose(); }

private:
	CTextPanelTextEntry* m_Text;
	vgui::Button* m_SetButton;
	vgui::TextEntry* m_FindTextEntry;
	vgui::Button* m_FindButton;
};

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundscapeTextPanel::CSoundscapeTextPanel(vgui::VPANEL parent, const char* name)
	: BaseClass(nullptr, name)
{
	SetParent(parent);

	SetKeyBoardInputEnabled(true);
	SetMouseInputEnabled(true);

	SetProportional(false);
	SetTitleBarVisible(true);
	SetMinimizeButtonVisible(false);
	SetMaximizeButtonVisible(false);
	SetCloseButtonVisible(true);
	SetSizeable(true);
	SetMoveable(true);
	SetVisible(false);
	SetMinimumSize(575, 120);

	int ScreenWide, ScreenTall;
	vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);

	SetTitle("Soundscape Text Editor", true);
	SetSize(TEXT_PANEL_WIDTH, TEXT_PANEL_HEIGHT);
	SetPos((ScreenWide - TEXT_PANEL_WIDTH) / 2, (ScreenTall - TEXT_PANEL_HEIGHT) / 2);



	//make text entry
	m_Text = new CTextPanelTextEntry(this, "EditBox");
	m_Text->SetBounds(5, 25, TEXT_PANEL_WIDTH - 10, TEXT_PANEL_HEIGHT - 55);
	m_Text->SetEnabled(true);
	m_Text->SetMultiline(true);
	m_Text->SetVerticalScrollbar(true);

	//make set button
	m_SetButton = new vgui::Button(this, "SetButton", "Apply Changes To Keyvalue Maker");
	m_SetButton->SetBounds(5, TEXT_PANEL_HEIGHT - 27, 250, 25);
	m_SetButton->SetCommand(TEXT_PANEL_COMMAND_SET);

	//make find text entry
	m_FindTextEntry = new vgui::TextEntry(this, "FindTextEntry");
	m_FindTextEntry->SetBounds(450, TEXT_PANEL_HEIGHT - 27, 200, 25);

	//make find button
	m_FindButton = new vgui::Button(this, "FindButton", "Find String");
	m_FindButton->SetBounds(655, TEXT_PANEL_HEIGHT - 27, 100, 25);
	m_FindButton->SetCommand(TEXT_PANEL_COMMAND_FIND);
}

//-----------------------------------------------------------------------------
// Purpose: Sets the keyvalues
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::Set(KeyValues* keyvalues)
{
	//write everything into a buffer
	CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);

	//now write the keyvalues
	KeyValues* pCurrent = keyvalues;
	while (pCurrent)
	{
		RecursiveSetText(pCurrent, buf, 0);

		//put a newline
		if (pCurrent->GetNextTrueSubKey())
			buf.PutChar('\n');

		//get next
		pCurrent = pCurrent->GetNextTrueSubKey();
	}

	//write that to the m_Text
	m_Text->SetText((const char*)buf.Base());
}

//-----------------------------------------------------------------------------
// Purpose: Recursively writes to a util buffer
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::RecursiveSetText(KeyValues* keyvalues, CUtlBuffer& buffer, int indent)
{
	//write \t indent
	for (int i = 0; i < indent; i++)
		buffer.PutString("    ");

	//write name
	buffer.PutChar('"');
	buffer.PutString(keyvalues->GetName());
	buffer.PutString("\"\n");

	//write {
	for (int i = 0; i < indent; i++)
		buffer.PutString("    ");

	buffer.PutString("{\n");

	//increment indent
	indent++;

	//write all the keys first
	FOR_EACH_VALUE(keyvalues, value)
	{
		for (int i = 0; i < indent; i++)
			buffer.PutString("    ");

		//write name and value
		buffer.PutChar('"');
		buffer.PutString(value->GetName());
		buffer.PutString("\"    ");

		buffer.PutChar('"');
		buffer.PutString(value->GetString());
		buffer.PutString("\"\n");
	}

	//write all the subkeys now
	FOR_EACH_TRUE_SUBKEY(keyvalues, value)
	{
		//increment indent
		RecursiveSetText(value, buffer, indent);

		if (value->GetNextTrueSubKey())
			buffer.PutChar('\n');
	}

	//decrement indent
	indent--;

	//write ending }
	for (int i = 0; i < indent; i++)
		buffer.PutString("    ");

	buffer.PutString("}\n");
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET))
	{
		//play sound
		vgui::surface()->PlaySound("ui/buttonclickrelease.wav");

		//check first incase you accidentally press it
		vgui::QueryBox* popup = new vgui::QueryBox("Set File?", "Are you sure you want to set the current keyvalues for the keyvalue maker?\nIf there are errors then this could break the keyvalue file.", this);
		popup->SetOKCommand(new KeyValues("Command", "command", TEXT_PANEL_COMMAND_SET_OK));
		popup->SetCancelButtonVisible(false);
		popup->AddActionSignalTarget(this);
		popup->DoModal(this);
		return;
	}

	//set text
	if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_SET_OK))
	{
		//get string
		int len = m_Text->GetTextLength() + 1;

		char* buf = new char[len];
		m_Text->GetText(buf, len);

		g_SoundscapeMaker->SetBuffer(buf);

		//delete string
		delete[] buf;

		//hide this
		SetVisible(false);
		return;
	}

	//find text
	if (!Q_strcmp(pszCommand, TEXT_PANEL_COMMAND_FIND))
	{
		//get buffer
		char buf[128];
		m_FindTextEntry->GetText(buf, sizeof(buf));

		int index = m_Text->_cursorPos + 1;
		int find = -1;

		//go in reversed order if holding shift
		if (vgui::input()->IsKeyDown(KEY_LSHIFT) || vgui::input()->IsKeyDown(KEY_RSHIFT))
		{

			//see if we find index
			find = Q_vecrstr(m_Text->m_TextStream, 0, index - 2, buf);
			if (find == -1)

				//look again
				find = Q_vecrstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count() - 1, buf);

		}
		else
		{

			//see if we find index
			find = Q_vecstr(m_Text->m_TextStream, index, m_Text->m_TextStream.Count(), buf);
			if (find == -1)

				//look again
				find = Q_vecstr(m_Text->m_TextStream, 0, index, buf);

		}

		//check for invalid index
		if (find == -1)
		{
			//play an error sound
			vgui::surface()->PlaySound("resource/warning.wav");

			//get text
			char error[512];
			Q_snprintf(error, sizeof(error), "Couldnt find any instances of '%s'", buf);

			//show an error
			vgui::QueryBox* popup = new vgui::QueryBox("No Instances Found", error, this);
			popup->SetOKButtonText("Ok");
			popup->SetCancelButtonVisible(false);
			popup->AddActionSignalTarget(this);
			popup->DoModal(this);

			return;
		}

		//get number of newlines
		/*int newline = 0;
		int column = 0;
		for (int i = 0; i < find; i++)
		{
			if (m_Text->m_TextStream[i] == '\n')
			{
				newline++;
				column = 0;
			}
			else
			{
				column++;
			}
		}*/

		//select that
		m_Text->_cursorPos = find;
		m_Text->_select[0] = find;
		m_Text->_select[1] = find + Q_strlen(buf);
		m_Text->LayoutVerticalScrollBarSlider();
		m_Text->Repaint();
		m_Text->RequestFocus();

		return;
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Called on panel size changed
//-----------------------------------------------------------------------------
void CSoundscapeTextPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	int wide, tall;
	GetSize(wide, tall);

	if (m_Text)
		m_Text->SetBounds(5, 25, wide - 10, tall - 55);

	if (m_SetButton)
		m_SetButton->SetBounds(5, tall - 27, 250, 25);

	if (m_FindTextEntry)
		m_FindTextEntry->SetBounds(wide - 310, tall - 27, 200, 25);

	if (m_FindButton)
		m_FindButton->SetBounds(wide - 105, tall - 27, 100, 25);
}

//soundscape settings panel
static CSoundscapeTextPanel* g_SoundscapeTextPanel = nullptr;




//soundscape maker text editor panel
#define DEBUG_PANEL_WIDTH 725
#define DEBUG_PANEL_HEIGHT 530

#define DEBUG_PANEL_COMMAND_CLEAR "Clear"

class CSoundscapeDebugPanel : public vgui::Frame
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeDebugPanel, vgui::Frame);

	CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name);

	//sets the keyvalues
	void AddMessage(Color color, const char* text);

	//other
	void OnCommand(const char* pszCommand);
	void PerformLayout();
	void OnClose() { BaseClass::OnClose(); }

private:
	vgui::RichText* m_Text;
	vgui::Button* m_ClearButton;
	vgui::Label* m_SoundscapesFadingInText;

public:
	CGraphPanel* m_PanelSoundscapesFadingIn;
};

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundscapeDebugPanel::CSoundscapeDebugPanel(vgui::VPANEL parent, const char* name)
	: BaseClass(nullptr, name)
{
	SetParent(parent);

	SetKeyBoardInputEnabled(true);
	SetMouseInputEnabled(true);

	SetProportional(false);
	SetTitleBarVisible(true);
	SetMinimizeButtonVisible(false);
	SetMaximizeButtonVisible(false);
	SetCloseButtonVisible(true);
	SetSizeable(true);
	SetMoveable(true);
	SetVisible(false);
	SetMinimumSize(575, 280);

	SetTitle("Soundscape Debug Panel", true);
	SetSize(DEBUG_PANEL_WIDTH, DEBUG_PANEL_HEIGHT);
	SetPos(0, 0);



	//make text entry
	m_Text = new vgui::RichText(this, "DebugText");
	m_Text->SetBounds(5, 25, DEBUG_PANEL_WIDTH - 10, DEBUG_PANEL_HEIGHT - 55);
	m_Text->SetEnabled(true);
	m_Text->SetVerticalScrollbar(true);

	//make clear button
	m_ClearButton = new vgui::Button(this, "ClearButton", "Clear");
	m_ClearButton->SetBounds(5, DEBUG_PANEL_HEIGHT - 215, DEBUG_PANEL_WIDTH - 10, 25);
	m_ClearButton->SetCommand(DEBUG_PANEL_COMMAND_CLEAR);

	//make fading in label
	m_SoundscapesFadingInText = new vgui::Label(this, "LabelFadingIn", "Soundscapes Fading In");
	m_SoundscapesFadingInText->SetBounds(5, DEBUG_PANEL_HEIGHT - 187, DEBUG_PANEL_WIDTH - 10, 20);

	//make soundscapes fading in thing
	m_PanelSoundscapesFadingIn = new CGraphPanel(this, "SoundscapesFadingIn");
	m_PanelSoundscapesFadingIn->SetBounds(5, DEBUG_PANEL_HEIGHT - 165, DEBUG_PANEL_WIDTH - 10, 155);
	m_PanelSoundscapesFadingIn->SetMaxTextValue(1.0f);
	m_PanelSoundscapesFadingIn->SetHorizontalLinesMax(5);
}

//-----------------------------------------------------------------------------
// Purpose: adds a message to the debug panel
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::AddMessage(Color color, const char* text)
{
	m_Text->InsertColorChange(color);
	m_Text->InsertString(text);

	m_Text->SetMaximumCharCount(100000);
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, DEBUG_PANEL_COMMAND_CLEAR))
	{
		//clear the text
		m_Text->SetText("");
		m_Text->GotoTextEnd();
		return;
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Called on panel size changed
//-----------------------------------------------------------------------------
void CSoundscapeDebugPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	int wide, tall;
	GetSize(wide, tall);

	m_Text->SetBounds(5, 25, wide - 10, tall - 245);
	m_ClearButton->SetBounds(5, tall - 215, wide - 10, 25);
	m_PanelSoundscapesFadingIn->SetBounds(5, tall - 165, wide - 10, 155);
	m_SoundscapesFadingInText->SetBounds(5, tall - 187, wide - 10, 20);
}

//soundscape debug panel
static CSoundscapeDebugPanel* g_SoundscapeDebugPanel = nullptr;

//-----------------------------------------------------------------------------
// Purpose: Function to print text to debug panel
//-----------------------------------------------------------------------------
void SoundscapePrint(Color color, const char* msg, ...)
{
	//format string
	va_list args;
	va_start(args, msg);

	char buf[2048];
	Q_vsnprintf(buf, sizeof(buf), msg, args);
	g_SoundscapeDebugPanel->AddMessage(color, buf);

	va_end(args);
}

//-----------------------------------------------------------------------------
// Purpose: Function to add a line to the soundscape debug panel
//-----------------------------------------------------------------------------
void SoundscapeAddLine(Color color, float speed, float width, bool accending)
{
	if (g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines() <= 6)
		g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->AddLine(accending, color.r(), color.g(), color.b(), speed, width);
}

//-----------------------------------------------------------------------------
// Purpose: Function to get debug line num
//-----------------------------------------------------------------------------
int SoundscapeGetLineNum()
{
	return g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->GetNumLines();
}

//vector positions
Vector g_SoundscapePositions[] = {
	vec3_origin,
	vec3_origin,
	vec3_origin,
	vec3_origin,
	vec3_origin,
	vec3_origin,
	vec3_origin,
	vec3_origin
};

#define SETTINGS_PANEL_WIDTH 350
#define SETTINGS_PANEL_HEIGHT 277

#define SETTINGS_PANEL_COMMAND_POS1 "GetPos0"
#define SETTINGS_PANEL_COMMAND_POS2 "GetPos1"
#define SETTINGS_PANEL_COMMAND_POS3 "GetPos2"
#define SETTINGS_PANEL_COMMAND_POS4 "GetPos3"
#define SETTINGS_PANEL_COMMAND_POS5 "GetPos4"
#define SETTINGS_PANEL_COMMAND_POS6 "GetPos5"
#define SETTINGS_PANEL_COMMAND_POS7 "GetPos6"
#define SETTINGS_PANEL_COMMAND_POS8 "GetPos7"
#define SETTINGS_PANEL_COMMAND_SHOW "ShowPositions"
#define SETTINGS_PANEL_COMMAND_DEBUG "Debug"

#define MAX_SOUNDSCAPES 8

//soundscape maker settings panel
class CSoundscapeSettingsPanel : public vgui::Frame
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeSettingsPanel, vgui::Frame);

	CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name);

	//other
	void OnCommand(const char* pszCommand);

	//sets the text
	void SetItem(int index, const Vector& value);

	//message funcs
	MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);

	~CSoundscapeSettingsPanel();

private:
	//position text entries
	vgui::TextEntry* m_TextEntryPos0;
	vgui::TextEntry* m_TextEntryPos1;
	vgui::TextEntry* m_TextEntryPos2;
	vgui::TextEntry* m_TextEntryPos3;
	vgui::TextEntry* m_TextEntryPos4;
	vgui::TextEntry* m_TextEntryPos5;
	vgui::TextEntry* m_TextEntryPos6;
	vgui::TextEntry* m_TextEntryPos7;
	vgui::CheckButton* m_ShowSoundscapePositions;
	vgui::Button* m_ShowSoundscapeDebug;

	friend class CSoundscapeMaker;
};


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundscapeSettingsPanel::CSoundscapeSettingsPanel(vgui::VPANEL parent, const char* name)
	: BaseClass(nullptr, name)
{
	SetParent(parent);

	SetKeyBoardInputEnabled(true);
	SetMouseInputEnabled(true);

	SetProportional(false);
	SetTitleBarVisible(true);
	SetMinimizeButtonVisible(false);
	SetMaximizeButtonVisible(false);
	SetCloseButtonVisible(true);
	SetSizeable(false);
	SetMoveable(true);
	SetVisible(false);

	//set the size and pos
	int ScreenWide, ScreenTall;
	vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);

	SetTitle("Soundscape Maker Settings", true);
	SetSize(SETTINGS_PANEL_WIDTH, SETTINGS_PANEL_HEIGHT);
	SetPos((ScreenWide - SETTINGS_PANEL_WIDTH) / 2, (ScreenTall - SETTINGS_PANEL_HEIGHT) / 2);



	//load settings
	KeyValues* settings = new KeyValues("settings");
	if (!settings->LoadFromFile(filesystem, "cfg/soundscape_maker.txt", "MOD"))
		ConWarning("Failed to load settings for 'cfg/soundscape_maker.txt'. Using default settings.");

	//get positions
	const char* pos0 = settings->GetString("Position0", "0 0 0");
	const char* pos1 = settings->GetString("Position1", "0 0 0");
	const char* pos2 = settings->GetString("Position2", "0 0 0");
	const char* pos3 = settings->GetString("Position3", "0 0 0");
	const char* pos4 = settings->GetString("Position4", "0 0 0");
	const char* pos5 = settings->GetString("Position5", "0 0 0");
	const char* pos6 = settings->GetString("Position6", "0 0 0");
	const char* pos7 = settings->GetString("Position7", "0 0 0");

	//create position text 1
	m_TextEntryPos0 = new vgui::TextEntry(this, "PosTextEntry0");
	m_TextEntryPos0->SetEnabled(true);
	m_TextEntryPos0->SetText(pos0 ? pos0 : "0 0 0");
	m_TextEntryPos0->SetBounds(5, 30, 230, 20);
	m_TextEntryPos0->SetMaximumCharCount(32);

	//create position 1 button
	vgui::Button* m_ButtonPos0 = new vgui::Button(this, "PosButton0", "Find Position 0", this, SETTINGS_PANEL_COMMAND_POS1);
	m_ButtonPos0->SetBounds(240, 30, 100, 20);

	//create position text 1
	m_TextEntryPos1 = new vgui::TextEntry(this, "PosTextEntry1");
	m_TextEntryPos1->SetEnabled(true);
	m_TextEntryPos1->SetText(pos1 ? pos1 : "0 0 0");
	m_TextEntryPos1->SetBounds(5, 55, 230, 20);
	m_TextEntryPos1->SetMaximumCharCount(32);

	//create position 2 button
	vgui::Button* m_ButtonPos1 = new vgui::Button(this, "PosButton1", "Find Position 1", this, SETTINGS_PANEL_COMMAND_POS2);
	m_ButtonPos1->SetBounds(240, 55, 100, 20);

	//create position text 3
	m_TextEntryPos2 = new vgui::TextEntry(this, "PosTextEntry0");
	m_TextEntryPos2->SetEnabled(true);
	m_TextEntryPos2->SetText(pos2 ? pos2 : "0 0 0");
	m_TextEntryPos2->SetBounds(5, 80, 230, 20);
	m_TextEntryPos2->SetMaximumCharCount(32);

	//create position 1 button
	vgui::Button* m_ButtonPos2 = new vgui::Button(this, "PosButton2", "Find Position 2", this, SETTINGS_PANEL_COMMAND_POS3);
	m_ButtonPos2->SetBounds(240, 80, 100, 20);

	// create position text 4
	m_TextEntryPos3 = new vgui::TextEntry(this, "PosTextEntry3");
	m_TextEntryPos3->SetEnabled(true);
	m_TextEntryPos3->SetText(pos3 ? pos3 : "0 0 0");
	m_TextEntryPos3->SetBounds(5, 105, 230, 20);
	m_TextEntryPos3->SetMaximumCharCount(32);

	// create position 4 button
	vgui::Button* m_ButtonPos3 = new vgui::Button(this, "PosButton3", "Find Position 3", this, SETTINGS_PANEL_COMMAND_POS4);
	m_ButtonPos3->SetBounds(240, 105, 100, 20);

	// create position text 5
	m_TextEntryPos4 = new vgui::TextEntry(this, "PosTextEntry4");
	m_TextEntryPos4->SetEnabled(true);
	m_TextEntryPos4->SetText(pos4 ? pos4 : "0 0 0");
	m_TextEntryPos4->SetBounds(5, 130, 230, 20);
	m_TextEntryPos4->SetMaximumCharCount(32);

	// create position 5 button
	vgui::Button* m_ButtonPos4 = new vgui::Button(this, "PosButton4", "Find Position 4", this, SETTINGS_PANEL_COMMAND_POS5);
	m_ButtonPos4->SetBounds(240, 130, 100, 20);

	// create position text 6
	m_TextEntryPos5 = new vgui::TextEntry(this, "PosTextEntry5");
	m_TextEntryPos5->SetEnabled(true);
	m_TextEntryPos5->SetText(pos5 ? pos5 : "0 0 0");
	m_TextEntryPos5->SetBounds(5, 155, 230, 20);
	m_TextEntryPos5->SetMaximumCharCount(32);

	// create position 6 button
	vgui::Button* m_ButtonPos5 = new vgui::Button(this, "PosButton5", "Find Position 5", this, SETTINGS_PANEL_COMMAND_POS6);
	m_ButtonPos5->SetBounds(240, 155, 100, 20);

	// create position text 7
	m_TextEntryPos6 = new vgui::TextEntry(this, "PosTextEntry6");
	m_TextEntryPos6->SetEnabled(true);
	m_TextEntryPos6->SetText(pos6 ? pos6 : "0 0 0");
	m_TextEntryPos6->SetBounds(5, 180, 230, 20);
	m_TextEntryPos6->SetMaximumCharCount(32);

	// create position 7 button
	vgui::Button* m_ButtonPos6 = new vgui::Button(this, "PosButton6", "Find Position 6", this, SETTINGS_PANEL_COMMAND_POS7);
	m_ButtonPos6->SetBounds(240, 180, 100, 20);

	// create position text 8
	m_TextEntryPos7 = new vgui::TextEntry(this, "PosTextEntry7");
	m_TextEntryPos7->SetEnabled(true);
	m_TextEntryPos7->SetText(pos7 ? pos7 : "0 0 0");
	m_TextEntryPos7->SetBounds(5, 205, 230, 20);
	m_TextEntryPos7->SetMaximumCharCount(32);

	// create position 8 button
	vgui::Button* m_ButtonPos7 = new vgui::Button(this, "PosButton7", "Find Position 7", this, SETTINGS_PANEL_COMMAND_POS8);
	m_ButtonPos7->SetBounds(240, 205, 100, 20);

	// create show soundscape positions checkbox
	m_ShowSoundscapePositions = new vgui::CheckButton(this, "ShowCheckox", "Show Soundscape Positions");
	m_ShowSoundscapePositions->SetBounds(75, 225, 200, 20);
	m_ShowSoundscapePositions->SetCommand(SETTINGS_PANEL_COMMAND_SHOW);
	m_ShowSoundscapePositions->SetSelected(settings->GetBool("ShowSoundscapes", false));

	//set convar value
	ConVar* cv = cvar->FindVar("__ss_draw");
	if (cv)
		cv->SetValue(m_ShowSoundscapePositions->IsSelected());

	//create divider
	vgui::Divider* div = new vgui::Divider(this, "Divider");
	div->SetBounds(-2, 247, SETTINGS_PANEL_WIDTH + 4, 2);

	//create debug thing
	m_ShowSoundscapeDebug = new vgui::Button(this, "DebugInfo", "Show soundscape debug panel");
	m_ShowSoundscapeDebug->SetBounds(20, 254, SETTINGS_PANEL_WIDTH - 40, 20);
	m_ShowSoundscapeDebug->SetCommand(SETTINGS_PANEL_COMMAND_DEBUG);

	//set server positions
	ConCommand* cc = cvar->FindCommand("__ss_maker_set");
	if (cc)
	{
		CCommand args;

		//do pos 0
		if (pos0)
		{
			args.Tokenize(CFmtStr("ssmaker 0 %s 1", pos0));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[0].Base(), pos0);
		}

		//do pos 1
		if (pos1)
		{
			args.Tokenize(CFmtStr("ssmaker 1 %s 1", pos1));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[1].Base(), pos1);
		}

		//do pos 2
		if (pos2)
		{
			args.Tokenize(CFmtStr("ssmaker 2 %s 1", pos2));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[2].Base(), pos2);
		}

		//do pos 3
		if (pos3)
		{
			args.Tokenize(CFmtStr("ssmaker 3 %s 1", pos3));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[3].Base(), pos3);
		}

		//do pos 4
		if (pos4)
		{
			args.Tokenize(CFmtStr("ssmaker 4 %s 1", pos4));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[4].Base(), pos4);
		}

		//do pos 5
		if (pos5)
		{
			args.Tokenize(CFmtStr("ssmaker 5 %s 1", pos5));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[5].Base(), pos5);
		}

		//do pos 6
		if (pos6)
		{
			args.Tokenize(CFmtStr("ssmaker 6 %s 1", pos6));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[6].Base(), pos6);
		}

		//do pos 7
		if (pos7)
		{
			args.Tokenize(CFmtStr("ssmaker 7 %s", pos7));
			cc->Dispatch(args);

			UTIL_StringToVector(g_SoundscapePositions[7].Base(), pos7);
		}
	}

	//delete settings
	settings->deleteThis();
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::OnCommand(const char* pszCommand)
{
	if (Q_strstr(pszCommand, "GetPos") == pszCommand)
	{
		//search for number
		pszCommand = pszCommand + 6;

		//execute command
		static ConCommand* cc = cvar->FindCommand("__ss_maker_start");
		if (cc)
		{
			//hide everything first
			g_SoundscapeMaker->SetAllVisible(false);

			CCommand args;
			args.Tokenize(CFmtStr("ssmaker %d", atoi(pszCommand)));
			cc->Dispatch(args);
		}

		return;
	}

	else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_SHOW))
	{
		static ConVar* cv = cvar->FindVar("__ss_draw");
		if (cv)
			cv->SetValue(m_ShowSoundscapePositions->IsSelected());

		return;
	}

	//handle debug thing
	else if (!Q_strcmp(pszCommand, SETTINGS_PANEL_COMMAND_DEBUG))
	{
		g_SoundscapeDebugPanel->SetVisible(true);
		g_SoundscapeDebugPanel->RequestFocus();
		g_SoundscapeDebugPanel->MoveToFront();
		return;
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::SetItem(int index, const Vector& value)
{
	const char* text = CFmtStr("%.3f %.3f %.3f", value.x, value.y, value.z);

	//check index
	switch (index)
	{
	case 0:
		m_TextEntryPos0->RequestFocus();
		m_TextEntryPos0->SetText(text);
		g_SoundscapePositions[0] = value;
		break;

	case 1:
		m_TextEntryPos1->RequestFocus();
		m_TextEntryPos1->SetText(text);
		g_SoundscapePositions[1] = value;
		break;

	case 2:
		m_TextEntryPos2->RequestFocus();
		m_TextEntryPos2->SetText(text);
		g_SoundscapePositions[2] = value;
		break;
	case 3:
		m_TextEntryPos3->RequestFocus();
		m_TextEntryPos3->SetText(text);
		g_SoundscapePositions[3] = value;
		break;

	case 4:
		m_TextEntryPos4->RequestFocus();
		m_TextEntryPos4->SetText(text);
		g_SoundscapePositions[4] = value;
		break;

	case 5:
		m_TextEntryPos5->RequestFocus();
		m_TextEntryPos5->SetText(text);
		g_SoundscapePositions[5] = value;
		break;

	case 6:
		m_TextEntryPos6->RequestFocus();
		m_TextEntryPos6->SetText(text);
		g_SoundscapePositions[6] = value;
		break;

	case 7:
		m_TextEntryPos7->RequestFocus();
		m_TextEntryPos7->SetText(text);
		g_SoundscapePositions[7] = value;
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called on text changed
//-----------------------------------------------------------------------------
void CSoundscapeSettingsPanel::OnTextChanged(KeyValues* kv)
{
	static ConCommand* cc = cvar->FindCommand("__ss_maker_set");

	//check focus
	if (m_TextEntryPos0->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos0->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[0].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 0 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos1->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos1->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[1].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 1 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos2->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos2->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[2].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 2 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos3->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos3->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[3].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 3 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos4->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos4->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[4].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 4 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos5->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos5->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[5].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 5 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos6->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos6->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[6].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 6 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

	//check focus
	if (m_TextEntryPos7->HasFocus())
	{
		//get text
		char buf[512];
		m_TextEntryPos7->GetText(buf, sizeof(buf));

		//convert to vector
		UTIL_StringToVector(g_SoundscapePositions[7].Base(), buf);

		//do command
		if (cc)
		{
			CCommand args;
			args.Tokenize(CFmtStr("ssmaker 7 %s 1", buf));
			cc->Dispatch(args);
		}

		return;
	}

}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSoundscapeSettingsPanel::~CSoundscapeSettingsPanel()
{
	//save everything
	KeyValues* settings = new KeyValues("settings");

	//get text's
	char text0[64];
	char text1[64];
	char text2[64];
	char text3[64];
	char text4[64];
	char text5[64];
	char text6[64];
	char text7[64];

	m_TextEntryPos0->GetText(text0, sizeof(text0));
	m_TextEntryPos1->GetText(text1, sizeof(text1));
	m_TextEntryPos2->GetText(text2, sizeof(text2));
	m_TextEntryPos3->GetText(text3, sizeof(text3));
	m_TextEntryPos4->GetText(text4, sizeof(text4));
	m_TextEntryPos5->GetText(text5, sizeof(text5));
	m_TextEntryPos6->GetText(text6, sizeof(text6));
	m_TextEntryPos7->GetText(text7, sizeof(text7));

	//save text entries
	settings->SetString("Position0", text0);
	settings->SetString("Position1", text1);
	settings->SetString("Position2", text2);
	settings->SetString("Position3", text3);
	settings->SetString("Position4", text4);
	settings->SetString("Position5", text5);
	settings->SetString("Position6", text6);
	settings->SetString("Position7", text7);

	//save check buttons
	settings->SetBool("ShowSoundscapes", m_ShowSoundscapePositions->IsSelected());

	//save to file
	settings->SaveToFile(filesystem, "cfg/soundscape_maker.txt", "MOD");
	settings->deleteThis();
}

//static soundscape settings panel
static CSoundscapeSettingsPanel* g_SettingsPanel = nullptr;


#define BUTTON_MENU_COMMAND_COPY_CLIPBOARD "CopyClipboard"

//button
class CSoundscapeButton : public vgui::Button
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeButton, vgui::Button)

	CSoundscapeButton(vgui::Panel* parent, const char* name, const char* text, vgui::Panel* target = nullptr, const char* command = nullptr, KeyValues* kv = nullptr, SoundscapeClipboardType type = SoundscapeClipboardType::Type_SoundscapeNone)
		: BaseClass(parent, name, text, target, command), m_bIsSelected(false), m_KeyValues(kv), m_KeyValuesType(type)
	{
		m_ColorSelected = Color(200, 200, 200, 200);
		m_FgColorSelected = Color(0, 0, 0, 255);
	}

	//apply scheme settings
	void ApplySchemeSettings(vgui::IScheme* scheme)
	{
		BaseClass::ApplySchemeSettings(scheme);

		m_ColorNotSelected = GetButtonArmedBgColor();
		m_FgColorNotSelected = GetButtonArmedFgColor();
	}

	//paints the background
	void PaintBackground()
	{
		if (m_bIsSelected)
			SetBgColor(m_ColorSelected);
		else
			SetBgColor(m_ColorNotSelected);

		BaseClass::PaintBackground();
	}

	//paints
	void Paint()
	{
		if (m_bIsSelected)
			SetFgColor(m_FgColorSelected);
		else
			SetFgColor(m_FgColorNotSelected);

		BaseClass::Paint();
	}

	//mouse release
	void OnMouseReleased(vgui::MouseCode code)
	{
		if (code != vgui::MouseCode::MOUSE_RIGHT)
			return BaseClass::OnMouseReleased(code);

		//this should never happen but just in case
		if (!m_KeyValues)
			return;

		//get cursor pos
		int x, y;
		vgui::surface()->SurfaceGetCursorPos(x, y);

		//show menu
		vgui::Menu* menu = new vgui::Menu(this, "Clipboard");
		menu->AddMenuItem("CopyToClipboard", "Copy", BUTTON_MENU_COMMAND_COPY_CLIPBOARD, this);
		menu->SetBounds(x, y, 200, 50);
		menu->SetVisible(true);

		BaseClass::Paint();
	}

	//mouse release
	void OnCommand(const char* pszCommand)
	{
		if (!Q_strcmp(pszCommand, BUTTON_MENU_COMMAND_COPY_CLIPBOARD))
		{
			//create copy of keyvalues
			switch (m_KeyValuesType)
			{
			case SoundscapeClipboardType::Type_SoundscapeName:
			{
				//copy
				if (CurrClipboardName.Count() >= MAX_CLIPBOARD_ITEMS)
				{
					CurrClipboardName[0]->deleteThis();
					CurrClipboardName.Remove(0);
				}

				CurrClipboardName.AddToTail(m_KeyValues->MakeCopy());

				//debug message
				SoundscapePrint(Color(255, 255, 255, 255), "Soundscape: '%s' Coppied to clipboard.\n", m_KeyValues->GetName());
				break;
			}
			case SoundscapeClipboardType::Type_SoundscapeData:
			{
				//copy
				if (CurrClipboardData.Count() >= MAX_CLIPBOARD_ITEMS)
				{
					CurrClipboardData[0]->deleteThis();
					CurrClipboardData.Remove(0);
				}

				//make copy
				CurrClipboardData.AddToTail(m_KeyValues->MakeCopy());

				//debug message
				SoundscapePrint(Color(255, 255, 255, 255), "Soundscape Data: '%s' Coppied to clipboard.\n", m_KeyValues->GetName());
				break;
			}
			case SoundscapeClipboardType::Type_SoundscapeRandomWave:
			{
				//copy
				if (CurrClipboardRandom.Count() >= MAX_CLIPBOARD_ITEMS)
				{
					CurrClipboardRandom[0]->deleteThis();
					CurrClipboardRandom.Remove(0);
				}

				CurrClipboardRandom.AddToTail(m_KeyValues->MakeCopy());

				//debug message
				SoundscapePrint(Color(255, 255, 255, 255), "Soundscape Random Wave: '%s' Coppied to clipboard.\n", m_KeyValues->GetString());
				break;
			}
			}
		}
	}

	//is this selected or not
	bool m_bIsSelected;
	static Color m_ColorSelected;
	static Color m_ColorNotSelected;
	static Color m_FgColorSelected;
	static Color m_FgColorNotSelected;

	KeyValues* m_KeyValues = nullptr;
	SoundscapeClipboardType m_KeyValuesType;
};

Color CSoundscapeButton::m_ColorSelected = Color();
Color CSoundscapeButton::m_ColorNotSelected = Color();
Color CSoundscapeButton::m_FgColorSelected = Color();
Color CSoundscapeButton::m_FgColorNotSelected = Color();


//soundscape combo box

class CSoundListComboBox : public vgui::ComboBox
{
public:
	DECLARE_CLASS_SIMPLE(CSoundListComboBox, vgui::ComboBox);

	CSoundListComboBox(Panel* parent, const char* panelName, int numLines, bool allowEdit) :
		BaseClass(parent, panelName, numLines, allowEdit) {}

	//on key typed. check for menu item with text inside it and if found then
	//select that item.
	void OnKeyTyped(wchar_t unichar)
	{
		//check for ctrl or shift down
		if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL) || unichar == '`')
			return;

		//open up this combo box
		if (unichar == 13)
		{
			ShowMenu();
			return;
		}

		BaseClass::OnKeyTyped(unichar);

		//check for backspace
		if (unichar == 8 || unichar == '_')
			return;

		//get text
		char buf[512];
		GetText(buf, sizeof(buf));

		//start from current index + 1
		int start = GetMenu()->GetActiveItem() + 1;

		//look for sound with same name starting from the start first
		for (int i = start; i < g_SoundDirectories.Count(); i++)
		{
			if (Q_stristr(g_SoundDirectories[i], buf))
			{
				GetMenu()->SetCurrentlyHighlightedItem(i);
				return;
			}
		}

		//now cheeck from 0 to the start
		for (int i = 0; i < start; i++)
		{
			if (Q_stristr(g_SoundDirectories[i], buf))
			{
				GetMenu()->SetCurrentlyHighlightedItem(i);
				return;
			}
		}
	}
};


//sounds list panel

#define SOUND_LIST_PANEL_WIDTH 375
#define SOUND_LIST_PANEL_HEIGHT 255
#define SOUND_LIST_PLAY_COMMAND "PlaySound"
#define SOUND_LIST_STOP_COMMAND "StopSound"
#define SOUND_LIST_INSERT_COMMAND "Insert"
#define SOUND_LIST_RELOAD_COMMAND "Reload"
#define SOUND_LIST_SEARCH_COMMAND "Search"

class CSoundListPanel : public vgui::Frame
{
public:
	DECLARE_CLASS_SIMPLE(CSoundListPanel, vgui::Frame);

	CSoundListPanel(vgui::VPANEL parent, const char* name);

	//initalizes sound combo box
	void InitalizeSounds();
	void InitalizeSoundscapes(CUtlVector<const char*>& OtherSoundscapes);

	//sets if this is currently using the soundscape panel or sound panel
	void SetIsUsingSoundPanel(bool bUsing);

	//other
	void OnCommand(const char* pszCommand);
	void OnClose();

private:
	friend class CSoundscapeMaker;

	//are we currently in the 'sound' panel or 'soundscape' panel
	bool bCurrentlyInSoundPanel = true;

	CSoundListComboBox* m_SoundsList;		//for sounds
	CSoundListComboBox* m_SoundscapesList;	//for soundscapes
	vgui::TextEntry* m_SearchText;
	vgui::Button* m_SearchButton;
	vgui::Button* m_PlayButton;
	vgui::Button* m_StopSoundButton;
	vgui::Button* m_InsertButton;
	vgui::Button* m_ReloadSounds;

	//current sound guid
	int m_iSongGuid = -1;
};

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSoundListPanel::CSoundListPanel(vgui::VPANEL parent, const char* name)
	: BaseClass(nullptr, name)
{
	SetParent(parent);

	SetKeyBoardInputEnabled(true);
	SetMouseInputEnabled(true);

	SetProportional(false);
	SetTitleBarVisible(true);
	SetMinimizeButtonVisible(false);
	SetMaximizeButtonVisible(false);
	SetCloseButtonVisible(true);
	SetSizeable(false);
	SetMoveable(true);
	SetVisible(false);

	//set the size and pos
	int ScreenWide, ScreenTall;
	vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);

	SetTitle("Sounds List", true);
	SetSize(SOUND_LIST_PANEL_WIDTH, SOUND_LIST_PANEL_HEIGHT);
	SetPos((ScreenWide - SOUND_LIST_PANEL_WIDTH) / 2, (ScreenTall - SOUND_LIST_PANEL_HEIGHT) / 2);

	//create combo box's
	m_SoundsList = new CSoundListComboBox(this, "SoundsList", 20, true);
	m_SoundsList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_SoundsList->AddActionSignalTarget(this);
	m_SoundsList->SetVisible(true);

	m_SoundscapesList = new CSoundListComboBox(this, "SoundscapesList", 20, true);
	m_SoundscapesList->SetBounds(5, 25, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_SoundscapesList->AddActionSignalTarget(this);
	m_SoundscapesList->SetVisible(false);

	//make divider
	vgui::Divider* divider1 = new vgui::Divider(this, "Divider");
	divider1->SetBounds(-5, 48, SOUND_LIST_PANEL_WIDTH + 10, 2);

	//create text
	vgui::Label* label1 = new vgui::Label(this, "FindSound", "Find Sound");
	label1->SetBounds(147, 51, 120, 20);

	//create text entry
	m_SearchText = new vgui::TextEntry(this, "SearchTextEntry");
	m_SearchText->SetBounds(5, 75, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_SearchText->SetEnabled(true);
	m_SearchText->SetText("");

	//create search for button
	m_SearchButton = new vgui::Button(this, "SearchButton", "Search For");
	m_SearchButton->SetBounds(5, 100, SOUND_LIST_PANEL_WIDTH - 15, 20);;
	m_SearchButton->SetEnabled(true);
	m_SearchButton->SetCommand(SOUND_LIST_SEARCH_COMMAND);

	//make divider
	vgui::Divider* divider2 = new vgui::Divider(this, "Divider");
	divider2->SetBounds(-5, 124, SOUND_LIST_PANEL_WIDTH + 10, 2);

	//create text
	vgui::Label* label2 = new vgui::Label(this, "SoundButtons", "Sound Buttons");
	label2->SetBounds(140, 127, 120, 20);

	//create play button
	m_PlayButton = new vgui::Button(this, "PlayButton", "Play Sound", this);
	m_PlayButton->SetBounds(5, 150, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_PlayButton->SetCommand(SOUND_LIST_PLAY_COMMAND);

	//create stop sound button
	m_StopSoundButton = new vgui::Button(this, "StopSound", "Stop Sound", this);
	m_StopSoundButton->SetBounds(5, 175, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_StopSoundButton->SetCommand(SOUND_LIST_STOP_COMMAND);

	//create sound insert button
	m_InsertButton = new vgui::Button(this, "InsertSound", "Insert Sound", this);
	m_InsertButton->SetBounds(5, 225, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_InsertButton->SetCommand(SOUND_LIST_INSERT_COMMAND);

	//create reload sounds button
	m_ReloadSounds = new vgui::Button(this, "ReloadSounds", "Reload Sounds", this);
	m_ReloadSounds->SetBounds(5, 200, SOUND_LIST_PANEL_WIDTH - 15, 20);
	m_ReloadSounds->SetCommand(SOUND_LIST_RELOAD_COMMAND);
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundListPanel::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, SOUND_LIST_SEARCH_COMMAND))
	{
		//get text
		char buf[512];
		m_SearchText->GetText(buf, sizeof(buf));

		//check for shift key
		bool shift = (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT));

		//vector of texts
		CUtlVector<char*> SoundNames;
		CSoundListComboBox* SoundList = bCurrentlyInSoundPanel ? m_SoundsList : m_SoundscapesList;

		//if we are in soundscape mode then set the SoundNames to all the soundscapes. else set SoundNames to g_SoundDirectories
		if (!bCurrentlyInSoundPanel)
		{
			for (int i = 0; i < m_SoundscapesList->GetItemCount(); i++)
			{
				//insert
				char* tmpbuf = new char[512];
				m_SoundscapesList->GetItemText(i, tmpbuf, 512);

				SoundNames.AddToTail(tmpbuf);
			}
		}
		else
		{
			SoundNames = g_SoundDirectories;
		}

		if (shift)
		{
			//start from current index - 1
			int start = SoundList->GetMenu()->GetActiveItem() - 1;

			//look for sound with same name starting from the start first and going down
			for (int i = start; i >= 0; i--)
			{
				if (Q_stristr(SoundNames[i], buf))
				{
					//select item
					SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
					SoundList->ActivateItem(i);

					//set text
					SoundList->SetText(SoundNames[i]);

					//delete all soundscapes if we need to
					if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
						delete[] SoundNames[i];

					return;
				}
			}


			//now cheeck from the SoundNames to the start
			for (int i = SoundNames.Count() - 1; i > start; i--)
			{
				if (Q_stristr(SoundNames[i], buf))
				{
					//select item
					SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
					SoundList->ActivateItem(i);

					//set text
					SoundList->SetText(SoundNames[i]);

					//delete all soundscapes if we need to
					if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
						delete[] SoundNames[i];

					return;
				}
			}
		}
		else
		{
			//start from current index + 1
			int start = SoundList->GetMenu()->GetActiveItem() + 1;

			//look for sound with same name starting from the start first
			for (int i = start; i < SoundNames.Count(); i++)
			{
				if (Q_stristr(SoundNames[i], buf))
				{
					//select item
					SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
					SoundList->ActivateItem(i);

					//set text
					SoundList->SetText(SoundNames[i]);

					//delete all soundscapes if we need to
					if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
						delete[] SoundNames[i];

					return;
				}
			}


			//now cheeck from 0 to the start
			for (int i = 0; i < start; i++)
			{
				if (Q_stristr(SoundNames[i], buf))
				{
					//select item
					SoundList->GetMenu()->SetCurrentlyHighlightedItem(i);
					SoundList->ActivateItem(i);

					//set text
					SoundList->SetText(SoundNames[i]);

					//delete all soundscapes if we need to
					if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
						delete[] SoundNames[i];

					return;
				}
			}
		}

		//delete all soundscapes if we need to
		if (!bCurrentlyInSoundPanel) for (int i = 0; i < SoundNames.Count(); i++)
			delete[] SoundNames[i];

		return;
	}
	else if (!Q_strcmp(pszCommand, SOUND_LIST_PLAY_COMMAND))
	{
		//get the sound
		char buf[512];
		m_SoundsList->GetText(buf, sizeof(buf));

		//stop the sound
		if (enginesound->IsSoundStillPlaying(m_iSongGuid))
		{
			enginesound->StopSoundByGuid(m_iSongGuid);
			m_iSongGuid = -1;
		}

		//precache and play the sound
		if (!enginesound->IsSoundPrecached(buf))
			enginesound->PrecacheSound(buf);

		enginesound->EmitAmbientSound(buf, 1, 100);
		m_iSongGuid = enginesound->GetGuidForLastSoundEmitted();
		return;
	}
	else if (!Q_strcmp(pszCommand, SOUND_LIST_STOP_COMMAND))
	{
		//stop the sound
		if (m_iSongGuid != -1 && enginesound->IsSoundStillPlaying(m_iSongGuid))
		{
			enginesound->StopSoundByGuid(m_iSongGuid);
			m_iSongGuid = -1;
		}

		return;
	}
	else if (!Q_strcmp(pszCommand, SOUND_LIST_INSERT_COMMAND))
	{
		//make not visible
		SetVisible(false);

		//stop the sound
		if (enginesound->IsSoundStillPlaying(m_iSongGuid))
		{
			enginesound->StopSoundByGuid(m_iSongGuid);
			m_iSongGuid = -1;
		}

		//get the sound
		char buf[512];

		if (bCurrentlyInSoundPanel)
			m_SoundsList->GetText(buf, sizeof(buf));
		else
			m_SoundscapesList->GetText(buf, sizeof(buf));

		//set the sound text
		g_SoundscapeMaker->SetSoundText(buf);
		return;
	}
	else if (!Q_strcmp(pszCommand, SOUND_LIST_RELOAD_COMMAND))
	{
		if (bCurrentlyInSoundPanel)
		{
			//clear everything for the combo box and reload it
			m_SoundsList->RemoveAll();
			InitalizeSounds();
		}
		else
		{
			//clear everything for the combo box and reload it
			m_SoundscapesList->RemoveAll();

			bool bPrev = g_bSSMHack;
			g_bSSMHack = true;

			//reload all the soundscape files
			enginesound->StopAllSounds(true);

			g_SoundscapeSystem.StartNewSoundscape(nullptr);
			g_SoundscapeSystem.RemoveAll();
			g_SoundscapeSystem. Init();

			g_bSSMHack = bPrev;

			//load all the temporary soundscapes
			CUtlVector<const char*> OtherSoundscapes;
			for (KeyValues* curr = g_SoundscapeMaker->GetPanelFile(); curr; curr = curr->GetNextKey())
			{
				if (curr == g_SoundscapeMaker->GetPanelSelected())
					continue;

				OtherSoundscapes.AddToTail(curr->GetName());
			}

			InitalizeSoundscapes(OtherSoundscapes);
		}

		return;
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Called on panel close
//-----------------------------------------------------------------------------
void CSoundListPanel::OnClose()
{
	OnCommand(SOUND_LIST_STOP_COMMAND);
	BaseClass::OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: Initalizes the sounds list
//-----------------------------------------------------------------------------
void CSoundListPanel::InitalizeSounds()
{
	//get the sound array
	GetSoundNames();

	//add all the sounds
	for (int i = 0; i < g_SoundDirectories.Size(); i++)
		m_SoundsList->AddItem(g_SoundDirectories[i], nullptr);

	m_SoundsList->ActivateItem(0);
}

//-----------------------------------------------------------------------------
// Purpose: Initalizes the soundscape list
//-----------------------------------------------------------------------------
void CSoundListPanel::InitalizeSoundscapes(CUtlVector<const char*>& OtherSoundscapes)
{
	//remove everything
	m_SoundscapesList->RemoveAll();

	//add all the soundscapes
	for (int i = 0; i < g_SoundscapeSystem.m_soundscapes.Count(); i++)
		OtherSoundscapes.AddToTail(g_SoundscapeSystem.m_soundscapes[i]->GetName());

	OtherSoundscapes.Sort(VectorSortFunc);

	//quickly remove duplicatesd
	for (int i = 1; i < OtherSoundscapes.Count(); )
	{
		if (!Q_strcmp(OtherSoundscapes[i], OtherSoundscapes[i - 1]))
		{
			OtherSoundscapes.Remove(i);
			continue;
		}
		i++;
	}

	for (int i = 0; i < OtherSoundscapes.Size(); i++)
		m_SoundscapesList->AddItem(OtherSoundscapes[i], nullptr);

	m_SoundscapesList->ActivateItem(0);
}

//-----------------------------------------------------------------------------
// Purpose: Sets if this panel is currently the sound panel or soundscape 
//			selector panel.
//-----------------------------------------------------------------------------
void CSoundListPanel::SetIsUsingSoundPanel(bool bUsing)
{
	bCurrentlyInSoundPanel = bUsing;

	//disable stuff
	if (bUsing)
	{
		//set 'reload' text
		m_ReloadSounds->SetText("Reload Sounds");

		m_SoundscapesList->SetVisible(false);
		m_SoundsList->SetVisible(true);

		//enable the play button
		m_PlayButton->SetEnabled(true);
		m_StopSoundButton->SetEnabled(true);

		//set texts
		m_PlayButton->SetText("Play Sound");
		m_StopSoundButton->SetText("Stop Sound");
		m_InsertButton->SetText("Insert Sound");

		//set title
		SetTitle("Sounds List", true);
	}
	else
	{
		//set 'reload' text
		m_ReloadSounds->SetText("Reload Soundscapes");

		m_SoundscapesList->SetVisible(true);
		m_SoundsList->SetVisible(false);

		//disable the play button
		m_PlayButton->SetEnabled(false);
		m_StopSoundButton->SetEnabled(false);

		//set texts
		m_PlayButton->SetText("Play Soundscape");
		m_StopSoundButton->SetText("Stop Soundscape");
		m_InsertButton->SetText("Insert Soundscape");

		//set stuff
		SetTitle("Soundscape List", true);
	}
}

//static sound list instance
static CSoundListPanel* g_SoundPanel = nullptr;


//soundscape list


#define ADD_SOUNDSCAPE_COMMAND "AddSoundscape"
#define PASTE_FROM_CLIBOARD_COMMAND "PasteFromClipboard"
#define OPEN_CLIBOARD_COMMAND "OpenClipboard"


//soundscape list class
class CSoundscapeList : public vgui::Divider
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeList, vgui::Divider);

	//constructor
	CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height);

	//menu item stuff
	virtual void AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent, KeyValues* add, SoundscapeClipboardType type);
	virtual void Clear();

	//other
	virtual void OnMouseWheeled(int delta);
	virtual void OnMouseReleased(vgui::MouseCode code);

	virtual void OnCommand(const char* pszCommand);
	virtual void PaintBackground();

	virtual void OnKeyCodeReleased(vgui::KeyCode code);

	//message funcs
	MESSAGE_FUNC_INT(ScrollBarMoved, "ScrollBarSliderMoved", position);

protected:
	friend class CSoundscapeMaker;

	//keyvalue list.
	KeyValues* m_Keyvalues = nullptr;

	//says "Soundscapes List"
	vgui::Label* m_pLabel;
	vgui::ScrollBar* m_pSideSlider;

	//menu
	vgui::Menu* menu;

	//menu button stuff
	CUtlVector<CSoundscapeButton*> m_MenuButtons;
	int m_iCurrentY;
	int m_iMax;
	int m_AmtAdded;
};

//-----------------------------------------------------------------------------
// Purpose: Constructor for soundscape list panel
//-----------------------------------------------------------------------------
CSoundscapeList::CSoundscapeList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
	: BaseClass(parent, name)
{
	//create the text
	m_pLabel = new vgui::Label(this, "ListsText", text);
	m_pLabel->SetVisible(true);
	m_pLabel->SetBounds(text_x_pos, 2, 150, 20);

	//create the side slider
	m_pSideSlider = new vgui::ScrollBar(this, "ListsSlider", true);
	m_pSideSlider->SetBounds(width - 20, 0, 20, height - 2);
	m_pSideSlider->SetValue(0);
	m_pSideSlider->SetEnabled(false);
	m_pSideSlider->SetRange(0, 0);
	m_pSideSlider->SetButtonPressedScrollValue(1);
	m_pSideSlider->SetRangeWindow(0);
	m_pSideSlider->AddActionSignalTarget(this);

	m_iCurrentY = 22;
	m_iMax = max;
	m_Keyvalues = nullptr;
}

//-----------------------------------------------------------------------------
// Purpose: adds a button to the soundscape list
//-----------------------------------------------------------------------------
void CSoundscapeList::AddButton(const char* name, const char* text, const char* command, vgui::Panel* parent, KeyValues* add, SoundscapeClipboardType type)
{
	//create a new button
	CSoundscapeButton* button = new CSoundscapeButton(this, name, text, parent, command, add, type);
	button->SetBounds(5, m_iCurrentY, GetWide() - 30, 20);

	//increment current y
	m_iCurrentY = m_iCurrentY + 22;

	//add button to array
	m_MenuButtons.AddToTail(button);

	//if the count is more then m_iMax then set slider value
	if (m_MenuButtons.Count() > m_iMax)
	{
		int max = m_MenuButtons.Count() - m_iMax;

		m_pSideSlider->SetRange(0, max);
		m_pSideSlider->SetRangeWindow(1);
		m_pSideSlider->SetEnabled(true);
	}

	m_AmtAdded++;

	//check to see if we need to scroll down
	if (m_MenuButtons.Count() >= m_iMax)
		OnMouseWheeled(-1);
}

//-----------------------------------------------------------------------------
// Purpose: Clears everything for this list
//-----------------------------------------------------------------------------
void CSoundscapeList::Clear()
{
	//reset the slider
	m_pSideSlider->SetValue(0);
	m_pSideSlider->SetEnabled(false);
	m_pSideSlider->SetRange(0, 0);
	m_pSideSlider->SetButtonPressedScrollValue(1);
	m_pSideSlider->SetRangeWindow(0);

	//delete and clear the buttons
	for (int i = 0; i < m_MenuButtons.Count(); i++)
		m_MenuButtons[i]->DeletePanel();

	m_MenuButtons.RemoveAll();

	//reset current y
	m_iCurrentY = 22;

	m_AmtAdded = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Called when a mouse is wheeled
//-----------------------------------------------------------------------------
void CSoundscapeList::OnMouseWheeled(int delta)
{
	//check for scroll down
	if (delta == -1)
		m_pSideSlider->SetValue(m_pSideSlider->GetValue() + 1);

	//check for scroll up
	else if (delta == 1)
		m_pSideSlider->SetValue(m_pSideSlider->GetValue() - 1);
}

//-----------------------------------------------------------------------------
// Purpose: Called when a mouse code is released
//-----------------------------------------------------------------------------
void CSoundscapeList::OnMouseReleased(vgui::MouseCode code)
{
	if (code != vgui::MouseCode::MOUSE_RIGHT)
		return;

	//get cursor pos
	int x, y;
	vgui::surface()->SurfaceGetCursorPos(x, y);

	//create menu
	menu = new vgui::Menu(this, "Menu");
	menu->AddMenuItem("AddSoundscape", "Add Soundscape", ADD_SOUNDSCAPE_COMMAND, this);

	//check clipboard item
	if (CurrClipboardName.Count() > 0)
	{
		menu->AddSeparator();
		menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
		menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
	}

	menu->SetBounds(x, y, 200, 50);
	menu->SetVisible(true);
}

//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeList::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, ADD_SOUNDSCAPE_COMMAND))
	{
		const char* name = CFmtStr("New Soundscape %d", m_AmtAdded);

		//add to keyvalues file
		KeyValues* kv = new KeyValues(name);
		KeyValues* tmp = m_Keyvalues;
		KeyValues* tmp2 = tmp;

		AddButton(name, name, name, GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeName);

		//get last subkey
		while (tmp != nullptr)
		{
			tmp2 = tmp;
			tmp = tmp->GetNextTrueSubKey();
		}

		//add to last subkey
		tmp2->SetNextKey(kv);

		GetParent()->OnCommand(name);
		return;
	}
	else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
	{
		int index = CurrClipboardName.Count() - 1;

		const char* name = CFmtStr("%s - (Copy %d)", CurrClipboardName[index]->GetName(), m_AmtAdded);

		//add to keyvalues file
		KeyValues* kv = new KeyValues(name);
		CurrClipboardName[index]->CopySubkeys(kv);

		KeyValues* tmp = m_Keyvalues;
		KeyValues* tmp2 = tmp;

		AddButton(name, name, name, GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeName);

		//get last subkey
		while (tmp != nullptr)
		{
			tmp2 = tmp;
			tmp = tmp->GetNextTrueSubKey();
		}

		//add to last subkey
		tmp2->SetNextKey(kv);

		GetParent()->OnCommand(name);
		return;
	}
	else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
	{
		if (g_SoundscapeClipboard)
			g_SoundscapeClipboard->DeletePanel();

		g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeName);
		return;
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Paints the background
//-----------------------------------------------------------------------------
void CSoundscapeList::PaintBackground()
{
	//colors
	static Color EnabledColor = Color(100, 100, 100, 200);
	static Color DisabledColor = Color(60, 60, 60, 200);

	//if m_KeyValues then paint the default color
	if (m_Keyvalues)
		SetBgColor(EnabledColor);
	else
		SetBgColor(DisabledColor);

	BaseClass::PaintBackground();
}

//-----------------------------------------------------------------------------
// Purpose: Called on keyboard code pressed
//-----------------------------------------------------------------------------
void CSoundscapeList::OnKeyCodeReleased(vgui::KeyCode code)
{
	//check for arrow
	if (code == KEY_UP)
	{
		//find selected item
		for (int i = 0; i < m_MenuButtons.Count(); i++)
		{
			if (m_MenuButtons[i]->m_bIsSelected)
			{
				//check for size and to see if we can select item
				if (i - 1 < 0)
					return;

				//select that item
				GetParent()->OnCommand(m_MenuButtons[i - 1]->GetCommand()->GetString("command"));
				return;
			}
		}
	}

	//check for arrow
	if (code == KEY_DOWN)
	{
		//find selected item
		for (int i = 0; i < m_MenuButtons.Count(); i++)
		{
			if (m_MenuButtons[i]->m_bIsSelected)
			{
				//check for size and to see if we can select item
				if (i + 1 >= m_MenuButtons.Count())
					return;

				//select that item
				GetParent()->OnCommand(m_MenuButtons[i + 1]->GetCommand()->GetString("command"));
				return;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Called on scroll bar moved
//-----------------------------------------------------------------------------
void CSoundscapeList::ScrollBarMoved(int delta)
{
	int position = m_pSideSlider->GetValue();

	//move everything down (if needed)
	for (int i = 0; i < m_MenuButtons.Count(); i++)
	{
		//make not visible if i < position
		if (i < position)
		{
			m_MenuButtons[i]->SetVisible(false);
			continue;
		}

		m_MenuButtons[i]->SetPos(5, 22 * ((i - position) + 1));
		m_MenuButtons[i]->SetVisible(true);
	}
}




//soundscape data list


#define NEW_PLAYLOOPING_COMMAND "NewLooping"
#define NEW_SOUNDSCAPE_COMMAND "NewSoundscape"
#define NEW_RANDOM_COMMAND "NewRandom"


class CSoundscapeDataList : public CSoundscapeList
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);

	CSoundscapeDataList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
		: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
	{}

	//override right click functionality
	virtual void OnMouseReleased(vgui::MouseCode code);

	void OnCommand(const char* pszCommand);

private:
	friend class CSoundscapeMaker;
};


//-----------------------------------------------------------------------------
// Purpose: Called when a mouse code is released
//-----------------------------------------------------------------------------
void CSoundscapeDataList::OnMouseReleased(vgui::MouseCode code)
{
	//if no soundscape is selected or mouse code != right then return
	if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
		return;

	//get cursor pos
	int x, y;
	vgui::surface()->SurfaceGetCursorPos(x, y);

	//create menu
	menu = new vgui::Menu(this, "Menu");
	menu->AddMenuItem("AddLooping", "Add Looping Sound", NEW_PLAYLOOPING_COMMAND, this);
	menu->AddMenuItem("AddSoundscape", "Add Soundscape", NEW_SOUNDSCAPE_COMMAND, this);
	menu->AddMenuItem("AddSoundscape", "Add Random Sounds", NEW_RANDOM_COMMAND, this);

	//add clipboard thing
	if (CurrClipboardData.Count() > 0)
	{
		menu->AddSeparator();
		menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
		menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
	}

	menu->SetBounds(x, y, 200, 50);
	menu->SetVisible(true);
}


//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeDataList::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, NEW_PLAYLOOPING_COMMAND))
	{
		int LoopingNum = 0;
		FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
		{
			//store data name
			const char* name = data->GetName();

			//increment variables based on name
			if (!Q_strcasecmp(name, "playlooping"))
				LoopingNum++;
		}

		//add the keyvalues
		KeyValues* kv = new KeyValues("playlooping");
		kv->SetFloat("volume", 1);
		kv->SetInt("pitch", 100);

		//add the keyvalue to both this and the keyvalues
		AddButton("playlooping", "playlooping", CFmtStr("$playlooping%d", LoopingNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);

		m_Keyvalues->AddSubKey(kv);

		GetParent()->OnCommand(CFmtStr("$playlooping%d", LoopingNum + 1));

		return;
	}
	else if (!Q_strcmp(pszCommand, NEW_SOUNDSCAPE_COMMAND))
	{
		int SoundscapeNum = 0;
		FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
		{
			//store data name
			const char* name = data->GetName();

			//increment variables based on name
			if (!Q_strcasecmp(name, "playsoundscape"))
				SoundscapeNum++;
		}

		//add the keyvalues
		KeyValues* kv = new KeyValues("playsoundscape");
		kv->SetFloat("volume", 1);

		AddButton("playsoundscape", "playsoundscape", CFmtStr("$playsoundscape%d", SoundscapeNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);

		//add the keyvalue to both this and the keyvalues
		m_Keyvalues->AddSubKey(kv);

		GetParent()->OnCommand(CFmtStr("$playsoundscape%d", SoundscapeNum + 1));

		return;
	}
	else if (!Q_strcmp(pszCommand, NEW_RANDOM_COMMAND))
	{
		int RandomNum = 0;
		FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
		{
			//store data name
			const char* name = data->GetName();

			//increment variables based on name
			if (!Q_strcasecmp(name, "playrandom"))
				RandomNum++;
		}

		//add the keyvalues
		KeyValues* kv = new KeyValues("playrandom");


		kv->SetString("volume", "0.5,0.8");
		kv->SetInt("pitch", 100);
		kv->SetString("time", "10,20");

		AddButton("playrandom", "playrandom", CFmtStr("$playrandom%d", RandomNum + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);

		//make rndwave subkey
		KeyValues* rndwave = new KeyValues("rndwave");
		kv->AddSubKey(rndwave);

		//add the keyvalue to both this and the keyvalues
		m_Keyvalues->AddSubKey(kv);

		//make the parent show the new item
		GetParent()->OnCommand(CFmtStr("$playrandom%d", RandomNum + 1));

		return;
	}
	else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
	{
		int index = CurrClipboardData.Count() - 1;

		const char* type = CurrClipboardData[index]->GetName();

		//get num of that item
		int NumItem = 0;
		FOR_EACH_TRUE_SUBKEY(m_Keyvalues, data)
		{
			//store data name
			const char* name = data->GetName();

			//increment variables based on name
			if (!Q_strcasecmp(name, type))
				NumItem++;
		}

		//add the keyvalues
		KeyValues* kv = new KeyValues(type);
		CurrClipboardData[index]->CopySubkeys(kv);

		AddButton(type, type, CFmtStr("$%s%d", type, NumItem + 1), GetParent(), kv, SoundscapeClipboardType::Type_SoundscapeData);

		//add the keyvalue to both this and the keyvalues
		m_Keyvalues->AddSubKey(kv);

		//make the parent show the new item
		GetParent()->OnCommand(CFmtStr("$%s%d", type, NumItem + 1));
		return;
	}
	else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
	{
		if (g_SoundscapeClipboard)
			g_SoundscapeClipboard->DeletePanel();

		g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeData);
		return;
	}

	BaseClass::OnCommand(pszCommand);
}


//soundscape rndwave data list


#define NEW_RNDWAVE_WAVE_COMMAND "NewRNDWave"


class CSoundscapeRndwaveList : public CSoundscapeList
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeDataList, CSoundscapeList);

	CSoundscapeRndwaveList(vgui::Panel* parent, const char* name, const char* text, int text_x_pos, int max, int width, int height)
		: CSoundscapeList(parent, name, text, text_x_pos, max, width, height)
	{}

	//override right click functionality
	virtual void OnMouseReleased(vgui::MouseCode code);

	void OnCommand(const char* pszCommand);

private:
	friend class CSoundscapeMaker;
};


//-----------------------------------------------------------------------------
// Purpose: Called when a mouse code is released
//-----------------------------------------------------------------------------
void CSoundscapeRndwaveList::OnMouseReleased(vgui::MouseCode code)
{
	//if no soundscape is selected or mouse code != right then return
	if (code != vgui::MouseCode::MOUSE_RIGHT || !m_Keyvalues)
		return;

	//get cursor pos
	int x, y;
	vgui::surface()->SurfaceGetCursorPos(x, y);

	//create menu
	menu = new vgui::Menu(this, "Menu");
	menu->AddMenuItem("AddRandom", "Add Random Wave", NEW_RNDWAVE_WAVE_COMMAND, this);

	//add clipboard thing
	if (CurrClipboardRandom.Count() > 0)
	{
		menu->AddSeparator();
		menu->AddMenuItem("PasteFromClipboard", "Paste", PASTE_FROM_CLIBOARD_COMMAND, this);
		menu->AddMenuItem("OpenClipboard", "Open Clipboard", OPEN_CLIBOARD_COMMAND, this);
	}

	menu->SetBounds(x, y, 200, 50);
	menu->SetVisible(true);
}


//-----------------------------------------------------------------------------
// Purpose: Called on command
//-----------------------------------------------------------------------------
void CSoundscapeRndwaveList::OnCommand(const char* pszCommand)
{
	if (!Q_strcmp(pszCommand, NEW_RNDWAVE_WAVE_COMMAND) && m_Keyvalues)
	{
		//get number of keyvalues
		int num = 0;

		FOR_EACH_VALUE(m_Keyvalues, kv)
			num++;

		KeyValues* add = new KeyValues("wave");
		add->SetString(nullptr, "");

		//add keyvalues and button
		AddButton("Rndwave", "", CFmtStr("$rndwave%d", num + 1), GetParent(), add, SoundscapeClipboardType::Type_SoundscapeRandomWave);

		m_Keyvalues->AddSubKey(add);

		//forward command to parent
		GetParent()->OnCommand(CFmtStr("$rndwave%d", num + 1));

		return;
	}

	else if (!Q_strcmp(pszCommand, PASTE_FROM_CLIBOARD_COMMAND))
	{
		//get number of keyvalues
		int num = 0;

		FOR_EACH_VALUE(m_Keyvalues, kv)
			num++;

		int index = CurrClipboardRandom.Count() - 1;

		const char* text = CurrClipboardRandom[index]->GetString();

		KeyValues* add = new KeyValues("wave");
		add->SetString(nullptr, text);

		//get last / or \ and make the string be that + 1
		char* fslash = Q_strrchr(text, '/');
		char* bslash = Q_strrchr(text, '\\');

		if (fslash > bslash)
			text = fslash + 1;
		else if (bslash > fslash)
			text = bslash + 1;

		//add keyvalues and button
		AddButton("Rndwave", text, CFmtStr("$rndwave%d", num + 1), GetParent(), add, SoundscapeClipboardType::Type_SoundscapeRandomWave);

		m_Keyvalues->AddSubKey(add);

		//forward command to parent
		GetParent()->OnCommand(CFmtStr("$rndwave%d", num + 1));
		return;
	}
	else if (!Q_strcmp(pszCommand, OPEN_CLIBOARD_COMMAND))
	{
		if (g_SoundscapeClipboard)
			g_SoundscapeClipboard->DeletePanel();

		g_SoundscapeClipboard = new CSoundscapeClipboard(SoundscapeClipboardType::Type_SoundscapeRandomWave);
		return;
	}

	BaseClass::OnCommand(pszCommand);
}


//soundscape panel


#define SOUNDSCAPE_PANEL_WIDTH 760
#define SOUNDSCAPE_PANEL_HEIGHT 630

#define NEW_BUTTON_COMMAND "$NewSoundscape"
#define SAVE_BUTTON_COMMAND "$SaveSoundscape"
#define LOAD_BUTTON_COMMAND "$LoadSoundscape"
#define OPTIONS_BUTTON_COMMAND "$ShowOptions"
#define EDIT_BUTTON_COMMAND "$Edit"
#define RESET_BUTTON_COMMAND "$ResetSoundscapes"
#define SOUNDS_LIST_BUTTON_COMMAND "$ShowSoundsList"
#define PLAY_SOUNDSCAPE_COMMAND "$PlaySoundscape"
#define RESET_SOUNDSCAPE_BUTTON_COMMAND "$ResetSoundscape"
#define DELETE_CURRENT_ITEM_COMMAND "$DeleteItem"

//static bool to determin if the soundscape panel should show or not
bool g_ShowSoundscapePanel = false;
bool g_IsPlayingSoundscape = false;

//soundscape maker panel
class CSoundscapeMaker : public vgui::Frame, CAutoGameSystem
{
public:
	DECLARE_CLASS_SIMPLE(CSoundscapeMaker, vgui::Frame)

	CSoundscapeMaker(vgui::VPANEL parent);

	//tick functions
	void OnTick();

	//other functions
	void OnClose();
	void OnCommand(const char* pszCommand);
	void Paste(SoundscapeClipboardType type);

	void PlaySelectedSoundscape();
	void LoadFile(KeyValues* file);

	void OnKeyCodePressed(vgui::KeyCode code);

	void SetSoundText(const char* text);

	//to play the soundscape on map spawn
	void LevelInitPostEntity();

	//sets the keyvalue file
	void Set(const char* buffer);

	//message pointer funcs
	MESSAGE_FUNC_CHARPTR(OnFileSelected, "FileSelected", fullpath);
	MESSAGE_FUNC_PARAMS(OnTextChanged, "TextChanged", data);

	~CSoundscapeMaker();

public:

	//the soundscape keyvalues file
	KeyValues* m_KeyValues = nullptr;

private:
	void CreateEverything();

private:
	//lists all the soundscapes
	CSoundscapeList* m_SoundscapesList;
	CSoundscapeDataList* m_pDataList;
	CSoundscapeRndwaveList* m_pSoundList;

	//buttons
	vgui::Button* m_ButtonNew = nullptr;
	vgui::Button* m_ButtonSave = nullptr;
	vgui::Button* m_ButtonLoad = nullptr;
	vgui::Button* m_ButtonOptions = nullptr;
	vgui::Button* m_EditButton = nullptr;

	//file load and save dialogs
	vgui::FileOpenDialog* m_FileSave = nullptr;
	vgui::FileOpenDialog* m_FileLoad = nullptr;
	bool m_bWasFileLoad = false;

	//text entry for name
	vgui::TextEntry* m_TextEntryName;

	//combo box for dsp effects
	vgui::ComboBox* m_DspEffects;
	vgui::ComboBox* m_SoundLevels;

	//sound data text entry
	vgui::TextEntry* m_TimeTextEntry;
	vgui::TextEntry* m_VolumeTextEntry;
	vgui::TextEntry* m_PitchTextEntry;
	vgui::TextEntry* m_PositionTextEntry;
	vgui::TextEntry* m_SoundNameTextEntry;

	//play sound button
	vgui::Button* m_SoundNamePlay;

	//play/reset soundscape buttons
	vgui::CheckButton* m_PlaySoundscapeButton;
	vgui::Button* m_ResetSoundscapeButton;
	vgui::Button* m_DeleteCurrentButton;

	//current selected soundscape
	CSoundscapeButton* m_pCurrentSelected = nullptr;

public:
	KeyValues* m_kvCurrSelected = nullptr;

private:
	KeyValues* m_kvCurrSound = nullptr;
	KeyValues* m_kvCurrRndwave = nullptr;

	int m_iCurrRndWave = 0;

	//currently in non randomwave thing
	SoundscapeMode m_iSoundscapeMode = SoundscapeMode::Mode_Random;

	//temporary added soundscapes
	CUtlVector<KeyValues*> m_TmpAddedSoundscapes;
};

//user message hook
void _SoundscapeMaker_Recieve(bf_read& bf);

//-----------------------------------------------------------------------------
// Purpose: Constructor for soundscape maker panel
//-----------------------------------------------------------------------------
CSoundscapeMaker::CSoundscapeMaker(vgui::VPANEL parent)
	: BaseClass(nullptr, "SoundscapeMaker")
{
	static bool bRegistered = false;
	if (!bRegistered)
	{
		usermessages->HookMessage("SoundscapeMaker_Recieve", _SoundscapeMaker_Recieve);
		bRegistered = true;
	}

	//set variables
	m_pCurrentSelected = nullptr;

	SetParent(parent);

	SetKeyBoardInputEnabled(true);
	SetMouseInputEnabled(true);

	SetProportional(false);
	SetTitleBarVisible(true);
	SetMinimizeButtonVisible(false);
	SetMaximizeButtonVisible(false);
	SetCloseButtonVisible(true);
	SetSizeable(false);
	SetMoveable(true);
	SetVisible(g_ShowSoundscapePanel);

	int ScreenWide, ScreenTall;
	vgui::surface()->GetScreenSize(ScreenWide, ScreenTall);

	SetTitle("Soundscape Maker (New File)", true);
	SetSize(SOUNDSCAPE_PANEL_WIDTH, SOUNDSCAPE_PANEL_HEIGHT);
	SetPos((ScreenWide - SOUNDSCAPE_PANEL_WIDTH) / 2, (ScreenTall - SOUNDSCAPE_PANEL_HEIGHT) / 2);



	//add a tick signal for every 50 ms
	vgui::ivgui()->AddTickSignal(GetVPanel(), 50);

	CreateEverything();
}

//-----------------------------------------------------------------------------
// Purpose: Creates everything for this panel
//-----------------------------------------------------------------------------
void CSoundscapeMaker::CreateEverything()
{
	//create the divider that will be the outline for the inside of the panel
	vgui::Divider* PanelOutline = new vgui::Divider(this, "InsideOutline");
	PanelOutline->SetEnabled(false);
	PanelOutline->SetBounds(5, 25, SOUNDSCAPE_PANEL_WIDTH - 10, SOUNDSCAPE_PANEL_HEIGHT - 62);

	//create the buttons
		//create the buttons
	m_ButtonNew = new vgui::Button(this, "NewButton", "New Soundscape File");
	m_ButtonNew->SetVisible(true);
	m_ButtonNew->SetBounds(7, 600, 145, 25);
	m_ButtonNew->SetCommand(NEW_BUTTON_COMMAND);
	m_ButtonNew->SetDepressedSound("ui/buttonclickrelease.wav");

	m_ButtonSave = new vgui::Button(this, "SaveButton", "Save Soundscapes");
	m_ButtonSave->SetVisible(true);
	m_ButtonSave->SetBounds(157, 600, 145, 25);
	m_ButtonSave->SetCommand(SAVE_BUTTON_COMMAND);
	m_ButtonSave->SetDepressedSound("ui/buttonclickrelease.wav");

	m_ButtonLoad = new vgui::Button(this, "LoadButton", "Load Soundscapes");
	m_ButtonLoad->SetVisible(true);
	m_ButtonLoad->SetBounds(307, 600, 145, 25);
	m_ButtonLoad->SetCommand(LOAD_BUTTON_COMMAND);
	m_ButtonLoad->SetDepressedSound("ui/buttonclickrelease.wav");

	m_ButtonOptions = new vgui::Button(this, "OptionsButton", "Show Options Panel");
	m_ButtonOptions->SetVisible(true);
	m_ButtonOptions->SetBounds(457, 600, 145, 25);
	m_ButtonOptions->SetCommand(OPTIONS_BUTTON_COMMAND);
	m_ButtonOptions->SetDepressedSound("ui/buttonclickrelease.wav");

	m_EditButton = new vgui::Button(this, "EditButton", "Show Text Editor");
	m_EditButton->SetVisible(true);
	m_EditButton->SetBounds(607, 600, 145, 25);
	m_EditButton->SetCommand(EDIT_BUTTON_COMMAND);
	m_EditButton->SetDepressedSound("ui/buttonclickrelease.wav");

	//create the soundscapes menu
	m_SoundscapesList = new CSoundscapeList(this, "SoundscapesList", "Soundscapes:", 90, 22, 300, 550);
	m_SoundscapesList->SetBounds(15, 35, 300, 550);
	m_SoundscapesList->SetVisible(true);

	//create data list
	m_pDataList = new CSoundscapeDataList(this, "SoudscapeDataList", "Soundscape Data:", 35, 10, 200, 310);
	m_pDataList->SetBounds(327, 275, 200, 310);
	m_pDataList->SetVisible(true);

	//create sound list
	m_pSoundList = new CSoundscapeRndwaveList(this, "SoudscapeDataList", "Random Sounds:", 40, 10, 200, 310);
	m_pSoundList->SetBounds(542, 275, 200, 310);
	m_pSoundList->SetVisible(true);

	//name text entry
	m_TextEntryName = new vgui::TextEntry(this, "NameTextEntry");
	m_TextEntryName->SetEnabled(false);
	m_TextEntryName->SetBounds(325, 40, 295, 20);
	m_TextEntryName->SetMaximumCharCount(256);

	//dsp effects combo box
	m_DspEffects = new vgui::ComboBox(this, "DspEffects", sizeof(g_DspEffects) / sizeof(g_DspEffects[0]), false);
	m_DspEffects->SetEnabled(false);
	m_DspEffects->SetBounds(325, 65, 295, 20);
	m_DspEffects->SetText("");
	m_DspEffects->AddActionSignalTarget(this);

	for (int i = 0; i < sizeof(g_DspEffects) / sizeof(g_DspEffects[i]); i++)
		m_DspEffects->AddItem(g_DspEffects[i], nullptr);

	//time text entry
	m_TimeTextEntry = new vgui::TextEntry(this, "TimeTextEntry");
	m_TimeTextEntry->SetBounds(325, 90, 295, 20);
	m_TimeTextEntry->SetEnabled(false);
	m_TimeTextEntry->SetVisible(true);

	//volume text entry
	m_VolumeTextEntry = new vgui::TextEntry(this, "VolumeTextEntry");
	m_VolumeTextEntry->SetBounds(325, 115, 295, 20);
	m_VolumeTextEntry->SetEnabled(false);
	m_VolumeTextEntry->SetVisible(true);

	//pitch text entry
	m_PitchTextEntry = new vgui::TextEntry(this, "PitchTextEntry");
	m_PitchTextEntry->SetBounds(325, 140, 295, 20);
	m_PitchTextEntry->SetEnabled(false);
	m_PitchTextEntry->SetVisible(true);

	//position text entry
	m_PositionTextEntry = new vgui::TextEntry(this, "PositionTextEntry");
	m_PositionTextEntry->SetBounds(325, 165, 295, 20);
	m_PositionTextEntry->SetEnabled(false);
	m_PositionTextEntry->SetVisible(true);

	//sound levels
	m_SoundLevels = new vgui::ComboBox(this, "SoundLevels", sizeof(g_SoundLevels) / sizeof(g_SoundLevels[0]), false);
	m_SoundLevels->SetEnabled(false);
	m_SoundLevels->SetBounds(325, 190, 295, 20);
	m_SoundLevels->SetText("");
	m_SoundLevels->AddActionSignalTarget(this);

	for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
		m_SoundLevels->AddItem(g_SoundLevels[i], nullptr);

	//sound name
	m_SoundNameTextEntry = new vgui::TextEntry(this, "SoundName");
	m_SoundNameTextEntry->SetBounds(325, 215, 215, 20);
	m_SoundNameTextEntry->SetEnabled(false);
	m_SoundNameTextEntry->SetVisible(true);

	//sound list button
	m_SoundNamePlay = new vgui::Button(this, "SoundPlayButton", "Sounds List");
	m_SoundNamePlay->SetBounds(545, 215, 75, 20);
	m_SoundNamePlay->SetCommand(SOUNDS_LIST_BUTTON_COMMAND);
	m_SoundNamePlay->SetEnabled(false);

	//starts the soundscape
	m_PlaySoundscapeButton = new vgui::CheckButton(this, "PlaySoundscape", "Play Soundscape");
	m_PlaySoundscapeButton->SetBounds(330, 243, 125, 20);
	m_PlaySoundscapeButton->SetCommand(PLAY_SOUNDSCAPE_COMMAND);
	m_PlaySoundscapeButton->SetEnabled(false);
	m_PlaySoundscapeButton->SetSelected(false);

	//reset soundscape button
	m_ResetSoundscapeButton = new vgui::Button(this, "ResetSoundscape", "Restart Soundscape");
	m_ResetSoundscapeButton->SetBounds(465, 243, 125, 20);
	m_ResetSoundscapeButton->SetCommand(RESET_SOUNDSCAPE_BUTTON_COMMAND);
	m_ResetSoundscapeButton->SetEnabled(false);

	//delete this item
	m_DeleteCurrentButton = new vgui::Button(this, "DeleteItem", "Delete Current Item");
	m_DeleteCurrentButton->SetBounds(595, 243, 135, 20);
	m_DeleteCurrentButton->SetCommand(DELETE_CURRENT_ITEM_COMMAND);
	m_DeleteCurrentButton->SetEnabled(false);

	//create the soundscape name text
	vgui::Label* NameLabel = new vgui::Label(this, "NameLabel", "Soundscape Name");
	NameLabel->SetBounds(635, 40, 125, 20);

	//create the soundscape dsp text
	vgui::Label* DspLabel = new vgui::Label(this, "DspLabel", "Soundscape Dsp");
	DspLabel->SetBounds(635, 65, 125, 20);

	//create the soundscape time text
	vgui::Label* TimeLabel = new vgui::Label(this, "TimeLabel", "Sound Time");
	TimeLabel->SetBounds(635, 90, 125, 20);

	//create the soundscape volumn text
	vgui::Label* VolumeLabel = new vgui::Label(this, "VolumeLabel", "Sound Volume");
	VolumeLabel->SetBounds(635, 115, 125, 20);

	//create the soundscape pitch text
	vgui::Label* PitchLabel = new vgui::Label(this, "PitchLabel", "Sound Pitch");
	PitchLabel->SetBounds(635, 140, 125, 20);

	//create the soundscape position text
	vgui::Label* PositionLabel = new vgui::Label(this, "PositionLabel", "Sound Position");
	PositionLabel->SetBounds(635, 165, 125, 20);

	//create the soundscape sound level text
	vgui::Label* SoundLevelLabel = new vgui::Label(this, "SoundLevelLabel", "Sound Level");
	SoundLevelLabel->SetBounds(635, 190, 125, 20);

	//create the soundscape sound name text
	vgui::Label* SoundName = new vgui::Label(this, "SoundName", "Sound Name");
	SoundName->SetBounds(635, 215, 125, 20);

	//create the soundscape keyvalues and load it
	m_KeyValues = new KeyValues("Empty Soundscape");

	LoadFile(m_KeyValues);
}

//-----------------------------------------------------------------------------
// Purpose: Called every tick for the soundscape maker
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnTick()
{
	//set the visibility
	static bool bPrevVisible = g_ShowSoundscapePanel;
	if (g_ShowSoundscapePanel != bPrevVisible)
		SetVisible(g_ShowSoundscapePanel);

	//set the old visibility
	bPrevVisible = g_ShowSoundscapePanel;
}

//-----------------------------------------------------------------------------
// Purpose: Called when the close button is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnClose()
{
	//hide the other panels
	g_SoundPanel->OnClose();
	g_SettingsPanel->OnClose();
	g_SoundscapeTextPanel->OnClose();

	g_ShowSoundscapePanel = false;
}

//-----------------------------------------------------------------------------
// Purpose: Play the selected soundscape
//-----------------------------------------------------------------------------
void CSoundscapeMaker::PlaySelectedSoundscape()
{
	//set debug stuff
	SoundscapePrint(Color(255, 255, 255, 255), "\n\n\n=============== %s %s =================\n\n", m_kvCurrSelected ? "Starting Soundscape: " : "Stopping Current Soundscape", m_kvCurrSelected ? m_kvCurrSelected->GetName() : "");
	g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Clear();

	g_IsPlayingSoundscape = true;
	g_bSSMHack = true;

	//remove all the temporary soundscapes from the soundscape system
	for (int i = 0; i < m_TmpAddedSoundscapes.Count(); i++)
	{
		for (int j = 0; j < g_SoundscapeSystem.m_soundscapes.Count(); j++)
		{
			if (g_SoundscapeSystem.m_soundscapes[j] == m_TmpAddedSoundscapes[i])
			{
				g_SoundscapeSystem.m_soundscapes.Remove(j);
				break;
			}
		}
	}

	m_TmpAddedSoundscapes.RemoveAll();

	//change audio params position
	g_SoundscapeSystem.m_params.localBits = 0x7f;
	for (int i = 0; i < MAX_SOUNDSCAPES - 1; i++)
		g_SoundscapeSystem.m_params.localSound.Set(i, g_SoundscapePositions[i]);


	//if m_kvCurrSelected then add all the "playsoundscape" soundscape keyvalues
	//into the g_SoundscapeSystem.m_soundscapes array
	if (m_kvCurrSelected)
	{
		CUtlVector<const char*> SoundscapeNames;
		FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, subkey)
		{
			//look for playsoundscape file
			if (!Q_strcasecmp(subkey->GetName(), "playsoundscape"))
			{
				const char* name = subkey->GetString("name", nullptr);
				if (!name || !name[0] || SoundscapeNames.Find(name) != SoundscapeNames.InvalidIndex())
					continue;

				SoundscapeNames.AddToTail(name);
			}
		}

		//now look for each keyvalue
		for (int i = 0; i < SoundscapeNames.Count(); i++)
		{
			for (KeyValues* subkey = m_KeyValues; subkey != nullptr; subkey = subkey->GetNextTrueSubKey())
			{
				//look for playsoundscape file
				if (!Q_strcmp(subkey->GetName(), SoundscapeNames[i]))
				{
					//add it to the soundscape system
					m_TmpAddedSoundscapes.AddToTail(subkey);
					g_SoundscapeSystem.m_soundscapes.AddToTail(subkey);
				}
			}
		}
	}

	//stop all sounds
	enginesound->StopAllSounds(true);

	//stop the current soundscape and start a new soundscape
	g_SoundscapeSystem.StartNewSoundscape(nullptr);
	g_SoundscapeSystem.StartNewSoundscape(m_kvCurrSelected);

	//start debug graphs
	g_SoundscapeDebugPanel->m_PanelSoundscapesFadingIn->Start();

	g_bSSMHack = false;
}

//-----------------------------------------------------------------------------
// Purpose: Called when a button or something else gets pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnCommand(const char* pszCommand)
{
	//check for close command first
	if (!Q_strcmp(pszCommand, "Close"))
	{
		BaseClass::OnCommand(pszCommand);
		return;
	}

	//check for the save button command
	else if (!Q_strcmp(pszCommand, SAVE_BUTTON_COMMAND))
	{
		//initalize the file save dialog
		if (!m_FileSave)
		{
			//get the current game directory
			char buf[512];
			filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));

			//create the save dialog
			m_FileSave = new vgui::FileOpenDialog(this, "Save Soundscape File", false);
			m_FileSave->AddFilter("*.txt", "Soundscape Text File", true);
			m_FileSave->AddFilter("*.*", "All Files (*.*)", false);
			m_FileSave->SetStartDirectory(buf);
			m_FileSave->AddActionSignalTarget(this);
		}

		//show the dialog
		m_FileSave->DoModal(false);
		m_FileSave->Activate();

		//file wasnt loadad
		m_bWasFileLoad = false;

		return;
	}

	//check for load button command
	else if (!Q_strcmp(pszCommand, LOAD_BUTTON_COMMAND))
	{
		//initalize the file save dialog
		if (!m_FileLoad)
		{
			//get the current game directory
			char buf[512];
			filesystem->RelativePathToFullPath("scripts", "MOD", buf, sizeof(buf));

			//create the load dialog
			m_FileLoad = new vgui::FileOpenDialog(this, "Load Soundscape File", true);
			m_FileLoad->AddFilter("*.txt", "Soundscape Text File", true);
			m_FileLoad->AddFilter("*.*", "All Files (*.*)", false);
			m_FileLoad->SetStartDirectory(buf);
			m_FileLoad->AddActionSignalTarget(this);
		}

		//show the file load dialog
		m_FileLoad->DoModal(false);
		m_FileLoad->Activate();

		//file was loadad
		m_bWasFileLoad = true;

		return;
	}

	//check for options panel button
	else if (!Q_strcmp(pszCommand, OPTIONS_BUTTON_COMMAND))
	{
		g_SettingsPanel->SetVisible(true);
		g_SettingsPanel->MoveToFront();
		g_SettingsPanel->RequestFocus();
		return;
	}

	//check for edit panel button
	else if (!Q_strcmp(pszCommand, EDIT_BUTTON_COMMAND))
	{
		g_SoundscapeTextPanel->SetVisible(true);
		g_SoundscapeTextPanel->MoveToFront();
		g_SoundscapeTextPanel->RequestFocus();
		g_SoundscapeTextPanel->Set(m_KeyValues);
		return;
	}

	//check for new soundscape
	else if (!Q_strcmp(pszCommand, NEW_BUTTON_COMMAND))
	{
		//make sure you want to create a new soundscape file
		vgui::QueryBox* popup = new vgui::QueryBox("New File?", "Are you sure you want to create a new soundscape file?", this);
		popup->SetOKCommand(new KeyValues("Command", "command", RESET_BUTTON_COMMAND));
		popup->SetCancelButtonVisible(false);
		popup->AddActionSignalTarget(this);
		popup->DoModal(this);

		return;
	}

	//check for reset soundscape
	else if (!Q_strcmp(pszCommand, RESET_BUTTON_COMMAND))
	{
		m_kvCurrSelected = nullptr;

		//stop all soundscapes before deleting the old soundscapes
		if (g_IsPlayingSoundscape)
			PlaySelectedSoundscape();

		m_KeyValues->deleteThis();
		m_KeyValues = new KeyValues("Empty Soundscape");

		//reset title
		SetTitle("Soundscape Maker (New File)", true);

		LoadFile(m_KeyValues);
		return;
	}

	//check for play sound
	else if (!Q_strcmp(pszCommand, SOUNDS_LIST_BUTTON_COMMAND))
	{
		//initalize the sounds
		static bool g_SoundPanelInitalized = false;
		if (!g_SoundPanelInitalized)
		{
			g_SoundPanelInitalized = true;
			g_SoundPanel->InitalizeSounds();
		}

		//get sound text entry name
		char buf[512];
		m_SoundNameTextEntry->GetText(buf, sizeof(buf));

		//check the current mode
		if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
		{
			g_SoundPanel->SetIsUsingSoundPanel(false);

			//load all the temporary soundscapes
			CUtlVector<const char*> OtherSoundscapes;
			for (KeyValues* curr = m_KeyValues; curr; curr = curr->GetNextKey())
			{
				if (curr == m_kvCurrSelected)
					continue;

				OtherSoundscapes.AddToTail(curr->GetName());
			}

			g_SoundPanel->InitalizeSoundscapes(OtherSoundscapes);
		}
		else
		{
			g_SoundPanel->SetIsUsingSoundPanel(true);

			//look for item with same name
			for (int i = 0; i < g_SoundDirectories.Count(); i++)
			{
				if (!Q_strcmp(buf, g_SoundDirectories[i]))
				{
					//select item
					g_SoundPanel->m_SoundsList->ActivateItem(i);
					g_SoundPanel->m_SoundsList->SetText(buf);

					break;
				}
			}
		}

		g_SoundPanel->SetVisible(true);
		g_SoundPanel->MoveToFront();
		g_SoundPanel->RequestFocus();
		return;
	}

	//check for play soundscape
	else if (!Q_strcmp(pszCommand, PLAY_SOUNDSCAPE_COMMAND))
	{
		if (m_PlaySoundscapeButton->IsSelected())
		{
			//enable the reset soundscape button
			m_ResetSoundscapeButton->SetEnabled(true);

			//play the soundscape
			PlaySelectedSoundscape();
		}
		else
		{
			//disable the reset soundscape button
			m_ResetSoundscapeButton->SetEnabled(false);

			g_IsPlayingSoundscape = false;

			//stop all sounds and soundscapes
			enginesound->StopAllSounds(true);
			g_SoundscapeSystem.StartNewSoundscape(nullptr);
		}

		return;
	}

	//check for play soundscape
	else if (!Q_strcmp(pszCommand, RESET_SOUNDSCAPE_BUTTON_COMMAND))
	{
		PlaySelectedSoundscape();
		return;
	}


	//check for delete item
	else if (!Q_strcmp(pszCommand, DELETE_CURRENT_ITEM_COMMAND))
	{
		//check for current rndwave
		if (m_kvCurrRndwave && m_SoundNameTextEntry->IsEnabled())
		{
			if (!m_kvCurrRndwave || m_iCurrRndWave <= 0)
				return;

			//get the keyvalues by the index
			int curr = 0;
			KeyValues* prev = nullptr;

			FOR_EACH_VALUE(m_kvCurrRndwave, keyvalues)
			{
				if (++curr == m_iCurrRndWave)
				{
					//delete
					if (prev)
						prev->SetNextKey(keyvalues->GetNextValue());
					else
					{
						m_kvCurrRndwave->m_pSub = keyvalues->GetNextValue();
						m_iCurrRndWave = -1;
					}

					curr = curr - 1;

					keyvalues->SetNextKey(nullptr);
					keyvalues->deleteThis();
					break;
				}



				prev = keyvalues;
			}

			//reset everything
			m_SoundNameTextEntry->SetText("");
			m_SoundNameTextEntry->SetEnabled(false);
			m_SoundNamePlay->SetEnabled(false);

			//store vector
			auto& vec = m_pSoundList->m_MenuButtons;

			//remove it
			delete vec[curr];
			vec.Remove(curr);

			//move everything down
			m_pSoundList->m_iCurrentY = m_pSoundList->m_iCurrentY - 22;
			m_pSoundList->m_Keyvalues = m_kvCurrRndwave;

			if (vec.Count() >= m_pSoundList->m_iMax)
			{
				m_pSoundList->OnMouseWheeled(1);

				int min, max;
				m_pSoundList->m_pSideSlider->GetRange(min, max);
				m_pSoundList->m_pSideSlider->SetRange(0, max - 1);
			}

			for (int i = curr; i < vec.Count(); i++)
			{
				//move everything down
				int x, y = 0;
				vec[i]->GetPos(x, y);
				vec[i]->SetPos(x, y - 22);
			}

			//reset every command
			int WaveAmount = 0;
			for (int i = 0; i < vec.Count(); i++)
			{
				//store data name
				const char* name = vec[i]->GetCommand()->GetString("command");

				//increment variables based on name
				if (Q_stristr(name, "$rndwave") == name)
				{
					WaveAmount++;
					vec[i]->SetCommand(CFmtStr("$rndwave%d", WaveAmount));
				}
			}

			//bounds check
			if (vec.Count() <= 0)
			{
				m_kvCurrRndwave = nullptr;
				m_pSoundList->m_Keyvalues = nullptr;

				//restart soundscape
				PlaySelectedSoundscape();

				return;
			}

			//select next item
			if (m_iCurrRndWave <= vec.Count())
				OnCommand(CFmtStr("$rndwave%d", curr + 1));
			else
				OnCommand(CFmtStr("$rndwave%d", curr));
		}
		else if (m_kvCurrSound)
		{
			//find keyvalue with same pointer and get the index
			int tmpindex = 0;
			int index = -1;

			KeyValues* prev = nullptr;
			FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, keyvalues)
			{
				if (m_kvCurrSound == keyvalues)
				{
					//remove it
					if (prev)
						prev->SetNextKey(keyvalues->GetNextTrueSubKey());
					else
						m_kvCurrSelected->m_pSub = keyvalues->GetNextTrueSubKey();

					keyvalues->SetNextKey(nullptr);
					keyvalues->deleteThis();

					//get index
					index = tmpindex;
					break;
				}

				prev = keyvalues;

				//increment
				tmpindex++;
			}

			//error
			if (index == -1)
				return;

			//store vector
			auto& vec = m_pDataList->m_MenuButtons;

			//remove it
			delete vec[index];
			vec.Remove(index);

			//move everything down
			m_pDataList->m_iCurrentY = m_pDataList->m_iCurrentY - 22;
			m_pDataList->m_Keyvalues = m_kvCurrSelected;

			for (int i = index; i < vec.Count(); i++)
			{
				//move everything down
				int x, y = 0;
				vec[i]->GetPos(x, y);
				vec[i]->SetPos(x, y - 22);
			}

			if (vec.Count() >= m_pDataList->m_iMax)
			{
				m_pDataList->OnMouseWheeled(1);

				int min, max;
				m_pDataList->m_pSideSlider->GetRange(min, max);

				if (max > 0)
					m_pDataList->m_pSideSlider->SetRange(0, max - 1);
				else
					m_pDataList->m_pSideSlider->SetRange(0, 0);
			}

			//reset the names of each button
			int RandomNum = 0;
			int LoopingNum = 0;
			int SoundscapeNum = 0;

			//change the commands of the buttons
			for (int i = 0; i < vec.Count(); i++)
			{
				//store data name
				const char* name = vec[i]->GetCommand()->GetString("command");

				//increment variables based on name
				if (Q_stristr(name, "$playrandom") == name)
				{
					RandomNum++;
					vec[i]->SetCommand(CFmtStr("$playrandom%d", RandomNum));
				}

				if (Q_stristr(name, "$playlooping") == name)
				{
					LoopingNum++;
					vec[i]->SetCommand(CFmtStr("$playlooping%d", LoopingNum));
				}

				if (Q_stristr(name, "$playsoundscape") == name)
				{
					SoundscapeNum++;
					vec[i]->SetCommand(CFmtStr("$playsoundscape%d", SoundscapeNum));
				}
			}

			//reset everything
			m_SoundLevels->SetText("");
			m_SoundNameTextEntry->SetText("");
			m_TimeTextEntry->SetText("");
			m_PitchTextEntry->SetText("");
			m_PositionTextEntry->SetText("");
			m_VolumeTextEntry->SetText("");

			m_SoundLevels->SetEnabled(false);
			m_SoundNameTextEntry->SetEnabled(false);
			m_TimeTextEntry->SetEnabled(false);
			m_PitchTextEntry->SetEnabled(false);
			m_PositionTextEntry->SetEnabled(false);
			m_VolumeTextEntry->SetEnabled(false);
			m_SoundNamePlay->SetEnabled(false);

			m_pSoundList->Clear();

			m_kvCurrSound = nullptr;
			m_pSoundList->m_Keyvalues = nullptr;

			//bounds checking
			if (index >= vec.Count())
				index = vec.Count() - 1; // fix bounds more safely

			//select the button
			if (index >= 0)
				OnCommand(vec[index]->GetCommand()->GetString("command"));
		}
		else if (m_kvCurrSelected)
		{
			if (m_KeyValues == m_kvCurrSelected)
			{
				//play an error sound
				vgui::surface()->PlaySound("resource/warning.wav");

				//show an error
				vgui::QueryBox* popup = new vgui::QueryBox("Error", "Can not delete base soundscape!", this);
				popup->SetOKButtonText("Ok");
				popup->SetCancelButtonVisible(false);
				popup->AddActionSignalTarget(this);
				popup->DoModal(this);

				return;
			}

			//find keyvalue with same pointer and get the index
			int tmpindex = 0;
			int index = -1;

			KeyValues* prev = nullptr;
			for (KeyValues* keyvalues = m_KeyValues; keyvalues != nullptr; keyvalues = keyvalues->GetNextTrueSubKey())
			{
				if (m_kvCurrSelected == keyvalues)
				{
					//remove it
					if (!prev)
						break;

					prev->SetNextKey(keyvalues->GetNextTrueSubKey());
					keyvalues->SetNextKey(nullptr);
					keyvalues->deleteThis();

					//get index
					index = tmpindex;
					break;
				}

				prev = keyvalues;

				//increment
				tmpindex++;
			}

			//error
			if (index == -1)
				return;

			//store vector
			auto& vec = m_SoundscapesList->m_MenuButtons;

			//remove it
			delete vec[index];
			vec.Remove(index);

			//move everything down
			m_SoundscapesList->m_iCurrentY = m_SoundscapesList->m_iCurrentY - 22;

			for (int i = index; i < vec.Count(); i++)
			{
				//move everything down
				int x, y = 0;
				vec[i]->GetPos(x, y);
				vec[i]->SetPos(x, y - 22);
			}

			if (vec.Count() >= m_SoundscapesList->m_iMax)
			{
				m_SoundscapesList->OnMouseWheeled(1);

				int min, max;
				m_SoundscapesList->m_pSideSlider->GetRange(min, max);
				m_SoundscapesList->m_pSideSlider->SetRange(0, max - 1);
			}

			//reset everything
			m_DspEffects->SetText("");
			m_SoundLevels->SetText("");
			m_TextEntryName->SetText("");
			m_SoundNameTextEntry->SetText("");
			m_TimeTextEntry->SetText("");
			m_PitchTextEntry->SetText("");
			m_PositionTextEntry->SetText("");
			m_VolumeTextEntry->SetText("");

			m_DspEffects->SetEnabled(false);
			m_SoundLevels->SetEnabled(false);
			m_TextEntryName->SetEnabled(false);
			m_SoundNameTextEntry->SetEnabled(false);
			m_TimeTextEntry->SetEnabled(false);
			m_PitchTextEntry->SetEnabled(false);
			m_PositionTextEntry->SetEnabled(false);
			m_VolumeTextEntry->SetEnabled(false);
			m_SoundNamePlay->SetEnabled(false);

			m_pDataList->Clear();
			m_pSoundList->Clear();

			m_kvCurrSound = nullptr;
			m_pDataList->m_Keyvalues = nullptr;

			//go to next soundscape
			if (!prev)
			{
				//restart soundscape
				PlaySelectedSoundscape();
				return;
			}

			if (prev->GetNextTrueSubKey())
				OnCommand(prev->GetNextTrueSubKey()->GetName());
			else
				OnCommand(prev->GetName());
		}

		//restart soundscape
		PlaySelectedSoundscape();
		return;
	}

	//check for "playrandom", "playsoundscape" or "playlooping"
	if (Q_stristr(pszCommand, "$playrandom") == pszCommand)
	{
		//get the selected number
		char* str_number = (char*)(pszCommand + 11);
		int number = atoi(str_number);
		if (number != 0)
		{
			//look for button with same command
			auto& vec = m_pDataList->m_MenuButtons;
			for (int i = 0; i < vec.Count(); i++)
			{
				//if the button doesnt have the same command then de-select it. else select it
				if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
					vec[i]->m_bIsSelected = true;
				else
					vec[i]->m_bIsSelected = false;
			}


			//clear the m_pSoundList
			m_pSoundList->Clear();
			m_pSoundList->m_Keyvalues = nullptr;

			//store variables
			KeyValues* data = nullptr;
			int curr = 0;

			//get subkey
			FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
			{
				if (Q_strcasecmp(sounds->GetName(), "playrandom"))
					continue;

				if (++curr == number)
				{
					data = sounds;
					break;
				}
			}

			//no data
			if (!data)
				return;

			m_kvCurrSound = data;
			m_kvCurrRndwave = nullptr;

			//set the random times
			m_TimeTextEntry->SetText(data->GetString("time", "10,20"));
			m_VolumeTextEntry->SetText(data->GetString("volume", "0.5,0.8"));
			m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
			m_PositionTextEntry->SetText(data->GetString("position", ""));
			m_SoundNameTextEntry->SetText("");

			//get snd level index
			int index = 8;	//8 = SNDLVL_NORM
			const char* name = data->GetString("soundlevel", nullptr);

			//check for the name
			if (name)
			{

				//loop through the sound levels to find the right one
				for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
				{
					if (!Q_strcmp(name, g_SoundLevels[i]))
					{
						index = i;
						break;
					}
				}
			}

			//select the index
			m_SoundLevels->ActivateItem(index);

			//enable the text entries
			m_TimeTextEntry->SetEnabled(true);
			m_VolumeTextEntry->SetEnabled(true);
			m_PitchTextEntry->SetEnabled(true);
			m_PositionTextEntry->SetEnabled(true);
			m_SoundLevels->SetEnabled(true);
			m_SoundNameTextEntry->SetEnabled(false);
			m_SoundNamePlay->SetEnabled(false);

			g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
			g_SoundPanel->SetVisible(false);

			//check for randomwave subkey
			if ((data = data->FindKey("rndwave")) == nullptr)
				return;

			m_kvCurrRndwave = data;
			m_pSoundList->m_Keyvalues = data;

			//add all the data
			int i = 0;
			FOR_EACH_VALUE(data, sound)
			{
				const char* name = sound->GetName();

				//get real text
				const char* text = sound->GetString();

				//get last / or \ and make the string be that + 1
				char* fslash = Q_strrchr(text, '/');
				char* bslash = Q_strrchr(text, '\\');

				//no forward slash and no back slash
				if (!fslash && !bslash)
				{
					text = text;
				}
				else
				{
					if (fslash > bslash)
						text = fslash + 1;

					else if (bslash > fslash)
						text = bslash + 1;
				}

				m_pSoundList->AddButton(name, text, CFmtStr("$rndwave%d", ++i), this, sound, SoundscapeClipboardType::Type_SoundscapeRandomWave);
			}

			m_iSoundscapeMode = SoundscapeMode::Mode_Random;
			return;
		}
	}
	else if (Q_stristr(pszCommand, "$playlooping") == pszCommand)
	{
		//get the selected number
		char* str_number = (char*)(pszCommand + 12);
		int number = atoi(str_number);
		if (number != 0)
		{
			//look for button with same command
			auto& vec = m_pDataList->m_MenuButtons;
			for (int i = 0; i < vec.Count(); i++)
			{
				//if the button doesnt have the same command then de-select it. else select it
				if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
					vec[i]->m_bIsSelected = true;
				else
					vec[i]->m_bIsSelected = false;
			}


			//clear the m_pSoundList
			m_pSoundList->Clear();
			m_pSoundList->m_Keyvalues = nullptr;

			//store variables
			KeyValues* data = nullptr;
			int curr = 0;

			//get subkey
			FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
			{
				if (Q_strcasecmp(sounds->GetName(), "playlooping"))
					continue;

				if (++curr == number)
				{
					data = sounds;
					break;
				}
			}

			//no data
			if (!data)
				return;

			m_kvCurrSound = data;
			m_kvCurrRndwave = nullptr;

			//set the random times
			m_TimeTextEntry->SetText("");
			m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
			m_PitchTextEntry->SetText(data->GetString("pitch", "100"));
			m_PositionTextEntry->SetText(data->GetString("position", ""));
			m_SoundNameTextEntry->SetText(data->GetString("wave", ""));

			//get snd level index
			int index = 8;	//8 = SNDLVL_NORM
			const char* name = data->GetString("soundlevel", nullptr);

			//check for the name
			if (name)
			{

				//loop through the sound levels to find the right one
				for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
				{
					if (!Q_strcmp(name, g_SoundLevels[i]))
					{
						index = i;
						break;
					}
				}
			}

			//select the index
			m_SoundLevels->ActivateItem(index);

			//enable the text entries
			m_TimeTextEntry->SetEnabled(false);
			m_VolumeTextEntry->SetEnabled(true);
			m_PitchTextEntry->SetEnabled(true);
			m_PositionTextEntry->SetEnabled(true);
			m_SoundLevels->SetEnabled(true);
			m_SoundNameTextEntry->SetEnabled(true);
			m_SoundNamePlay->SetEnabled(true);
			g_SoundPanel->SetVisible(false);

			m_iSoundscapeMode = SoundscapeMode::Mode_Looping;
			return;
		}
	}
	else if (Q_stristr(pszCommand, "$playsoundscape") == pszCommand)
	{
		//get the selected number
		char* str_number = (char*)(pszCommand + 15);
		int number = atoi(str_number);
		if (number != 0)
		{
			//look for button with same command
			auto& vec = m_pDataList->m_MenuButtons;
			for (int i = 0; i < vec.Count(); i++)
			{
				//if the button doesnt have the same command then de-select it. else select it
				if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
					vec[i]->m_bIsSelected = true;
				else
					vec[i]->m_bIsSelected = false;
			}


			//clear the m_pSoundList
			m_pSoundList->Clear();
			m_pSoundList->m_Keyvalues = nullptr;

			//store variables
			KeyValues* data = nullptr;
			int curr = 0;

			//get subkey
			FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, sounds)
			{
				if (Q_strcasecmp(sounds->GetName(), "playsoundscape"))
					continue;

				if (++curr == number)
				{
					data = sounds;
					break;
				}
			}

			//no data
			if (!data)
				return;

			m_kvCurrSound = data;
			m_kvCurrRndwave = nullptr;

			//set the random times
			m_TimeTextEntry->SetText("");
			m_VolumeTextEntry->SetText(data->GetString("volume", "1"));
			m_PositionTextEntry->SetText(data->GetString("positionoverride", ""));
			m_SoundNameTextEntry->SetText(data->GetString("name", ""));
			m_PitchTextEntry->SetText("");

			//get snd level index
			int index = 8;	//8 = SNDLVL_NORM
			const char* name = data->GetString("soundlevel", nullptr);

			//check for the name
			if (name)
			{

				//loop through the sound levels to find the right one
				for (int i = 0; i < sizeof(g_SoundLevels) / sizeof(g_SoundLevels[i]); i++)
				{
					if (!Q_strcmp(name, g_SoundLevels[i]))
					{
						index = i;
						break;
					}
				}
			}

			//select the index
			m_SoundLevels->ActivateItem(index);

			//enable the text entries
			m_TimeTextEntry->SetEnabled(true);
			m_VolumeTextEntry->SetEnabled(true);
			m_PitchTextEntry->SetEnabled(false);
			m_PositionTextEntry->SetEnabled(true);
			m_SoundLevels->SetEnabled(true);
			m_SoundNameTextEntry->SetEnabled(true);
			m_TimeTextEntry->SetEnabled(false);
			m_SoundNamePlay->SetEnabled(true);

			g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
			g_SoundPanel->SetVisible(false);

			m_iSoundscapeMode = SoundscapeMode::Mode_Soundscape;
			return;
		}
	}
	else if (Q_stristr(pszCommand, "$rndwave") == pszCommand)
	{
		if (!m_kvCurrRndwave)
			return;

		//get the selected number
		char* str_number = (char*)(pszCommand + 8);
		m_iCurrRndWave = atoi(str_number);
		if (m_iCurrRndWave != 0)
		{
			//look for button with same command
			auto& vec = m_pSoundList->m_MenuButtons;
			for (int i = 0; i < vec.Count(); i++)
			{
				//if the button doesnt have the same command then de-select it. else select it
				if (!Q_strcmp(vec[i]->GetCommand()->GetString("command"), pszCommand))
					vec[i]->m_bIsSelected = true;
				else
					vec[i]->m_bIsSelected = false;
			}


			int i = 0;

			//get value
			KeyValues* curr = nullptr;
			FOR_EACH_VALUE(m_kvCurrRndwave, wave)
			{
				if (++i == m_iCurrRndWave)
				{
					curr = wave;
					break;
				}
			}

			//if no curr then throw an error
			if (!curr)
			{
				//play an error sound
				vgui::surface()->PlaySound("resource/warning.wav");

				//show error
				char buf[1028];
				Q_snprintf(buf, sizeof(buf), "Failed to get rndwave '%d' for subkey \"%s\"\nfor current soundscape file!", i, m_kvCurrSelected->GetName());

				//show an error
				vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
				popup->SetOKButtonText("Ok");
				popup->SetCancelButtonVisible(false);
				popup->AddActionSignalTarget(this);
				popup->DoModal(this);
				return;
			}

			m_SoundNameTextEntry->SetEnabled(true);
			m_SoundNameTextEntry->SetText(curr->GetString());

			m_SoundNamePlay->SetEnabled(true);

			m_iSoundscapeMode = SoundscapeMode::Mode_Random;
			return;
		}
	}

	//look for button with the same name as the command
	{
		//store vars
		CUtlVector<CSoundscapeButton*>& array = m_SoundscapesList->m_MenuButtons;

		//de-select button
		if (m_pCurrentSelected)
			m_pCurrentSelected->m_bIsSelected = false;

		//check for name
		for (int i = 0; i < m_SoundscapesList->m_MenuButtons.Size(); i++)
		{
			//check button name
			if (!Q_strcmp(array[i]->GetCommand()->GetString("command"), pszCommand))
			{
				//found it
				m_pCurrentSelected = array[i];
				break;
			}
		}

		//set needed stuff
		if (m_pCurrentSelected)
		{
			//select button
			m_pCurrentSelected->m_bIsSelected = true;
			m_DeleteCurrentButton->SetEnabled(false);

			//reset the selected kv
			m_kvCurrSelected = nullptr;

			//find selected keyvalues
			for (KeyValues* kv = m_KeyValues; kv != nullptr; kv = kv->GetNextTrueSubKey())
			{
				if (!Q_strcmp(kv->GetName(), pszCommand))
				{
					m_kvCurrSelected = kv;
					break;
				}
			}

			//set 
			m_kvCurrSound = nullptr;
			m_kvCurrRndwave = nullptr;

			m_TimeTextEntry->SetEnabled(false);
			m_TimeTextEntry->SetText("");

			m_VolumeTextEntry->SetEnabled(false);
			m_VolumeTextEntry->SetText("");

			m_PitchTextEntry->SetEnabled(false);
			m_PitchTextEntry->SetText("");

			m_PositionTextEntry->SetEnabled(false);
			m_PositionTextEntry->SetText("");

			m_SoundLevels->SetEnabled(false);
			m_SoundLevels->SetText("");

			m_SoundNameTextEntry->SetEnabled(false);
			m_SoundNameTextEntry->SetText("");

			m_SoundNamePlay->SetEnabled(false);

			if (g_SoundPanel)
			{
				g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
				g_SoundPanel->SetVisible(false);
			}

			//check for current keyvalues. should never bee nullptr but could be
			if (!m_kvCurrSelected)
			{
				//play an error sound
				vgui::surface()->PlaySound("resource/warning.wav");

				//show error
				char buf[1028];
				Q_snprintf(buf, sizeof(buf), "Failed to find KeyValue subkey \"%s\"\nfor current soundscape file!", pszCommand);

				//show an error
				vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
				popup->SetOKButtonText("Ok");
				popup->SetCancelButtonVisible(false);
				popup->AddActionSignalTarget(this);
				popup->DoModal(this);

				//reset vars
				m_pCurrentSelected = nullptr;

				m_TextEntryName->SetEnabled(false);
				m_TextEntryName->SetText("");

				m_DspEffects->SetEnabled(false);
				m_DspEffects->SetText("");
				return;
			}

			if (g_IsPlayingSoundscape)
				PlaySelectedSoundscape();

			m_DeleteCurrentButton->SetEnabled(true);

			//set current soundscape name
			m_TextEntryName->SetText(pszCommand);
			m_TextEntryName->SetEnabled(true);
			m_pDataList->m_Keyvalues = m_kvCurrSelected;

			//set dsp effect
			int dsp = Clamp<int>(m_kvCurrSelected->GetInt("dsp"), 0, 29);

			m_PlaySoundscapeButton->SetEnabled(true);

			m_DspEffects->SetEnabled(true);
			m_DspEffects->ActivateItem(dsp);

			//clear these
			m_pDataList->Clear();
			m_pSoundList->Clear();
			m_pSoundList->m_Keyvalues = nullptr;

			//set variables
			int RandomNum = 0;
			int LoopingNum = 0;
			int SoundscapeNum = 0;

			FOR_EACH_TRUE_SUBKEY(m_kvCurrSelected, data)
			{
				//store data name
				const char* name = data->GetName();

				//increment variables based on name
				if (!Q_strcasecmp(name, "playrandom"))
				{
					RandomNum++;
					m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playrandom%d", RandomNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
				}

				if (!Q_strcasecmp(name, "playlooping"))
				{
					LoopingNum++;
					m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playlooping%d", LoopingNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
				}

				if (!Q_strcasecmp(name, "playsoundscape"))
				{
					SoundscapeNum++;
					m_pDataList->AddButton(data->GetName(), data->GetName(), CFmtStr("$playsoundscape%d", SoundscapeNum), this, data, SoundscapeClipboardType::Type_SoundscapeData);
				}
			}
		}
	}

	BaseClass::OnCommand(pszCommand);
}

//-----------------------------------------------------------------------------
// Purpose: Paste item from clipboard
//-----------------------------------------------------------------------------
void CSoundscapeMaker::Paste(SoundscapeClipboardType type)
{
	switch (type)
	{
	case SoundscapeClipboardType::Type_SoundscapeName:
		m_SoundscapesList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
		break;
	case SoundscapeClipboardType::Type_SoundscapeData:
		m_pDataList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
		break;
	case SoundscapeClipboardType::Type_SoundscapeRandomWave:
		m_pSoundList->OnCommand(PASTE_FROM_CLIBOARD_COMMAND);
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Function to recursivly write keyvalues to keyvalue files. the keyvalues
//			class does have a function to do this BUT this function writes every single
//			item one after another. this function does that but writes the keys
//			first then the subkeys so the order is good.
//-----------------------------------------------------------------------------
void RecursivlyWriteKeyvalues(KeyValues* prev, CUtlBuffer& buffer, int& indent)
{
	//write \t indent
	for (int i = 0; i < indent; i++)
		buffer.PutChar('\t');

	//write name
	buffer.PutChar('"');
	buffer.PutString(prev->GetName());
	buffer.PutString("\"\n");

	//write {
	for (int i = 0; i < indent; i++)
		buffer.PutChar('\t');

	buffer.PutString("{\n");

	//increment indent
	indent++;

	//write all the keys first
	FOR_EACH_VALUE(prev, value)
	{
		for (int i = 0; i < indent; i++)
			buffer.PutChar('\t');

		//write name and value
		buffer.PutChar('"');
		buffer.PutString(value->GetName());
		buffer.PutString("\"\t");

		buffer.PutChar('"');
		buffer.PutString(value->GetString());
		buffer.PutString("\"\n");
	}

	//write all the subkeys now
	FOR_EACH_TRUE_SUBKEY(prev, value)
	{
		//increment indent
		RecursivlyWriteKeyvalues(value, buffer, indent);

		if (value->GetNextTrueSubKey())
			buffer.PutChar('\n');
	}

	//decrement indent
	indent--;

	//write ending }
	for (int i = 0; i < indent; i++)
		buffer.PutChar('\t');

	buffer.PutString("}\n");
}

//-----------------------------------------------------------------------------
// Purpose: Called when a file gets opened/closed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnFileSelected(const char* pszFileName)
{
	//check for null or empty string
	if (!pszFileName || pszFileName[0] == '\0')
		return;

	//check for file save
	if (!m_bWasFileLoad)
	{
		//save the file
		if (m_KeyValues)
		{
			//write everything into a buffer
			CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
			buf.PutString("//------------------------------------------------------------------------------------\n");
			buf.PutString("//\n");
			buf.PutString("// Auto-generated soundscape file created with modbases soundscape tool'\n");
			buf.PutString("//\n");
			buf.PutString("//------------------------------------------------------------------------------------\n");

			//now write the keyvalues
			KeyValues* pCurrent = m_KeyValues;
			while (pCurrent)
			{
				int indent = 0;
				RecursivlyWriteKeyvalues(pCurrent, buf, indent);

				//put a newline
				if (pCurrent->GetNextTrueSubKey())
					buf.PutChar('\n');

				//get next
				pCurrent = pCurrent->GetNextTrueSubKey();
			}

			if (!g_pFullFileSystem->WriteFile(pszFileName, "MOD", buf))
			{
				//play an error sound
				vgui::surface()->PlaySound("resource/warning.wav");

				//get the error first
				char buf[1028];
				Q_snprintf(buf, sizeof(buf), "Failed to save soundscape to file \"%s\"", pszFileName);

				//show an error
				vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
				popup->SetOKButtonText("Ok");
				popup->SetCancelButtonVisible(false);
				popup->AddActionSignalTarget(this);
				popup->DoModal(this);
				return;
			}
		}


		//store vars
		const char* last = pszFileName;
		const char* tmp = nullptr;

		//get the last /
		while ((last = Q_strstr(last, "\\")) != nullptr)
			tmp = ++last; //move past the backslash

		//check tmp
		if (!tmp || !*tmp)
			tmp = pszFileName;

		//set new title
		char buf[1028];
		Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);

		SetTitle(buf, true);

		//create copy of pszFileName
		char manifest[1028];
		Q_strncpy(manifest, pszFileName, sizeof(manifest));

		//get last /
		char* lastSlash = Q_strrchr(manifest, '\\');
		if (lastSlash == nullptr || *lastSlash == '\0')
			return;

		//append 'soundscapes_manifest.txt'
		lastSlash[1] = '\0';
		strcat(manifest, "soundscapes_manifest.txt");

		//see if we can open manifest file
		KeyValues* man_file = new KeyValues("manifest");
		if (!man_file->LoadFromFile(g_pFullFileSystem, manifest))
		{
			//cant open manifest file
			man_file->deleteThis();
			return;
		}

		//get real filename
		pszFileName = Q_strrchr(pszFileName, '\\');
		if (!pszFileName || !*pszFileName)
		{
			man_file->deleteThis();
			return;
		}

		pszFileName = pszFileName + 1;

		//create name to be added to the manifest file
		char add_file[1028];
		Q_snprintf(add_file, sizeof(add_file), "scripts/%s", pszFileName);

		//add filename to manifest file if not found
		FOR_EACH_VALUE(man_file, value)
		{
			if (!Q_strcmp(value->GetString(), add_file))
			{
				man_file->deleteThis();
				return;
			}
		}


		//add to manifest file
		KeyValues* kv = new KeyValues("file");
		kv->SetString(nullptr, add_file);
		man_file->AddSubKey(kv);

		//write to file
		man_file->SaveToFile(g_pFullFileSystem, manifest);

		man_file->deleteThis();
		return;
	}

	//try and load the keyvalues file first
	KeyValues* temp = new KeyValues("SoundscapeFile");
	if (!temp->LoadFromFile(filesystem, pszFileName))
	{
		//play an error sound
		vgui::surface()->PlaySound("resource/warning.wav");

		//get the error first
		char buf[1028];
		Q_snprintf(buf, sizeof(buf), "Failed to open keyvalues file \"%s\"", pszFileName);

		//show an error
		vgui::QueryBox* popup = new vgui::QueryBox("Error", buf, this);
		popup->SetOKButtonText("Ok");
		popup->SetCancelButtonVisible(false);
		popup->AddActionSignalTarget(this);
		popup->DoModal(this);

		temp->deleteThis();
		return;
	}

	//set the new title
	{
		//store vars
		const char* last = pszFileName;
		const char* tmp = nullptr;

		//get the last /
		while ((last = Q_strstr(last, "\\")) != nullptr)
			tmp = ++last; //move past the backslash

		//check tmp
		if (!tmp || !*tmp)
			tmp = pszFileName;

		//create the new new title
		char buf[1028];
		Q_snprintf(buf, sizeof(buf), "Soundscape Maker (%s)", tmp);

		SetTitle(buf, true);
	}

	//stop all soundscapes before deleting the old soundscapes
	m_kvCurrSelected = nullptr;

	if (g_IsPlayingSoundscape)
		PlaySelectedSoundscape();

	//delete and set the old keyvalues
	if (m_KeyValues)
		m_KeyValues->deleteThis();

	m_KeyValues = temp;

	//load the file
	LoadFile(m_KeyValues);
}

//-----------------------------------------------------------------------------
// Purpose: Called when a text thing changes
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnTextChanged(KeyValues* keyvalues)
{
	//check for these things
	if (!m_pCurrentSelected || !m_kvCurrSelected)
		return;

	//check to see if the current focus is the text text entry
	if (m_TextEntryName->HasFocus())
	{
		//get text
		char buf[50];
		m_TextEntryName->GetText(buf, sizeof(buf));

		//set current text and keyvalue name
		m_kvCurrSelected->SetName(buf);
		m_pCurrentSelected->SetText(buf);
		m_pCurrentSelected->SetCommand(buf);
		return;
	}

	//set dsp
	m_kvCurrSelected->SetInt("dsp", Clamp<int>(m_DspEffects->GetActiveItem(), 0, 28));

	//if the m_kvCurrSound is nullptr then dont do the rest of the stuff
	if (!m_kvCurrSound)
		return;

	//set the curr sound and stuff
	if (m_TimeTextEntry->HasFocus())
	{
		//get text
		char buf[38];
		m_TimeTextEntry->GetText(buf, sizeof(buf));

		m_kvCurrSound->SetString("time", buf);
		return;
	}
	else if (m_VolumeTextEntry->HasFocus())
	{
		//get text
		char buf[38];
		m_VolumeTextEntry->GetText(buf, sizeof(buf));

		m_kvCurrSound->SetString("volume", buf);
		return;
	}
	else if (m_PitchTextEntry->HasFocus())
	{
		//dont add to soundscaep
		if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
			return;

		//get text
		char buf[38];
		m_PitchTextEntry->GetText(buf, sizeof(buf));

		m_kvCurrSound->SetString("pitch", buf);
		return;
	}
	else if (m_PositionTextEntry->HasFocus())
	{
		//get text
		char buf[38];
		m_PositionTextEntry->GetText(buf, sizeof(buf));

		//if the string is empty then remove the position instead
		if (!buf[0])
		{
			if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
				m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("positionoverride"));
			else
				m_kvCurrSound->RemoveSubKey(m_kvCurrSound->FindKey("position"));
		}
		else
		{
			if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
				m_kvCurrSound->SetString("positionoverride", buf);
			else
				m_kvCurrSound->SetString("position", buf);

		}

		return;
	}

	//get the sound level amount
	int sndlevel = Clamp<int>(m_SoundLevels->GetActiveItem(), 0, 20);
	m_kvCurrSound->SetString("soundlevel", g_SoundLevels[sndlevel]);

	//set soundscape name/wave
	if (m_SoundNameTextEntry->HasFocus())
	{
		//get text
		char buf[512];
		m_SoundNameTextEntry->GetText(buf, sizeof(buf));

		if (m_iSoundscapeMode == SoundscapeMode::Mode_Looping)
			m_kvCurrSound->SetString("wave", buf);
		else if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
			m_kvCurrSound->SetString("name", buf);
		else if (m_iSoundscapeMode == SoundscapeMode::Mode_Random && m_kvCurrRndwave)
		{
			//get value
			int i = 0;

			FOR_EACH_VALUE(m_kvCurrRndwave, wave)
			{
				if (++i == m_iCurrRndWave)
				{
					wave->SetStringValue(buf);

					//set text on the sounds panel
					vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
					if (button)
					{
						//get last / or \ and make the string be that + 1
						char* fslash = Q_strrchr(buf, '/');
						char* bslash = Q_strrchr(buf, '\\');

						//no forward slash and no back slash
						if (!fslash && !bslash)
						{
							button->SetText(buf);
							return;
						}

						if (fslash > bslash)
						{
							button->SetText(fslash + 1);
							return;
						}

						else if (bslash > fslash)
						{
							button->SetText(bslash + 1);
							return;
						}
					}

					break;
				}
			}
		}

		return;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Loads the keyvalues
//-----------------------------------------------------------------------------
void CSoundscapeMaker::LoadFile(KeyValues* file)
{
	//clear all the text's
	m_TextEntryName->SetEnabled(false);
	m_TextEntryName->SetText("");

	m_DspEffects->SetEnabled(false);
	m_DspEffects->SetText("");

	m_TimeTextEntry->SetEnabled(false);
	m_TimeTextEntry->SetText("");

	m_VolumeTextEntry->SetEnabled(false);
	m_VolumeTextEntry->SetText("");

	m_PitchTextEntry->SetEnabled(false);
	m_PitchTextEntry->SetText("");

	m_PositionTextEntry->SetEnabled(false);
	m_PositionTextEntry->SetText("");

	m_SoundNameTextEntry->SetEnabled(false);
	m_SoundNameTextEntry->SetText("");

	m_SoundNamePlay->SetEnabled(false);

	if (g_SoundPanel)
	{
		g_SoundPanel->OnCommand(SOUND_LIST_STOP_COMMAND);
		g_SoundPanel->SetVisible(false);
	}

	m_PlaySoundscapeButton->SetEnabled(false);
	m_PlaySoundscapeButton->SetSelected(false);

	m_ResetSoundscapeButton->SetEnabled(false);
	m_DeleteCurrentButton->SetEnabled(false);

	//clear current file
	m_pCurrentSelected = nullptr;
	m_kvCurrSelected = nullptr;
	m_kvCurrSound = nullptr;
	m_kvCurrRndwave = nullptr;

	//clear the menu items
	m_SoundscapesList->Clear();
	m_pSoundList->Clear();
	m_pDataList->Clear();

	m_SoundscapesList->m_Keyvalues = file;
	m_pDataList->m_Keyvalues = nullptr;
	m_pSoundList->m_Keyvalues = nullptr;

	g_IsPlayingSoundscape = false;

	//temp soundscapes list
	CUtlVector<const char*> Added;

	//add all the menu items
	for (KeyValues* soundscape = file; soundscape != nullptr; soundscape = soundscape->GetNextTrueSubKey())
	{
		//add the menu buttons
		const char* name = soundscape->GetName();

		//check for the soundscape first
		if (Added.Find(name) != Added.InvalidIndex())
		{
			ConWarning("CSoundscapePanel: Failed to add repeated soundscape '%s'\n", name);
			continue;
		}

		Added.AddToTail(name);
		m_SoundscapesList->AddButton(name, name, name, this, soundscape, SoundscapeClipboardType::Type_SoundscapeName);
	}

	m_SoundscapesList->m_pSideSlider->SetValue(0);
	m_SoundscapesList->ScrollBarMoved(0);

	//
	OnCommand(file->GetName());
}

//-----------------------------------------------------------------------------
// Purpose: Sets the sounds text
//-----------------------------------------------------------------------------
void CSoundscapeMaker::SetSoundText(const char* text)
{
	m_SoundNameTextEntry->SetText(text);

	//set soundscape name/wave
	if (m_iSoundscapeMode != SoundscapeMode::Mode_Random)
	{
		if (m_iSoundscapeMode == SoundscapeMode::Mode_Soundscape)
			m_kvCurrSound->SetString("name", text);
		else
			m_kvCurrSound->SetString("wave", text);
	}
	else
	{
		//get value
		int i = 0;

		FOR_EACH_VALUE(m_kvCurrRndwave, wave)
		{
			if (++i == m_iCurrRndWave)
			{
				wave->SetStringValue(text);

				//set text on the sounds panel
				vgui::Button* button = m_pSoundList->m_MenuButtons[i - 1];
				if (button)
				{
					//get last / or \ and make the string be that + 1
					char* fslash = Q_strrchr(text, '/');
					char* bslash = Q_strrchr(text, '\\');

					//no forward slash and no back slash
					if (!fslash && !bslash)
					{
						button->SetText(text);
						return;
					}

					if (fslash > bslash)
					{
						button->SetText(fslash + 1);
						return;
					}

					else if (bslash > fslash)
					{
						button->SetText(bslash + 1);
						return;
					}
				}

				break;
			}
		}
	}

	return;
}

//-----------------------------------------------------------------------------
// Purpose: Called when a keyboard key is pressed
//-----------------------------------------------------------------------------
void CSoundscapeMaker::OnKeyCodePressed(vgui::KeyCode code)
{
	//check for ctrl o or ctrl s
	if (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LCONTROL) ||
		vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RCONTROL))
	{
		if (code == vgui::KeyCode::KEY_O)
			OnCommand(LOAD_BUTTON_COMMAND);
		else if (code == vgui::KeyCode::KEY_S)
			OnCommand(SAVE_BUTTON_COMMAND);
		else if (code == vgui::KeyCode::KEY_N)
			OnCommand(NEW_BUTTON_COMMAND);
		else if (code == vgui::KeyCode::KEY_P)
		{
			//show settings
			g_SettingsPanel->SetVisible(true);
			g_SettingsPanel->RequestFocus();
			g_SettingsPanel->MoveToFront();
		}

		else if (code == vgui::KeyCode::KEY_D)
			OnCommand(DELETE_CURRENT_ITEM_COMMAND);

		//check for ctrl+alt+a
		else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LALT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RALT)) && m_pSoundList && m_pSoundList->m_Keyvalues)
			m_pSoundList->OnCommand(NEW_RNDWAVE_WAVE_COMMAND);

		//check for just ctrl+shift+a
		else if (code == vgui::KeyCode::KEY_A && (vgui::input()->IsKeyDown(vgui::KeyCode::KEY_LSHIFT) || vgui::input()->IsKeyDown(vgui::KeyCode::KEY_RSHIFT)) && m_SoundscapesList)
			m_SoundscapesList->OnCommand(ADD_SOUNDSCAPE_COMMAND);

		return;
	}

	//check for arrow keys
	if (code == KEY_DOWN || code == KEY_UP)
	{
		if (m_kvCurrRndwave)
		{
			m_pSoundList->OnKeyCodePressed(code);
		}
		else if (m_kvCurrSelected && m_pDataList->m_MenuButtons.Count())
		{
			m_pDataList->OnKeyCodePressed(code);
		}
		else
		{
			m_SoundscapesList->OnKeyCodePressed(code);
		}

		return;
	}

	//get key bound to this
	const char* key = engine->Key_LookupBinding("modbase_soundscape_panel");
	if (!key)
		return;

	//convert the key to a keyboard code
	const char* keystring = KeyCodeToString(code);

	//remove the KEY_ if found
	if (Q_strstr(keystring, "KEY_") == keystring)
		keystring = keystring + 4;

	//check both strings
	if (!Q_strcasecmp(key, keystring))
		OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: Starts the soundscape on map spawn
//-----------------------------------------------------------------------------
void CSoundscapeMaker::LevelInitPostEntity()
{
	if (g_IsPlayingSoundscape)
		PlaySelectedSoundscape();
}

//-----------------------------------------------------------------------------
// Purpose: Sets keyvalues from text
//-----------------------------------------------------------------------------
void CSoundscapeMaker::Set(const char* buffer)
{
	CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER);
	buf.PutString(buffer);

	//try and load the keyvalues file first
	KeyValues* temp = new KeyValues("SoundscapeFile");
	if (!temp->LoadFromBuffer("Text Editor Panel Buffer", buf))
	{
		//play an error sound
		vgui::surface()->PlaySound("resource/warning.wav");

		//show an error
		vgui::QueryBox* popup = new vgui::QueryBox("Error", "Failed to open keyvalues data from \"Text Editor Panel\"", this);
		popup->SetOKButtonText("Ok");
		popup->SetCancelButtonVisible(false);
		popup->AddActionSignalTarget(this);
		popup->DoModal(this);

		temp->deleteThis();
		return;
	}

	//stop all soundscapes before deleting the old soundscapes
	m_kvCurrSelected = nullptr;

	if (g_IsPlayingSoundscape)
		PlaySelectedSoundscape();

	//delete and set the old keyvalues
	if (m_KeyValues)
		m_KeyValues->deleteThis();

	m_KeyValues = temp;

	//load the file
	LoadFile(m_KeyValues);
}

//-----------------------------------------------------------------------------
// Purpose: Destructor for soundscape maker panel
//-----------------------------------------------------------------------------
CSoundscapeMaker::~CSoundscapeMaker()
{
	//delete the keyvalue files if needed
	if (m_KeyValues)
		m_KeyValues->deleteThis();
}

//static panel instance
static CSoundscapeMaker* g_SSMakerPanel = nullptr;

//interface class
class CSoundscapeMakerInterface : public ISoundscapeMaker
{
public:
	void Create(vgui::VPANEL parent)
	{
		g_SSMakerPanel = new CSoundscapeMaker(parent);
		g_SoundPanel = new CSoundListPanel(parent, "SoundscapeSoundListPanel");
		g_SettingsPanel = new CSoundscapeSettingsPanel(parent, "SoundscapeSettingsPanel");
		g_SoundscapeTextPanel = new CSoundscapeTextPanel(parent, "SoundscapeTextPanel");
		g_SoundscapeDebugPanel = new CSoundscapeDebugPanel(parent, "SoundscapeDebugPanel");
	}

	void SetVisible(bool bVisible)
	{
		if (g_SSMakerPanel)
			g_SSMakerPanel->SetVisible(bVisible);
	}

	void Destroy()
	{
		if (g_SSMakerPanel)
			g_SSMakerPanel->DeletePanel();

		if (g_SoundPanel)
			g_SoundPanel->DeletePanel();

		if (g_SettingsPanel)
			g_SettingsPanel->DeletePanel();

		if (g_SoundscapeTextPanel)
			g_SoundscapeTextPanel->DeletePanel();

		if (g_SoundscapeDebugPanel)
			g_SoundscapeDebugPanel->DeletePanel();

		if (g_SoundscapeClipboard)
			g_SoundscapeClipboard->DeletePanel();

		g_SSMakerPanel = nullptr;
		g_SoundPanel = nullptr;
		g_SettingsPanel = nullptr;
		g_SoundscapeTextPanel = nullptr;
		g_SoundscapeDebugPanel = nullptr;
		g_SoundscapeClipboard = nullptr;
	}

	void SetSoundText(const char* text)
	{
		if (!g_SSMakerPanel)
			return;

		g_SSMakerPanel->SetSoundText(text);
		g_SSMakerPanel->RequestFocus();
		g_SSMakerPanel->MoveToFront();
	}

	void SetAllVisible(bool bVisible)
	{
		g_ShowSoundscapePanel = false;

		if (g_SSMakerPanel)
			g_SSMakerPanel->SetVisible(bVisible);

		if (g_SoundPanel)
			g_SoundPanel->SetVisible(bVisible);

		if (g_SettingsPanel)
			g_SettingsPanel->SetVisible(bVisible);

		if (g_SoundscapeTextPanel)
			g_SoundscapeTextPanel->SetVisible(bVisible);

		if (g_SoundscapeDebugPanel)
			g_SoundscapeDebugPanel->SetVisible(bVisible);
	}

	void SetBuffer(const char* text)
	{
		if (g_SSMakerPanel)
			g_SSMakerPanel->Set(text);
	}

	KeyValues* GetPanelFile()
	{
		return g_SSMakerPanel->m_KeyValues;
	}

	KeyValues* GetPanelSelected()
	{
		return g_SSMakerPanel->m_kvCurrSelected;
	}

	void PasteFromClipboard(int type)
	{
		g_SSMakerPanel->Paste((SoundscapeClipboardType)type);
	}
};

CSoundscapeMakerInterface SoundscapeMaker;
ISoundscapeMaker* g_SoundscapeMaker = &SoundscapeMaker;

//-----------------------------------------------------------------------------
// Purpose: User message hook function for setting/getting soundscape position
//-----------------------------------------------------------------------------
void _SoundscapeMaker_Recieve(bf_read& bf)
{
	//show the soundscape panel
	g_ShowSoundscapePanel = true;
	g_SSMakerPanel->SetVisible(true);

	//show settings panel
	g_SettingsPanel->SetVisible(true);

	//get the stuff
	byte index = bf.ReadByte();
	Vector pos;
	bf.ReadBitVec3Coord(pos);

	//if index == -1 (255) then that means the message was canceled
	if (index == 255)
		return;

	//clamp index and get pos
	index = Clamp<int>(index, 0, MAX_SOUNDSCAPES - 1);

	//send message to settings
	g_SettingsPanel->SetItem(index, pos);
}

//-----------------------------------------------------------------------------
// Purpose: Command to toggle the soundscape panel
//-----------------------------------------------------------------------------
CON_COMMAND(modbase_soundscape_panel, "Toggles the modebase soundscape panel")
{
	g_ShowSoundscapePanel = !g_ShowSoundscapePanel;
	SoundscapeMaker.SetVisible(g_ShowSoundscapePanel);

	//tell player to stop soundscape mode
	static ConCommand* cc = cvar->FindCommand("__ss_maker_stop");
	if (cc)
		cc->Dispatch({});
}