//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/soundman.cpp                              $
// $Revision:: 10                                                             $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:36p                                                  $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
// DESCRIPTION:
// Sound Manager
//

#include "_pch_cpp.h"
//#include "g_local.h"
#include "entity.h"
#include "trigger.h"
#include "player.h"
#include "soundman.h"

SoundManager SoundMan;

/******************************************************************************

  Sound Manager

******************************************************************************/

Event EV_SoundManager_AddSpeaker
(
	"addspeaker",
	EV_CONSOLE,
	NULL,
	NULL,
	"Add a new sound where the player is standing."
);
Event EV_SoundManager_AddRandomSpeaker
(
	"addrandomspeaker",
	EV_CONSOLE,
	NULL,
	NULL,
	"Add a new sound where the player is standing."
);
Event EV_SoundManager_AddMusicTrigger
(
	"addmusictrigger",
	EV_CONSOLE,
	NULL,
	NULL,
	"Add a new music trigger where the player is standing."
);
Event EV_SoundManager_AddReverbTrigger
(
	"addreverbtrigger",
	EV_CONSOLE,
	NULL,
	NULL,
	"Add a new reverb trigger where the player is standing."
);
Event EV_SoundManager_Replace
(
	"replace",
	EV_CONSOLE,
	NULL,
	NULL,
	"Replace the current sound position with the player's."
);
Event EV_SoundManager_Delete
(
	"delete",
	EV_CONSOLE,
	NULL,
	NULL,
	"Delete the current sound."
);
Event EV_SoundManager_MovePlayer
(
	"moveplayer",
	EV_CONSOLE,
	NULL,
	NULL,
	"Move the player to the current sound position."
);
Event EV_SoundManager_Next
(
	"next",
	EV_CONSOLE,
	NULL,
	NULL,
	"Go to the next sound."
);
Event EV_SoundManager_Previous
(
	"prev",
	EV_CONSOLE,
	NULL,
	NULL,
	"Go to the previous sound."
);
Event EV_SoundManager_Show
(
	"show",
	EV_DEFAULT,
	"E",
	"path",
	"Show all the sounds."
);
Event EV_SoundManager_ShowingSounds
(
	"_showing_sounds",
	EV_CONSOLE,
	NULL,
	NULL,
	"Internal event for showing the sounds."
);
Event EV_SoundManager_Hide
(
	"hide",
	EV_DEFAULT,
	NULL,
	NULL,
	"Hides the sounds."
);
Event EV_SoundManager_Save
(
	"save",
	EV_CONSOLE,
	NULL,
	NULL,
	"Saves the sounds."
);
Event EV_SoundManager_UpdateInput
(
	"updateinput",
	EV_CONSOLE,
	NULL,
	NULL,
	"Updates the current sound with user interface values."
);
Event EV_SoundManager_Reset
(
	"reset",
	EV_CONSOLE,
	NULL,
	NULL,
	"Resets the state of all sounds and triggers."
);
Event EV_SoundManager_GlobalTranslate
(
	"globaltranslate",
	EV_CONSOLE,
	"v",
	"translate_amount",
	"Translates all sounds and triggers by specified amount."
);
Event EV_SoundManager_SwitchFacet
(
	"switchfacet",
	EV_CONSOLE,
	NULL,
	NULL,
	"Switch the current facet that we are editing."
);
Event EV_SoundManager_PreviewReverb
(
	"previewreverb",
	EV_CONSOLE,
	NULL,
	NULL,
	"Test out the current reverb settings."
);
Event EV_SoundManager_ResetReverb
(
	"resetreverb",
	EV_CONSOLE,
	NULL,
	NULL,
	"reset the reverb settings to a normal."
);

CLASS_DECLARATION( Listener, SoundManager, NULL )
{
	{ &EV_SoundManager_AddSpeaker,					&SoundManager::AddSpeaker },
	{ &EV_SoundManager_AddRandomSpeaker,			&SoundManager::AddRandomSpeaker },
	{ &EV_SoundManager_AddMusicTrigger,				&SoundManager::AddMusicTrigger },
	{ &EV_SoundManager_AddReverbTrigger,			&SoundManager::AddReverbTrigger },
	{ &EV_SoundManager_Replace,						&SoundManager::Replace },
	{ &EV_SoundManager_Delete,						&SoundManager::Delete },
	{ &EV_SoundManager_MovePlayer,					&SoundManager::MovePlayer },
	{ &EV_SoundManager_Next,						&SoundManager::Next },
	{ &EV_SoundManager_Previous,					&SoundManager::Previous },
	{ &EV_SoundManager_Show,						&SoundManager::Show },
	{ &EV_SoundManager_ShowingSounds,				&SoundManager::ShowingSounds },
	{ &EV_SoundManager_Hide,						&SoundManager::Hide },
	{ &EV_SoundManager_Save,						&SoundManager::Save },
	{ &EV_SoundManager_UpdateInput,					&SoundManager::UpdateEvent },
	{ &EV_SoundManager_Reset,						&SoundManager::ResetEvent },
	{ &EV_SoundManager_GlobalTranslate,				&SoundManager::GlobalTranslateEvent },
	{ &EV_SoundManager_SwitchFacet,					&SoundManager::SwitchFacetEvent },
	{ &EV_SoundManager_PreviewReverb,				&SoundManager::PreviewReverbEvent },
	{ &EV_SoundManager_ResetReverb,					&SoundManager::ResetReverbEvent },

	{ NULL, NULL }
};

Player *SoundManager_GetPlayer( void )
{
	assert( g_entities[ 0 ].entity && g_entities[ 0 ].entity->isSubclassOf( Player ) );
	if ( !g_entities[ 0 ].entity || !( g_entities[ 0 ].entity->isSubclassOf( Player ) ) )
	{
		gi.WPrintf( "No player found.\n" );
		return NULL;
	}
	
	return ( Player * )g_entities[ 0 ].entity;
}

SoundManager::SoundManager()
{
	Init();
}

void SoundManager::Init( void )
{
   currentFacet = 0;
   Reset();
}

void SoundManager::Reset( void )
{
	current = NULL;
	soundList.ClearObjectList();
}

void SoundManager::UpdateUI( void )
{
	if ( current )
	{
		gi.cvar_set( "snd_multifaceted", "" );
		gi.cvar_set( "snd_currentfacet", "" );
		gi.cvar_set( "snd_onetime", "0" );
		gi.cvar_set( "snd_useangles", "0" );
		gi.cvar_set( "snd_yaw", "0" );
		
		gi.cvar_set( "snd_origin", va( "%.2f %.2f %.2f", current->origin[ 0 ], current->origin[ 1 ], current->origin[ 2 ] ) );
		gi.cvar_set( "snd_targetname", current->targetname.c_str() );
		if ( current->isSubclassOf( TriggerSpeaker ) )
		{
			TriggerSpeaker * speaker;
			
			speaker = ( TriggerSpeaker * )current;
			if ( speaker->volume != DEFAULT_VOL )
            {
				gi.cvar_set( "snd_volume", va( "%.1f", speaker->volume ) );
            }
			else
            {
				gi.cvar_set( "snd_volume", "Default" );
            }
			if ( speaker->min_dist != DEFAULT_MIN_DIST )
            {
				gi.cvar_set( "snd_mindist", va( "%.1f", speaker->min_dist ) );
            }
			else
            {
				gi.cvar_set( "snd_mindist", "Default" );
            }
			
			// setup the sound
			gi.cvar_set( "ui_pickedsound", speaker->Noise().c_str() );
			
			if ( current->isSubclassOf( RandomSpeaker ) )
            {
				RandomSpeaker * random;
				
				random = ( RandomSpeaker * )current;
				gi.cvar_set( "snd_mindelay", va( "%.1f", random->mindelay ) );
				gi.cvar_set( "snd_maxdelay", va( "%.1f", random->maxdelay ) );
				gi.cvar_set( "snd_channel", va( "%d", random->channel ) );
				gi.cvar_set( "snd_chance", va( "%.1f", random->chance ) );
				gi.cvar_set( "snd_type", "RandomSpeaker" );
            }
			else
            {
				gi.cvar_set( "snd_type", "Speaker" );
            }
		}
		else if ( current->isSubclassOf( TriggerMusic )  || current->isSubclassOf( TriggerReverb ) )
		{
			int      multiFaceted;
			Trigger  *trigger;
			
			trigger = ( Trigger * )current;
			gi.cvar_set( "snd_width", va( "%.0f", trigger->maxs[ 0 ] ) );
			gi.cvar_set( "snd_length", va( "%.0f", trigger->maxs[ 1 ] ) );
			gi.cvar_set( "snd_height", va( "%.0f", trigger->maxs[ 2 ] ) );
			if ( trigger->UsingTriggerDir() )
            {
				gi.cvar_set( "snd_useangles", "1" );
            }
			else
            {
				gi.cvar_set( "snd_useangles", "0" );
            }
			gi.cvar_set( "snd_yaw", va( "%.0f", trigger->angles[ 1 ] ) );
			multiFaceted = trigger->GetMultiFaceted();
			if ( multiFaceted )
            {
				if ( multiFaceted == 1 )
				{
					gi.cvar_set( "snd_multifaceted", "North/South" );
					if ( currentFacet )
					{
						gi.cvar_set( "snd_currentfacet", "South" );
					}
					else
					{
						gi.cvar_set( "snd_currentfacet", "North" );
					}
				}
				else
				{
					gi.cvar_set( "snd_multifaceted", "East/West" );
					if ( currentFacet )
					{
						gi.cvar_set( "snd_currentfacet", "West" );
					}
					else
					{
						gi.cvar_set( "snd_currentfacet", "East" );
					}
				}
            }
			else
            {
				gi.cvar_set( "snd_multifaceted", "Not" );
            }
			
			if ( current->isSubclassOf( TriggerMusic ) )
            {
				TriggerMusic * music;
				
				music = ( TriggerMusic * )trigger;
				
				gi.cvar_set( "snd_type", "MusicTrigger" );
				if ( music->oneshot )
				{
					gi.cvar_set( "snd_onetime", "1" );
				}
				else
				{
					gi.cvar_set( "snd_onetime", "0" );
				}
				
				if ( !currentFacet )
				{
					gi.cvar_set( "snd_currentmood", music->current.c_str() );
					gi.cvar_set( "snd_fallbackmood", music->fallback.c_str() );
				}
				else
				{
					gi.cvar_set( "snd_currentmood", music->altcurrent.c_str() );
					gi.cvar_set( "snd_fallbackmood", music->altfallback.c_str() );
				}
            }
			else if ( current->isSubclassOf( TriggerReverb ) )
			{
				TriggerReverb * reverb;
				
				reverb = ( TriggerReverb * )trigger;
				
				gi.cvar_set( "snd_type", "ReverbTrigger" );
				if ( reverb->oneshot )
				{
					gi.cvar_set( "snd_onetime", "1" );
				}
				else
				{
					gi.cvar_set( "snd_onetime", "0" );
				}
				
				if ( !currentFacet )
				{
					gi.cvar_set( "snd_reverbtypedisplay", EAXMode_NumToName( reverb->reverbtype ) );
					gi.cvar_set( "snd_reverbtype", va( "%d", reverb->reverbtype ) );
					gi.cvar_set( "snd_reverblevel", va( "%.2f", reverb->reverblevel ) );
				}
				else
				{
					gi.cvar_set( "snd_reverbtypedisplay", EAXMode_NumToName( reverb->altreverbtype ) );
					gi.cvar_set( "snd_reverbtype", va( "%d", reverb->altreverbtype ) );
					gi.cvar_set( "snd_reverblevel", va( "%.2f", reverb->altreverblevel ) );
				}
			}
		}
		if ( EventPending( EV_SoundManager_ShowingSounds ) )
		{
			gi.cvar_set( "snd_hiddenstate", "visible" );
		}
		else
		{
			gi.cvar_set( "snd_hiddenstate", "hidden" );
		}
		gi.cvar_set( "snd_speakernum", va( "%d", soundList.IndexOfObject( current ) - 1 ) );
	}
}

void SoundManager::UpdateRandomSpeaker( RandomSpeaker * speaker )
{
	cvar_t * cvar;
	Vector tempvec;
	
	if ( !speaker )
	{
		return;
	}
	
	// get origin
	cvar = gi.cvar( "snd_origin", "", 0 );
	sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
	speaker->setOrigin( tempvec );
	
	// get targetname
	cvar = gi.cvar( "snd_targetname", "", 0 );
	speaker->SetTargetName( cvar->string );
	
	// get volume
	cvar = gi.cvar( "snd_volume", "", 0 );
	if ( str( cvar->string ) == "Default" )
	{
		speaker->SetVolume( DEFAULT_VOL );
	}
	else
	{
		speaker->SetVolume( cvar->value );
	}
	
	// get min_dist
	cvar = gi.cvar( "snd_mindist", "", 0 );
	if ( str( cvar->string ) == "Default" )
	{
		speaker->SetMinDist( DEFAULT_MIN_DIST );
	}
	else
	{
		speaker->SetMinDist( cvar->value );
	}
	
	// get mindelay
	cvar = gi.cvar( "snd_mindelay", "", 0 );
	speaker->SetMinDelay( cvar->value );
	
	// get maxdelay
	cvar = gi.cvar( "snd_maxdelay", "", 0 );
	speaker->SetMaxDelay( cvar->value );
	
	// get chance
	cvar = gi.cvar( "snd_chance", "", 0 );
	speaker->SetChance( cvar->value );
	
	// get sound
	cvar = gi.cvar( "ui_pickedsound", "", 0 );
	if ( cvar->string[ 0 ] )
	{
		speaker->SetNoise( cvar->string );
		speaker->ScheduleSound();
	}
}


void SoundManager::UpdateSpeaker( TriggerSpeaker * speaker )
{
	cvar_t * cvar;
	Vector tempvec;
	
	if ( !speaker )
	{
		return;
	}
	
	//
	// make sure the speaker is an ambient speaker
	//
	speaker->ambient = true;
	
	// get origin
	cvar = gi.cvar( "snd_origin", "", 0 );
	sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
	speaker->setOrigin( tempvec );
	
	// get targetname
	cvar = gi.cvar( "snd_targetname", "", 0 );
	speaker->SetTargetName( cvar->string );
	
	// get volume
	cvar = gi.cvar( "snd_volume", "", 0 );
	if ( str( cvar->string ) == "Default" )
	{
		speaker->SetVolume( DEFAULT_VOL );
	}
	else
	{
		speaker->SetVolume( cvar->value );
	}
	
	// get min_dist
	cvar = gi.cvar( "snd_mindist", "", 0 );
	if ( str( cvar->string ) == "Default" )
	{
		speaker->SetMinDist( DEFAULT_MIN_DIST );
	}
	else
	{
		speaker->SetMinDist( cvar->value );
	}
	
	// get sound
	cvar = gi.cvar( "ui_pickedsound", "", 0 );
	if ( cvar->string[ 0 ] )
	{
		speaker->SetNoise( cvar->string );
		speaker->StartSound();
	}
}

void SoundManager::UpdateTriggerMusic( TriggerMusic * music )
{
	cvar_t   *cvar;
	str      current;
	str      fallback;
	str      faceted;
	Vector   tempvec;
	
	if ( !music )
	{
		return;
	}
	
	//
	// go to a known state
	//
	
	music->SetOneShot( false );
	// no angle use
	music->useTriggerDir = false;
	// make sure it doesn't trigger
	music->triggerable = false;
	
	// get origin
	cvar = gi.cvar( "snd_origin", "", 0 );
	sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
	music->setOrigin( tempvec );
	
	// get targetname
	cvar = gi.cvar( "snd_targetname", "", 0 );
	music->SetTargetName( cvar->string );
	
	// get width
	cvar = gi.cvar( "snd_width", "", 0 );
	music->mins[ 0 ] = -cvar->value;
	music->maxs[ 0 ] = cvar->value;
	
	// get length
	cvar = gi.cvar( "snd_length", "", 0 );
	music->mins[ 1 ] = -cvar->value;
	music->maxs[ 1 ] = cvar->value;
	
	// get height
	cvar = gi.cvar( "snd_height", "", 0 );
	music->mins[ 2 ] = 0;
	music->maxs[ 2 ] = cvar->value;
	
	music->setSize( music->mins, music->maxs );
	
	// get current multi faceted ness
	cvar = gi.cvar( "snd_multifaceted", "", 0 );
	faceted = cvar->string;
	
	if ( faceted == "North/South" )
	{
		music->SetMultiFaceted( 1 );
	}
	else if ( faceted == "East/West" )
	{
		music->SetMultiFaceted( 2 );
	}
	else
	{
		music->SetMultiFaceted( 0 );
	}
	
	// get current mood
	cvar = gi.cvar( "snd_currentmood", "", 0 );
	current = cvar->string;
	
	// get fallback mood
	cvar = gi.cvar( "snd_fallbackmood", "", 0 );
	fallback = cvar->string;
	
	if ( music->multiFaceted && currentFacet )
	{
		music->SetAltMood( current, fallback );
	}
	else
	{
		music->SetMood( current, fallback );
	}
	
	// get onetime
	cvar = gi.cvar( "snd_onetime", "", 0 );
	if ( cvar->integer )
	{
		music->SetOneShot( true );
	}
	
	// get yaw
	cvar = gi.cvar( "snd_yaw", "", 0 );
	music->angles[ YAW ] = cvar->value;
	music->setAngles();
	
	// get useangles
	cvar = gi.cvar( "snd_useangles", "", 0 );
	if ( cvar->integer )
	{
		music->SetTriggerDir( music->angles[ YAW ] );
	}
	UpdateUI();
}

void SoundManager::UpdateTriggerReverb( TriggerReverb * reverb )
{
	cvar_t   *cvar;
	int      reverbtype;
	float    reverblevel;
	str      faceted;
	Vector   tempvec;
	
	if ( !reverb )
	{
		return;
	}
	
	//
	// go to a known state
	//
	
	reverb->SetOneShot( false );
	// no angle use
	reverb->useTriggerDir = false;
	// make sure it doesn't trigger
	reverb->triggerable = false;
	
	// get origin
	cvar = gi.cvar( "snd_origin", "", 0 );
	sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
	reverb->setOrigin( tempvec );
	
	// get targetname
	cvar = gi.cvar( "snd_targetname", "", 0 );
	reverb->SetTargetName( cvar->string );
	
	// get width
	cvar = gi.cvar( "snd_width", "", 0 );
	reverb->mins[ 0 ] = -cvar->value;
	reverb->maxs[ 0 ] = cvar->value;
	
	// get length
	cvar = gi.cvar( "snd_length", "", 0 );
	reverb->mins[ 1 ] = -cvar->value;
	reverb->maxs[ 1 ] = cvar->value;
	
	// get height
	cvar = gi.cvar( "snd_height", "", 0 );
	reverb->mins[ 2 ] = 0;
	reverb->maxs[ 2 ] = cvar->value;
	
	reverb->setSize( reverb->mins, reverb->maxs );
	
	// get current multi faceted ness
	cvar = gi.cvar( "snd_multifaceted", "", 0 );
	faceted = cvar->string;
	
	if ( faceted == "North/South" )
	{
		reverb->SetMultiFaceted( 1 );
	}
	else if ( faceted == "East/West" )
	{
		reverb->SetMultiFaceted( 2 );
	}
	else
	{
		reverb->SetMultiFaceted( 0 );
	}
	
	// get reverb type
	cvar = gi.cvar( "snd_reverbtype", "", 0 );
	reverbtype = cvar->integer;
	
	// get reverb level
	cvar = gi.cvar( "snd_reverblevel", "", 0 );
	reverblevel = cvar->value;
	
	if ( reverb->multiFaceted && currentFacet )
	{
		reverb->SetAltReverb( reverbtype, reverblevel );
	}
	else
	{
		reverb->SetReverb( reverbtype, reverblevel );
	}
	
	// get onetime
	cvar = gi.cvar( "snd_onetime", "", 0 );
	if ( cvar->integer )
	{
		reverb->SetOneShot( true );
	}
	
	// get yaw
	cvar = gi.cvar( "snd_yaw", "", 0 );
	reverb->angles[ YAW ] = cvar->value;
	reverb->setAngles();
	
	// get useangles
	cvar = gi.cvar( "snd_useangles", "", 0 );
	if ( cvar->integer )
	{
		reverb->SetTriggerDir( reverb->angles[ YAW ] );
	}
	UpdateUI();
}

void SoundManager::UpdateEvent( Event *ev )
{
	if ( !current )
	{
		return;
	}
	if ( current->isSubclassOf( RandomSpeaker ) )
	{
		UpdateRandomSpeaker( ( RandomSpeaker * )current );
	}
	else if ( current->isSubclassOf( TriggerSpeaker ) )
	{
		UpdateSpeaker( ( TriggerSpeaker * )current );
	}
	else if ( current->isSubclassOf( TriggerMusic ) )
	{
		UpdateTriggerMusic( ( TriggerMusic * )current );
	}
	else if ( current->isSubclassOf( TriggerReverb ) )
	{
		UpdateTriggerReverb( ( TriggerReverb * )current );
	}
}

void SoundManager::AddSpeaker( Event *ev )
{
	Player *player;
	Vector ang;
	Vector pos;
	
	player = SoundManager_GetPlayer();
	if ( player )
	{
		player->GetPlayerView( &pos, &ang );
		
		current = new TriggerSpeaker;
		current->setOrigin( pos );
		current->setAngles( ang );
		
		soundList.AddUniqueObject( current );
		
		Show();
	}
	UpdateUI();
}

void SoundManager::AddRandomSpeaker( Event *ev )
{
	Player *player;
	Vector ang;
	Vector pos;
	
	player = SoundManager_GetPlayer();
	if ( player )
	{
		player->GetPlayerView( &pos, &ang );
		
		current = new RandomSpeaker;
		current->setOrigin( pos );
		current->setAngles( ang );
		
		soundList.AddUniqueObject( current );
		
		Show();
	}
	UpdateUI();
}

void SoundManager::AddMusicTrigger( Event *ev )
{
	Player *player;
	Vector ang;
	
	player = SoundManager_GetPlayer();
	if ( player )
	{
		player->GetPlayerView( NULL, &ang );
		
		current = new TriggerMusic;
		// we grab the origin from the feet of the player
		current->setOrigin( player->origin );
		current->setAngles( ang );
		current->setSize( Vector(-16, -16, 0), Vector(16, 16, 64) );
		// make sure it doesn't trigger
		( ( TriggerMusic * )current )->triggerable = false;
		
		soundList.AddUniqueObject( current );
		
		Show();
	}
	UpdateUI();
}

void SoundManager::AddReverbTrigger( Event *ev )
{
	Player *player;
	Vector ang;
	
	player = SoundManager_GetPlayer();
	if ( player )
	{
		player->GetPlayerView( NULL, &ang );
		
		current = new TriggerReverb;
		// we grab the origin from the feet of the player
		current->setOrigin( player->origin );
		current->setAngles( ang );
		current->setSize( Vector(-16, -16, 0), Vector(16, 16, 64) );
		// make sure it doesn't trigger
		( ( TriggerReverb * )current )->triggerable = false;
		
		soundList.AddUniqueObject( current );
		
		Show();
	}
	UpdateUI();
}

void SoundManager::Replace( Event *ev )
{
	Player *player;
	Vector ang;
	Vector pos;
	
	player = SoundManager_GetPlayer();
	if ( current && player )
	{
		player->GetPlayerView( &pos, &ang );
		
		if ( current->isSubclassOf( TriggerMusic ) || current->isSubclassOf( TriggerReverb )  )
		{
			current->setOrigin( player->origin );
		}
		else
		{
			current->setOrigin( pos );
		}
		current->setAngles( ang );
	}
	UpdateUI();
}

void SoundManager::Delete( Event *ev )
{
	int index = 0;
	
	if ( !current )
		return;
	
	if ( soundList.ObjectInList( current ) )
	{
		index = soundList.IndexOfObject( current );
		// remove the speaker
		soundList.RemoveObject( current );
	}
	current->PostEvent( EV_Remove, 0.0f );
	
	if ( ( index > 0 ) && ( index < soundList.NumObjects() ) )
	{
		current = soundList.ObjectAt( index );
		CurrentGainsFocus();
	}
	else
	{
		current = NULL;
	}
	
	UpdateUI();
}

void SoundManager::MovePlayer( Event *ev )
{
	Player *player;
	Vector pos;
	
	player = SoundManager_GetPlayer();
	if ( current && player )
	{
		player->GetPlayerView( &pos, NULL );
		
		if ( current->isSubclassOf( TriggerMusic ) || current->isSubclassOf( TriggerReverb ) )
		{
			player->setOrigin( current->origin );
		}
		else
		{
			player->setOrigin( current->origin - pos + player->origin );
		}
		player->SetViewAngles( current->angles );
	}
}

void SoundManager::CurrentLostFocus( void )
{
	if ( current )
	{
		if ( current->isSubclassOf( TriggerMusic ) || current->isSubclassOf( TriggerReverb ) )
		{
			current->PostEvent( EV_Trigger_SetTriggerable, 0.1f );
		}
	}
}

void SoundManager::CurrentGainsFocus( void )
{
	if ( current )
	{
		if ( current->isSubclassOf( TriggerMusic ) || current->isSubclassOf( TriggerReverb ) )
		{
			current->ProcessEvent( EV_Trigger_SetNotTriggerable );
		}
	}
}

void SoundManager::Next( Event *ev )
{
	int index;
	
	currentFacet = 0;
	
	if ( current )
	{
		//
		// find current sound in container of sounds
		//
		index = soundList.IndexOfObject( current );
		if ( index < soundList.NumObjects() )
		{
			index++;
		}
		else
		{
			index = 1;
		}
		CurrentLostFocus();
	}
	else
	{
		index = 1;
	}
	
	if ( index <= soundList.NumObjects() )
	{
		current = soundList.ObjectAt( index );
		CurrentGainsFocus();
		UpdateUI();
	}
}

void SoundManager::Previous( Event *ev )
{
	int index;
	
	currentFacet = 0;
	
	if ( current )
	{
		//
		// find current sound in container of sounds
		//
		index = soundList.IndexOfObject( current );
		if ( index > 1 )
		{
			index--;
		}
		else
		{
			index = soundList.NumObjects();
		}
		CurrentLostFocus();
	}
	else
	{
		index = 1;
	}
	
	if ( index <= soundList.NumObjects() )
	{
		current = soundList.ObjectAt( index );
		CurrentGainsFocus();
		UpdateUI();
	}
}

void SoundManager::ResetEvent( Event *ev )
{
	int i;
	Entity * ent;
	
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		ent = soundList.ObjectAt( i );
		if ( ent->isSubclassOf( TriggerSpeaker ) )
		{
			// nothing to reset
		}
		else if ( ent->isSubclassOf( TriggerMusic ) )
		{
			TriggerMusic * music;
			
			music = ( TriggerMusic * )ent;
			music->SetOneShot( music->oneshot );
		}
		else if ( ent->isSubclassOf( TriggerReverb ) )
		{
			TriggerReverb * reverb;
			
			reverb = ( TriggerReverb * )ent;
			reverb->SetOneShot( reverb->oneshot );
		}
	}
}

void SoundManager::GlobalTranslateEvent( Event *ev )
{
	int i;
	Entity * ent;
	Vector amount;
	
	amount = ev->GetVector( 1 );
	
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		ent = soundList.ObjectAt( i );
		ent->addOrigin( amount );
	}
}

void SoundManager::SwitchFacetEvent( Event *ev )
{
	if ( current && current->isSubclassOf( Trigger ) )
	{
		Trigger * trigger;
		
		trigger = ( Trigger * )current;
		if ( trigger->GetMultiFaceted() && !currentFacet )
		{
			currentFacet = 1;
		}
		else
		{
			currentFacet = 0;
		}
	}
	UpdateUI();
}

void SoundManager::PreviewReverbEvent( Event *ev )
{
	cvar_t   *cvar;
	int      reverbtype;
	float    reverblevel;
	
	// get reverb type
	cvar = gi.cvar( "snd_reverbtype", "", 0 );
	reverbtype = cvar->integer;
	
	// get reverb level
	cvar = gi.cvar( "snd_reverblevel", "", 0 );
	reverblevel = cvar->value;
	
	if ( g_entities[ 0 ].inuse && g_entities[ 0 ].client )
	{
		Player *client;
		
		client = ( Player * )g_entities[ 0 ].entity;
		client->SetReverb( reverbtype, reverblevel );
	}
}

void SoundManager::ResetReverbEvent( Event *ev )
{
	if ( g_entities[ 0 ].inuse && g_entities[ 0 ].client )
	{
		Player *client;
		
		client = ( Player * )g_entities[ 0 ].entity;
		client->SetReverb( "Generic", 0.0f );
	}
}

void SoundManager::ShowingSounds( Event *ev )
{
	int i;
	Entity * ent;
	
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		ent = soundList.ObjectAt( i );
		
		if ( !ent )
			continue;
		
		if ( ent->isSubclassOf( TriggerSpeaker ) )
		{
			TriggerSpeaker * speaker;
			
			speaker = ( TriggerSpeaker * )ent;
			if ( current == ent )
            {
				if ( speaker->volume != DEFAULT_VOL )
					G_DrawDebugNumber( speaker->origin + Vector( 0.0f, 0.0f, 10.0f ), speaker->volume, 0.5f, 0.0f, 1.0f, 0.0f, 2 );
				if ( speaker->min_dist != DEFAULT_MIN_DIST )
					G_DrawDebugNumber( speaker->origin + Vector( 0.0f, 0.0f, 20.0f ), speaker->min_dist, 0.5f, 0.0f, 0.0f, 1.0f, 0 );
				
				// falloff circles
				if ( speaker->min_dist != DEFAULT_VOL )
				{
					G_DebugCircle( speaker->origin, speaker->min_dist, 0.0f, 1.0f, 1.0f, 1.0f, true );
					G_DebugCircle( speaker->origin, 2.0f * speaker->min_dist, 0.0f, 0.5f, 0.5f, 0.5f, true );
					G_DebugCircle( speaker->origin, 4.0f * speaker->min_dist, 0.0f, 0.25f, 0.25f, 0.25f, true );
				}
				
				if ( speaker->isSubclassOf( RandomSpeaker ) )
				{
					RandomSpeaker * random;
					
					G_DebugPyramid( speaker->origin, 24.0f, 1.0f, 1.0f, 0.5f, 1.0f );
					random = ( RandomSpeaker * )ent;
					
					G_DrawDebugNumber( random->origin + Vector( 0.0f, 0.0f, 30.0f ), random->mindelay, 0.5f, 0.25f, 0.0f, 1.0f, 1 );
					G_DrawDebugNumber( random->origin + Vector( 0.0f, 0.0f, 38.0f ), random->maxdelay, 0.5f, 0.25f, 0.0f, 1.0f, 1 );
					G_DrawDebugNumber( random->origin + Vector( 0.0f, 0.0f, 46.0f ), random->chance, 0.5f, 0.25f, 0.0f, 1.0f, 2 );
				}
				else
				{
					G_DebugPyramid( speaker->origin, 24.0f, 1.0f, 1.0f, 0.0f, 1.0f );
				}
            }
			else
            {
				if ( speaker->isSubclassOf( RandomSpeaker ) )
				{
					G_DebugPyramid( speaker->origin, 24.0f, 1.0f, 0.0f, 1.0f, 1.0f );
				}
				else
				{
					G_DebugPyramid( speaker->origin, 24.0f, 1.0f, 0.0f, 0.0f, 1.0f );
				}
            }
		}
		else if ( ent->isSubclassOf( TriggerMusic ) )
		{
			int            facet;
			TriggerMusic * music;
			
			music = ( TriggerMusic * )ent;
			
			if ( current == ent )
            {
				if ( music->oneshot )
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.0f, 1.0f, 1.0f, 1.0f );
				else
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 1.0f, 0.0f, 1.0f );
            }
			else
            {
				if ( music->oneshot )
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.0f, 0.5f, 0.5f, 1.0f );
				else
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 0.0f, 0.0f, 1.0f );
            }
			
			if ( music->useTriggerDir )
            {
				Vector org;
				
				org = ent->origin;
				org[ 2 ] += 0.5f * ent->maxs[ 2 ];
				
				G_DebugArrow( org, music->GetTriggerDir(), 48.0f, 0.5f, 1.0f, 1.0f, 1.0f );
            }
			facet = music->GetMultiFaceted();
			if ( facet )
            {
				if ( current == ent )
				{
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + currentFacet ), 1.0f, 1.0f, 1.0f, 1.0f );
				}
				else
				{
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + 0 ), 0.6f, 0.3f, 0.2f, 0.1f );
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + 1 ), 0.6f, 0.3f, 0.2f, 0.1f );
				}
            }
		}
		else if ( ent->isSubclassOf( TriggerReverb ) )
		{
			int            facet;
			TriggerReverb  *reverb;
			
			reverb = ( TriggerReverb * )ent;
			
			if ( current == ent )
            {
				if ( reverb->oneshot )
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.5f, 0.5f, 1.0f, 1.0f );
				else
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.0f, 0.5f, 1.0f, 1.0f );
            }
			else
            {
				if ( reverb->oneshot )
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.5f, 0.0f, 1.0f, 1.0f );
				else
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.0f, 0.0f, 1.0f, 1.0f );
            }
			
			if ( reverb->useTriggerDir )
            {
				Vector org;
				
				org = ent->origin;
				org[ 2 ] += 0.5f * ent->maxs[ 2 ];
				
				G_DebugArrow( org, reverb->GetTriggerDir(), 48.0f, 0.5f, 1.0f, 1.0f, 1.0f );
            }
			facet = reverb->GetMultiFaceted();
			if ( facet )
            {
				if ( current == ent )
				{
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + currentFacet ), 1.0f, 1.0f, 1.0f, 1.0f );
				}
				else
				{
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + 0 ), 0.3f, 0.3f, 0.6f, 0.1f );
					G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + 1 ), 0.3f, 0.3f, 0.6f, 0.1f );
				}
            }
		}
	}
	PostEvent( EV_SoundManager_ShowingSounds, FRAMETIME );
}

void SoundManager::Show( void )
{
	CurrentGainsFocus();
	CancelEventsOfType( EV_SoundManager_ShowingSounds );
	PostEvent( EV_SoundManager_ShowingSounds, FRAMETIME );
	UpdateUI();
}

void SoundManager::Show( Event *ev )
{
	Show();
}

void SoundManager::Hide( Event *ev )
{
	CurrentLostFocus();
	CancelEventsOfType( EV_SoundManager_ShowingSounds );
	UpdateUI();
}

void SoundManager::Save( void )
{
	Entity      *ent;
	str         buf;
	str         filename;
	int         i;
	
	// get the name of the sound file from the world
	filename = "maps/";
	filename += level.mapname;
	for( i = filename.length() - 1; i >= 0; i-- )
	{
		if ( filename[ i ] == '.' )
		{
			filename[ i ] = 0;
			break;
		}
	}
	
	filename += ".snd";
	
	gi.Printf( "Saving soundmanager file to '%s'...\n", filename.c_str() );
	
	buf = "";
	buf += va( "//\n" );
	buf += va( "// Sound Manager File \"%s\", %d Sound Entities.\n", filename.c_str(), soundList.NumObjects() );
	buf += va( "//\n" );
	
	//
	// save out normal TriggerSpeakers
	//
	buf += va( "// TriggerSpeakers\n" );
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		TriggerSpeaker * speaker;
		
		ent = soundList.ObjectAt( i );
		if ( !ent->isSubclassOf( TriggerSpeaker ) || ent->isSubclassOf( RandomSpeaker ) )
			continue;
		
		speaker = ( TriggerSpeaker * )ent;
		//
		// start off the spawn command
		//
		buf += "spawn TriggerSpeaker";
		//
		// set the targetname
		//
		if ( ent->targetname != "" )
		{
			buf += va( " targetname %s", ent->targetname.c_str() );
		}
		//
		// set the origin
		//
		buf += va( " origin \"%.2f %.2f %.2f\"", ent->origin.x, ent->origin.y, ent->origin.z );
		//
		// make the speaker ambient and on
		//
		buf += " spawnflags 1";
		//
		// set the volume
		//
		if ( speaker->volume != DEFAULT_VOL )
			buf += va( " volume %.2f", speaker->volume );
		//
		// set the mindist
		//
		if ( speaker->min_dist != DEFAULT_MIN_DIST )
			buf += va( " min_dist %.1f", speaker->min_dist );
		//
		// save the sound
		//
		buf += va( " sound \"%s\"", speaker->Noise().c_str() );
		//
		// make sure it gets re-added to the sound manager
		//
		buf += " _addtosoundmanager 0";
		buf += va( "\n" );
	}
	//
	// save out RandomSpeakers
	//
	buf += va( "// RandomSpeakers\n" );
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		RandomSpeaker * speaker;
		
		ent = soundList.ObjectAt( i );
		if ( !ent->isSubclassOf( TriggerSpeaker ) || !ent->isSubclassOf( RandomSpeaker ) )
			continue;
		
		speaker = ( RandomSpeaker * )ent;
		//
		// start off the spawn command
		//
		buf += "spawn RandomSpeaker";
		//
		// set the targetname
		//
		if ( ent->targetname != "" )
		{
			buf += va( " targetname %s", ent->targetname.c_str() );
		}
		//
		// set the origin
		//
		buf += va( " origin \"%.2f %.2f %.2f\"", ent->origin.x, ent->origin.y, ent->origin.z );
		//
		// set the volume
		//
		if ( speaker->volume != DEFAULT_VOL )
			buf += va( " volume %.2f", speaker->volume );
		//
		// set the mindist
		//
		if ( speaker->min_dist != DEFAULT_MIN_DIST )
			buf += va( " min_dist %.1f", speaker->min_dist );
		//
		// set the channel
		//
		buf += va( " channel %d", speaker->channel );
		//
		// set the mindelay
		//
		buf += va( " mindelay %.2f", speaker->mindelay );
		//
		// set the maxdelay
		//
		buf += va( " maxdelay %.2f", speaker->maxdelay );
		//
		// set the chance
		//
		buf += va( " chance %.2f", speaker->chance );
		//
		// save the sound
		//
		buf += va( " sound \"%s\"", speaker->Noise().c_str() );
		//
		// make sure it gets re-added to the sound manager
		//
		buf += " _addtosoundmanager 0";
		buf += va( "\n" );
	}
	//
	// save out TriggerMusic
	//
	buf += va( "// TriggerMusics\n" );
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		TriggerMusic * music;
		
		ent = soundList.ObjectAt( i );
		if ( !ent->isSubclassOf( TriggerMusic ) )
			continue;
		
		music = ( TriggerMusic * )ent;
		//
		// start off the spawn command
		//
		buf += "spawn TriggerMusic";
		//
		// set the targetname
		//
		if ( ent->targetname != "" )
		{
			buf += va( " targetname %s", ent->targetname.c_str() );
		}
		//
		// set the origin
		//
		buf += va( " origin \"%.2f %.2f %.2f\"", ent->origin.x, ent->origin.y, ent->origin.z );
		//
		// set the angle
		//
		if ( music->useTriggerDir )
		{
			buf += va( " angle %.1f", AngleMod( ent->angles.y ) );
		}
		//
		// set the oneshot
		//
		if ( music->oneshot )
		{
			buf += " oneshot 0";
		}
		//
		// set current
		//
		buf += va( " current %s", music->current.c_str() );
		
		//
		// set fallback
		//
		buf += va( " fallback %s", music->fallback.c_str() );
		
		if ( music->GetMultiFaceted() )
		{
			//
			// save out multi faceted
			//
			buf += va( " multifaceted %d", music->GetMultiFaceted() );
			
			//
			// set alt current
			//
			buf += va( " altcurrent %s", music->altcurrent.c_str() );
			
			//
			// set alt fallback
			//
			buf += va( " altfallback %s", music->altfallback.c_str() );
		}
		
		//
		// set mins
		//
		buf += va( " _setmins \"%.2f %.2f %.2f\"", ent->mins.x, ent->mins.y, ent->mins.z );
		
		//
		// set maxs
		//
		buf += va( " _setmaxs \"%.2f %.2f %.2f\"", ent->maxs.x, ent->maxs.y, ent->maxs.z );
		//
		// make sure it gets re-added to the sound manager
		//
		buf += " _addtosoundmanager 0";
		buf += va( "\n" );
	}
	
	//
	// save out TriggerReverb
	//
	buf += va( "// TriggerReverbs\n" );
	for( i = 1; i <= soundList.NumObjects(); i++ )
	{
		TriggerReverb * reverb;
		
		ent = soundList.ObjectAt( i );
		if ( !ent->isSubclassOf( TriggerReverb ) )
			continue;
		
		reverb = ( TriggerReverb * )ent;
		//
		// start off the spawn command
		//
		buf += "spawn TriggerReverb";
		//
		// set the targetname
		//
		if ( ent->targetname != "" )
		{
			buf += va( " targetname %s", ent->targetname.c_str() );
		}
		//
		// set the origin
		//
		buf += va( " origin \"%.2f %.2f %.2f\"", ent->origin.x, ent->origin.y, ent->origin.z );
		//
		// set the angle
		//
		if ( reverb->useTriggerDir )
		{
			buf += va( " angle %.1f", AngleMod( ent->angles.y ) );
		}
		//
		// set the oneshot
		//
		if ( reverb->oneshot )
		{
			buf += " oneshot 0";
		}
		//
		// set reverb type
		//
		buf += va( " reverbtype %d", reverb->reverbtype );
		
		//
		// set reverb level
		//
		buf += va( " reverblevel %.2f", reverb->reverblevel );
		
		if ( reverb->GetMultiFaceted() )
		{
			//
			// save out multi faceted
			//
			buf += va( " multifaceted %d", reverb->GetMultiFaceted() );
			
			//
			// set alt reverb type
			//
			buf += va( " altreverbtype %d", reverb->altreverbtype );
			
			//
			// set alt reverb level
			//
			buf += va( " altreverblevel %.2f", reverb->altreverblevel );
		}
		
		//
		// set mins
		//
		buf += va( " _setmins \"%.2f %.2f %.2f\"", ent->mins.x, ent->mins.y, ent->mins.z );
		
		//
		// set maxs
		//
		buf += va( " _setmaxs \"%.2f %.2f %.2f\"", ent->maxs.x, ent->maxs.y, ent->maxs.z );
		//
		// make sure it gets re-added to the sound manager
		//
		buf += " _addtosoundmanager 0";
		buf += va( "\n" );
	}
	
	buf += "end\n";
	
	gi.FS_WriteFile( filename.c_str(), buf.c_str(), buf.length() + 1 );
	gi.Printf( "done.\n" );
}

void SoundManager::Save( Event *ev )
{
	Save();
}

void SoundManager::Load( void )
{
	str filename;
	int i;
	
	// get the name of the sound file from the world
	filename = "maps/";
	filename += level.mapname;
	for( i = filename.length() - 1; i >= 0; i-- )
	{
		if ( filename[ i ] == '.' )
		{
			filename[ i ] = 0;
			break;
		}
	}
	
	filename += ".snd";
	
	// If there isn't a script with the same name as the map, then don't try to load script
	if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 )
	{
		Reset();
		level.consoleThread->Parse( filename.c_str() );
	}
}

void SoundManager::AddEntity( Entity * ent )
{
	if ( ent )
	{
		soundList.AddUniqueObject( ent );
	}
}
