Programming/creating vgui soundscape maker: Difference between revisions
WadDelz0949 (talk | contribs) m (Added fix for options panel) |
WadDelz0949 (talk | contribs) m (added new features needed for vgui_soundscape_maker.cpp like ConColorMsg's and changing textentry class) |
||
Line 47: | Line 47: | ||
g_SoundscapeMaker->Destroy(); | g_SoundscapeMaker->Destroy(); | ||
</source> | </source> | ||
<br> | |||
Now go to '''public\tier1\KeyValues.h''' and find: | Now go to '''public\tier1\KeyValues.h''' and find: | ||
<source lang=cpp> | <source lang=cpp> | ||
Line 58: | Line 58: | ||
KeyValues( KeyValues& ); // prevent copy constructor being used | KeyValues( KeyValues& ); // prevent copy constructor being used | ||
</source> | </source> | ||
<br> | |||
Now go to '''public\vgui_controls\TextEntry.h''' and find: | |||
<source lang=cpp> | |||
virtual void GetTextRange( char *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination | |||
private: | |||
CUtlVector<wchar_t> m_TextStream; // the text in the text window is stored in this buffer | |||
</source> | |||
(should be around line 318) and make that be: | |||
<source lang=cpp> | |||
virtual void GetTextRange( char *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination | |||
public: | |||
CUtlVector<wchar_t> m_TextStream; // the text in the text window is stored in this buffer | |||
</source> | |||
<br> | |||
Now go to '''client/c_soundscape.cpp''' and copy: | Now go to '''client/c_soundscape.cpp''' and copy: | ||
<source lang=cpp> | <source lang=cpp> | ||
Line 63: | Line 81: | ||
extern bool g_IsPlayingSoundscape; | extern bool g_IsPlayingSoundscape; | ||
extern bool g_SSMakerShowMessages; | |||
extern bool g_bSSMHack; | |||
</source> | </source> | ||
to be under | to be under | ||
Line 78: | Line 98: | ||
return; | return; | ||
</source> | </source> | ||
Now find '''StartSubSoundscape( pSoundscapeKeys, subParams );''' inside '''void C_SoundscapeSystem::ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t ¶msIn )''' and under it add | |||
<source lang=cpp> | |||
if (g_IsPlayingSoundscape && g_SSMakerShowMessages) | |||
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing Soundscape \"%s\"\n", pSoundscapeName); | |||
</source> | |||
Now find '''if ( soundSlot < 0 )''' in the function '''int C_SoundscapeSystem::AddLoopingSound( const char *pSoundName, bool isAmbient, float volume, soundlevel_t soundlevel, int pitch, const Vector &position )''' and above it add: | |||
<source lang=cpp> | |||
if (g_IsPlayingSoundscape && g_SSMakerShowMessages) | |||
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing looping sound \"%s\", Volume = %f, Pitch = %d\n", pSoundName, volume, pitch); | |||
</source> | |||
For the final console message you need to find: '''float volume = sound.masterVolume * RandomInterval(sound.volume);''' inside '''void C_SoundscapeSystem::PlayRandomSound''' and under it add: | |||
<source lang=cpp> | |||
if (g_IsPlayingSoundscape && g_SSMakerShowMessages) | |||
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing Random sound \"%s\", Volume = %f, Pitch = %d\n", pWaveName, volume, pitch); | |||
</source> | |||
<br> | |||
Then in the same file move | Then in the same file move | ||
<source lang=cpp> | <source lang=cpp> |
Revision as of 05:30, 17 May 2025


This tutorial will show you how to make a vgui soundscape maker panel that allows you to modify, create, and play soundscapes in game.
Requirements
- A mod running on Source SDK 2013
- Knowledge of C++
The main vgui panel
Before you start the tutorial you need to copy and paste both these files into your src/game/client/ directory

Once that is done you are almost all done. The next thing you are going to want to do is go over to client/vgui_int.cpp and at the top of the file and under
#include <vgui_controls/Controls.h>
add
#include "vgui_soundscape_maker.h"
Then in the same file find:
void VGui_CreateGlobalPanels( void )
{
VPANEL gameToolParent = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS );
VPANEL toolParent = enginevgui->GetPanel( PANEL_TOOLS );
and under that add
g_SoundscapeMaker->Create(toolParent);
Now find
void VGui_Shutdown()
{
And under it add:
g_SoundscapeMaker->Destroy();
Now go to public\tier1\KeyValues.h and find:
private:
KeyValues( KeyValues& ); // prevent copy constructor being used
(should be around line 258) and make that be:
public:
KeyValues( KeyValues& ); // prevent copy constructor being used
Now go to public\vgui_controls\TextEntry.h and find:
virtual void GetTextRange( char *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination
private:
CUtlVector<wchar_t> m_TextStream; // the text in the text window is stored in this buffer
(should be around line 318) and make that be:
virtual void GetTextRange( char *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination
public:
CUtlVector<wchar_t> m_TextStream; // the text in the text window is stored in this buffer
Now go to client/c_soundscape.cpp and copy:
#include "c_soundscape.h"
extern bool g_IsPlayingSoundscape;
extern bool g_SSMakerShowMessages;
extern bool g_bSSMHack;
to be under
#include "tier0/icommandline.h"
Then. find the function: void Soundscape_Update( audioparams_t &audio ) and at the very top of the function and before g_SoundscapeSystem.UpdateAudioParams( audio ); add:
if (g_IsPlayingSoundscape)
return;
now find the function: void C_SoundscapeSystem::StartNewSoundscape( KeyValues *pSoundscape ) and at the very top of it and before int i; add:
if (g_IsPlayingSoundscape)
return;
Now find StartSubSoundscape( pSoundscapeKeys, subParams ); inside void C_SoundscapeSystem::ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t ¶msIn ) and under it add
if (g_IsPlayingSoundscape && g_SSMakerShowMessages)
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing Soundscape \"%s\"\n", pSoundscapeName);
Now find if ( soundSlot < 0 ) in the function int C_SoundscapeSystem::AddLoopingSound( const char *pSoundName, bool isAmbient, float volume, soundlevel_t soundlevel, int pitch, const Vector &position ) and above it add:
if (g_IsPlayingSoundscape && g_SSMakerShowMessages)
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing looping sound \"%s\", Volume = %f, Pitch = %d\n", pSoundName, volume, pitch);
For the final console message you need to find: float volume = sound.masterVolume * RandomInterval(sound.volume); inside void C_SoundscapeSystem::PlayRandomSound and under it add:
if (g_IsPlayingSoundscape && g_SSMakerShowMessages)
ConColorMsg(Color(100, 255, 255, 255), "<Soundscape Maker> : Playing Random sound \"%s\", Volume = %f, Pitch = %d\n", pWaveName, volume, pitch);
Then in the same file move
struct loopingsound_t
{
Vector position; // position (if !isAmbient)
const char *pWaveName; // name of the wave file
float volumeTarget; // target volume level (fading towards this)
float volumeCurrent; // current volume level
soundlevel_t soundlevel; // sound level (if !isAmbient)
int pitch; // pitch shift
int id; // Used to fade out sounds that don't belong to the most current setting
bool isAmbient; // Ambient sounds have no spatialization - they play from everywhere
};
to be under
#ifdef _WIN32
#pragma once
from c_soundscapes.cpp into the file client/c_soundscapes.h
and finally move:
struct randomsound_t
{
Vector position;
float nextPlayTime; // time to play a sound from the set
interval_t time;
interval_t volume;
interval_t pitch;
interval_t soundlevel;
float masterVolume;
int waveCount;
bool isAmbient;
bool isRandom;
KeyValues *pWaves;
void Init()
{
memset( this, 0, sizeof(*this) );
}
};
struct subsoundscapeparams_t
{
int recurseLevel; // test for infinite loops in the script / circular refs
float masterVolume;
int startingPosition;
int positionOverride; // forces all sounds to this position
int ambientPositionOverride; // forces all ambient sounds to this position
bool allowDSP;
bool wroteSoundMixer;
bool wroteDSPVolume;
};
class C_SoundscapeSystem : public CBaseGameSystemPerFrame
{
public:
virtual char const *Name() { return "C_SoundScapeSystem"; }
C_SoundscapeSystem()
{
m_nRestoreFrame = -1;
}
~C_SoundscapeSystem() {}
void OnStopAllSounds()
{
m_params.ent.Set( NULL );
m_params.soundscapeIndex = -1;
m_loopingSounds.Purge();
m_randomSounds.Purge();
}
// IClientSystem hooks, not needed
virtual void LevelInitPreEntity()
{
Shutdown();
Init();
TouchSoundFiles();
}
virtual void LevelInitPostEntity()
{
if ( !m_pSoundMixerVar )
{
m_pSoundMixerVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
}
if ( !m_pDSPVolumeVar )
{
m_pDSPVolumeVar = (ConVar *)cvar->FindVar( "dsp_volume" );
}
}
// The level is shutdown in two parts
virtual void LevelShutdownPreEntity() {}
// Entities are deleted / released here...
virtual void LevelShutdownPostEntity()
{
OnStopAllSounds();
}
virtual void OnSave() {}
virtual void OnRestore()
{
m_nRestoreFrame = gpGlobals->framecount;
}
virtual void SafeRemoveIfDesired() {}
// Called before rendering
virtual void PreRender() { }
// Called after rendering
virtual void PostRender() { }
// IClientSystem hooks used
virtual bool Init();
virtual void Shutdown();
// Gets called each frame
virtual void Update( float frametime );
void PrintDebugInfo()
{
Msg( "\n------- CLIENT SOUNDSCAPES -------\n" );
for ( int i=0; i < m_soundscapes.Count(); i++ )
{
Msg( "- %d: %s\n", i, m_soundscapes[i]->GetName() );
}
if ( m_forcedSoundscapeIndex )
{
Msg( "- PLAYING DEBUG SOUNDSCAPE: %d [%s]\n", m_forcedSoundscapeIndex, SoundscapeNameByIndex(m_forcedSoundscapeIndex) );
}
Msg( "- CURRENT SOUNDSCAPE: %d [%s]\n", m_params.soundscapeIndex.Get(), SoundscapeNameByIndex(m_params.soundscapeIndex) );
Msg( "----------------------------------\n\n" );
}
// local functions
void UpdateAudioParams( audioparams_t &audio );
void GetAudioParams( audioparams_t &out ) const { out = m_params; }
int GetCurrentSoundscape()
{
if ( m_forcedSoundscapeIndex >= 0 )
return m_forcedSoundscapeIndex;
return m_params.soundscapeIndex;
}
void DevReportSoundscapeName( int index );
void UpdateLoopingSounds( float frametime );
int AddLoopingAmbient( const char *pSoundName, float volume, int pitch );
void UpdateLoopingSound( loopingsound_t &loopSound );
void StopLoopingSound( loopingsound_t &loopSound );
int AddLoopingSound( const char *pSoundName, bool isAmbient, float volume,
soundlevel_t soundLevel, int pitch, const Vector &position );
int AddRandomSound( const randomsound_t &sound );
void PlayRandomSound( randomsound_t &sound );
void UpdateRandomSounds( float gameClock );
Vector GenerateRandomSoundPosition();
void ForceSoundscape( const char *pSoundscapeName, float radius );
int FindSoundscapeByName( const char *pSoundscapeName );
const char *SoundscapeNameByIndex( int index );
KeyValues *SoundscapeByIndex( int index );
// main-level soundscape processing, called on new soundscape
void StartNewSoundscape( KeyValues *pSoundscape );
void StartSubSoundscape( KeyValues *pSoundscape, subsoundscapeparams_t ¶ms );
// root level soundscape keys
// add a process for each new command here
// "dsp"
void ProcessDSP( KeyValues *pDSP );
// "dsp_player"
void ProcessDSPPlayer( KeyValues *pDSPPlayer );
// "playlooping"
void ProcessPlayLooping( KeyValues *pPlayLooping, const subsoundscapeparams_t ¶ms );
// "playrandom"
void ProcessPlayRandom( KeyValues *pPlayRandom, const subsoundscapeparams_t ¶ms );
// "playsoundscape"
void ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t ¶ms );
// "soundmixer"
void ProcessSoundMixer( KeyValues *pSoundMixer, subsoundscapeparams_t ¶ms );
// "dsp_volume"
void ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t ¶ms );
bool IsBeingRestored() const
{
return gpGlobals->framecount == m_nRestoreFrame ? true : false;
}
void AddSoundScapeFile( const char *filename );
void TouchPlayLooping( KeyValues *pAmbient );
void TouchPlayRandom( KeyValues *pPlayRandom );
void TouchWaveFiles( KeyValues *pSoundScape );
void TouchSoundFile( char const *wavefile );
void TouchSoundFiles();
int m_nRestoreFrame;
CUtlVector< KeyValues * > m_SoundscapeScripts; // The whole script file in memory
CUtlVector<KeyValues *> m_soundscapes; // Lookup by index of each root section
audioparams_t m_params; // current player audio params
CUtlVector<loopingsound_t> m_loopingSounds; // list of currently playing sounds
CUtlVector<randomsound_t> m_randomSounds; // list of random sound commands
float m_nextRandomTime; // next time to play a random sound
int m_loopingSoundId; // marks when the sound was issued
int m_forcedSoundscapeIndex;// >= 0 if this a "forced" soundscape? i.e. debug mode?
float m_forcedSoundscapeRadius;// distance to spatialized sounds
static ConVar *m_pDSPVolumeVar;
static ConVar *m_pSoundMixerVar;
};
from c_soundscape.cpp to under the code you added in the c_soundscape.h file. before anything else you need to look for
void ProcessDSPVolume(KeyValues* pKey, subsoundscapeparams_t& params);
private:
bool IsBeingRestored() const
in the .h file you just added the class to. and if you find it then make the private be public.
Then under the class definition add:
extern C_SoundscapeSystem g_SoundscapeSystem;
Now go to shared/hl2/hl2_usermessages.cpp and inside the file find:
usermessages->Register( "AchievementEvent", -1 );
usermessages->Register( "UpdateJalopyRadar", -1 );
(should be around line 47) and under it add
usermessages->Register("SoundscapeMaker_Recieve", -1);
Now for the final part. you need to go to server/player.cpp and just above:
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer::PostThink()
(should be around line 4590) add:
bool g_bInSoundscapePositionEditor = false;
int g_SoundscapePositionNumber = 0;
//color based on g_SoundscapePositionNumber
Color g_SoundscapeColors[] = {
Color(255, 0, 0, 50),
Color(200, 200, 0, 50),
Color(200, 0, 255, 50),
Color(0, 0, 255, 50),
Color(0, 255, 0, 50),
Color(200, 100, 0, 50),
Color(0, 0, 0, 50),
Color(0, 100, 250, 50),
};
//vector positions
Vector g_SoundscapePositions[] = {
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
vec3_origin,
};
bool g_bUsingVector[] = {
false,
false,
false,
false,
false,
false,
false,
false,
};
#define MAX_SOUNDSCAPES 8
//hidden commands
CON_COMMAND_F(__ss_maker_start, "", FCVAR_HIDDEN)
{
g_bInSoundscapePositionEditor = true;
g_SoundscapePositionNumber = atoi(args.Arg(1));
}
CON_COMMAND_F(__ss_maker_set, "", FCVAR_HIDDEN)
{
int index = Clamp<int>(atoi(args.Arg(1)), 0, MAX_SOUNDSCAPES - 1);
g_SoundscapePositions[index] = Vector(atof(args.Arg(2)), atof(args.Arg(3)), atof(args.Arg(4)));
g_bUsingVector[index] = atoi(args.Arg(5)) == 0 ? false : true;
}
ConVar __ss_draw("__ss_draw", "0", FCVAR_HIDDEN);
CON_COMMAND_F(__ss_maker_stop, "", FCVAR_HIDDEN)
{
g_bInSoundscapePositionEditor = false;
if (!__ss_draw.GetBool())
g_bUsingVector[g_SoundscapePositionNumber] = false;
}
Then finally at the top of the CBasePlayer::PostThink function add:
if (g_bInSoundscapePositionEditor)
{
do {
//get endpos for player's eye position + forward 150
//eye position
Vector EyePos = EyePosition();
EyePos.z = EyePos.z - 10;
//forward direction
Vector ForwardDir;
AngleVectors(EyeAngles(), &ForwardDir);
//get endpos
Vector EndPos = EyePos + (ForwardDir * 75);
int index = Clamp<int>(g_SoundscapePositionNumber, 0, MAX_SOUNDSCAPES - 1);
//draw overlay line
Color& c = g_SoundscapeColors[index];
NDebugOverlay::Line(EyePos, EndPos, c.r(), c.b(), c.g(), false, NDEBUG_PERSIST_TILL_NEXT_SERVER);
//set vector at position
g_SoundscapePositions[index] = EndPos;
g_bUsingVector[index] = true;
//check for primary attack
if (m_afButtonPressed & IN_USE)
{
g_bInSoundscapePositionEditor = false;
//write message for client
CReliableBroadcastRecipientFilter filter;
UserMessageBegin(filter, "SoundscapeMaker_Recieve");
WRITE_BYTE(g_SoundscapePositionNumber);
WRITE_VEC3COORD(g_SoundscapePositions[g_SoundscapePositionNumber]);
MessageEnd();
break;
}
if (m_afButtonPressed & IN_RELOAD)
{
g_bInSoundscapePositionEditor = false;
g_bUsingVector[index] = false;
//write that we canceled the thing
CReliableBroadcastRecipientFilter filter;
UserMessageBegin(filter, "SoundscapeMaker_Recieve");
WRITE_BYTE(255);
WRITE_VEC3COORD(vec3_invalid);
MessageEnd();
break;
}
} while (false);
}
//draw all the positions
if (g_bInSoundscapePositionEditor || __ss_draw.GetBool())
{
//now draw all the positions
for (int i = 0; i < MAX_SOUNDSCAPES; i++)
{
//check for invalid vector
if (g_bUsingVector[i] == false)
continue;
//get box stuff
Vector vec = g_SoundscapePositions[i];
Color& c = g_SoundscapeColors[i];
//draw all the items
NDebugOverlay::Box(vec, Vector(-10, -10, -10), Vector(10, 10, 10), c.r(), c.b(), c.g(), c.a(), NDEBUG_PERSIST_TILL_NEXT_SERVER);
//move vec up 3
vec.z = vec.z + 3;
NDebugOverlay::Text(vec, CFmtStr("Origin: %f %f %f", vec.x, vec.y, vec.z), false, NDEBUG_PERSIST_TILL_NEXT_SERVER);
//move vec down 6
vec.z = vec.z - 6;
NDebugOverlay::Text(vec, CFmtStr("Position: %d", i), false, NDEBUG_PERSIST_TILL_NEXT_SERVER);
}
}
Now the panel should be all done. to open the panel in game use the modbase_soundscape_panel command and the panel should show.
For information on how to use the vgui panel head over to: