Programming/creating vgui soundscape maker: Difference between revisions
WadDelz0949 (talk | contribs) mNo edit summary |
WadDelz0949 (talk | contribs) (Added new tutorial for updated options panel for vgui soundscape maker) |
||
Line 61: | Line 61: | ||
<source lang=cpp> | <source lang=cpp> | ||
#include "c_soundscape.h" | #include "c_soundscape.h" | ||
extern bool g_IsPlayingSoundscape; | |||
</source> | </source> | ||
to be under | to be under | ||
Line 66: | Line 68: | ||
#include "tier0/icommandline.h" | #include "tier0/icommandline.h" | ||
</source> | </source> | ||
Then | 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: | ||
<source lang=cpp> | <source lang=cpp> | ||
if (g_IsPlayingSoundscape) | |||
return; | |||
</source> | </source> | ||
and | now find the function: '''void C_SoundscapeSystem::StartNewSoundscape( KeyValues *pSoundscape )''' and at the very top of it and before '''int i;''' add: | ||
<source lang=cpp> | <source lang=cpp> | ||
if (g_IsPlayingSoundscape) | |||
return; | |||
</source> | </source> | ||
Then in the same file move | Then in the same file move | ||
Line 306: | Line 305: | ||
bool IsBeingRestored() const | bool IsBeingRestored() const | ||
</source> | </source> | ||
in the .h file you just added the class to. and if you find it then make the '''private''' be '''public'''. | in the .h file you just added the class to. and if you find it then make the '''private''' be '''public'''.<br> | ||
Then under the class definition add: | Then under the class definition add: | ||
<source lang=cpp> | <source lang=cpp> | ||
extern C_SoundscapeSystem g_SoundscapeSystem; | extern C_SoundscapeSystem g_SoundscapeSystem; | ||
</source> | |||
<br> | |||
Now go to '''shared/hl2/hl2_usermessages.cpp''' and inside the file find: | |||
<source lang=cpp> | |||
usermessages->Register( "AchievementEvent", -1 ); | |||
usermessages->Register( "UpdateJalopyRadar", -1 ); | |||
</source> | |||
(should be around line 47) and under it add | |||
<source lang=cpp> | |||
usermessages->Register("SoundscapeMaker_Recieve", -1); | |||
</source> | |||
Now for the final part. you need to go to '''server/player.cpp''' and just above: | |||
<source lang=cpp> | |||
//----------------------------------------------------------------------------- | |||
// Purpose: | |||
//----------------------------------------------------------------------------- | |||
void CBasePlayer::PostThink() | |||
</source> | |||
(should be around line 4590) add: | |||
<source lang=cpp> | |||
bool g_bInSoundscapePositionEditor = false; | |||
int g_SoundscapePositionNumber = 2; | |||
//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) | |||
{ | |||
g_SoundscapePositions[Clamp<int>(atoi(args.Arg(1)), 0, MAX_SOUNDSCAPES - 1)] = Vector(atof(args.Arg(2)), atof(args.Arg(3)), atof(args.Arg(4))); | |||
} | |||
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; | |||
} | |||
</source> | |||
Then finally at the top of the '''CBasePlayer::PostThink''' function add: | |||
<source lang=cpp> | |||
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); | |||
} | |||
} | |||
</source> | </source> | ||
Now the panel should be all done. to open the panel in game use the '''modbase_soundscape_panel''' command and the panel should show. | Now the panel should be all done. to open the panel in game use the '''modbase_soundscape_panel''' command and the panel should show. |
Revision as of 02:05, 12 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 client/c_soundscape.cpp and copy:
#include "c_soundscape.h"
extern bool g_IsPlayingSoundscape;
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;
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 = 2;
//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)
{
g_SoundscapePositions[Clamp<int>(atoi(args.Arg(1)), 0, MAX_SOUNDSCAPES - 1)] = Vector(atof(args.Arg(2)), atof(args.Arg(3)), atof(args.Arg(4)));
}
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: