/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// soundman.cpp: Sound Manager
//

#include "g_local.h"
#include "debuglines.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.",
	EV_NORMAL
	);
Event EV_SoundManager_AddRandomSpeaker
	(
	"addrandomspeaker", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Add a new sound where the player is standing.",
	EV_NORMAL
	);
Event EV_SoundManager_AddMusicTrigger
	(
	"addmusictrigger", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Add a new music trigger where the player is standing.",
	EV_NORMAL
	);
Event EV_SoundManager_AddReverbTrigger
	(
	"addreverbtrigger", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Add a new reverb trigger where the player is standing.",
	EV_NORMAL
	);
Event EV_SoundManager_Replace
	( 
	"replace", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Replace the current sound position with the player's.",
	EV_NORMAL
	);
Event EV_SoundManager_Delete
	( 
	"delete", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Delete the current sound.",
	EV_NORMAL
	);
Event EV_SoundManager_MovePlayer
	( 
	"moveplayer", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Move the player to the current sound position.",
	EV_NORMAL
	);
Event EV_SoundManager_Next
	( 
	"next", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Go to the next sound.",
	EV_NORMAL
	);
Event EV_SoundManager_Previous
	( 
	"prev", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Go to the previous sound.",
	EV_NORMAL
	);

Event EV_SoundManager_Show
	( 
	"show", 
	EV_CONSOLE,
   "E",
   "path",
   "Show all the sounds.",
	EV_NORMAL
	);

Event EV_SoundManager_ShowingSounds
	( 
	"_showing_sounds", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Internal event for showing the sounds.",
	EV_NORMAL
	);
Event EV_SoundManager_Hide
	( 
	"hide", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Hides the sounds.",
	EV_NORMAL
	);

Event EV_SoundManager_Save
	( 
	"save", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Saves the sounds.",
	EV_NORMAL
	);
Event EV_SoundManager_UpdateInput
	( 
	"updateinput", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Updates the current sound with user interface values.",
	EV_NORMAL
	);

Event EV_SoundManager_Reset
	( 
	"reset", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Resets the state of all sounds and triggers.",
	EV_NORMAL
	);

Event EV_SoundManager_GlobalTranslate
	( 
	"globaltranslate", 
	EV_CONSOLE,
   "v",
   "translate_amount",
   "Translates all sounds and triggers by specified amount.",
	EV_NORMAL
	);

Event EV_SoundManager_SwitchFacet
	( 
	"switchfacet", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Switch the current facet that we are editing.",
	EV_NORMAL
	);

Event EV_SoundManager_PreviewReverb
	( 
	"previewreverb", 
	EV_CONSOLE,
   NULL,
   NULL,
   "Test out the current reverb settings.",
	EV_NORMAL
	);

Event EV_SoundManager_ResetReverb
	( 
	"resetreverb", 
	EV_CONSOLE,
   NULL,
   NULL,
   "reset the reverb settings to a normal.",
	EV_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.Printf( "No player found.\n" );
      return NULL;
      }

   return ( Player * )g_entities[ 0 ].entity;
   }

SoundManager::SoundManager()
   {
   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", Director.GetString(music->current).c_str() );
               gi.cvar_set( "snd_fallbackmood", Director.GetString(music->fallback).c_str() );
               }
            else
               {
               gi.cvar_set( "snd_currentmood", Director.GetString(music->altcurrent).c_str() );
               gi.cvar_set( "snd_fallbackmood", Director.GetString(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_Get( "snd_origin", "", 0 );
   sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
   speaker->setOrigin( tempvec );

   // get targetname
   cvar = gi.Cvar_Get( "snd_targetname", "", 0 );
   speaker->SetTargetName( cvar->string );

   // get volume
   cvar = gi.Cvar_Get( "snd_volume", "", 0 );
   if ( str( cvar->string ) == "Default" )
      {
      speaker->SetVolume( DEFAULT_VOL );
      }
   else
      {
      speaker->SetVolume( cvar->value );
      }

   // get min_dist
   cvar = gi.Cvar_Get( "snd_mindist", "", 0 );
   if ( str( cvar->string ) == "Default" )
      {
      speaker->SetMinDist( DEFAULT_MIN_DIST );
      }
   else
      {
      speaker->SetMinDist( cvar->value );
      }

   // get mindelay
   cvar = gi.Cvar_Get( "snd_mindelay", "", 0 );
   speaker->SetMinDelay( cvar->value );

   // get maxdelay
   cvar = gi.Cvar_Get( "snd_maxdelay", "", 0 );
   speaker->SetMaxDelay( cvar->value );

   // get chance
   cvar = gi.Cvar_Get( "snd_chance", "", 0 );
   speaker->SetChance( cvar->value );

   // get sound
   cvar = gi.Cvar_Get( "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_Get( "snd_origin", "", 0 );
   sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
   speaker->setOrigin( tempvec );

   // get targetname
   cvar = gi.Cvar_Get( "snd_targetname", "", 0 );
   speaker->SetTargetName( cvar->string );

   // get volume
   cvar = gi.Cvar_Get( "snd_volume", "", 0 );
   if ( str( cvar->string ) == "Default" )
      {
      speaker->SetVolume( DEFAULT_VOL );
      }
   else
      {
      speaker->SetVolume( cvar->value );
      }

   // get min_dist
   cvar = gi.Cvar_Get( "snd_mindist", "", 0 );
   if ( str( cvar->string ) == "Default" )
      {
      speaker->SetMinDist( DEFAULT_MIN_DIST );
      }
   else
      {
      speaker->SetMinDist( cvar->value );
      }

   // get sound
   cvar = gi.Cvar_Get( "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_Get( "snd_origin", "", 0 );
   sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
   music->setOrigin( tempvec );

   // get targetname
   cvar = gi.Cvar_Get( "snd_targetname", "", 0 );
   music->SetTargetName( cvar->string );

   // get width
   cvar = gi.Cvar_Get( "snd_width", "", 0 );
   music->mins[ 0 ] = -cvar->value;
   music->maxs[ 0 ] = cvar->value;

   // get length
   cvar = gi.Cvar_Get( "snd_length", "", 0 );
   music->mins[ 1 ] = -cvar->value;
   music->maxs[ 1 ] = cvar->value;

   // get height
   cvar = gi.Cvar_Get( "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_Get( "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_Get( "snd_currentmood", "", 0 );
   current = cvar->string;

   // get fallback mood
   cvar = gi.Cvar_Get( "snd_fallbackmood", "", 0 );
   fallback = cvar->string;

   if ( music->multiFaceted && currentFacet )
      {
      music->SetAltMood( current, fallback );
      }
   else
      {
      music->SetMood( current, fallback );
      }

   // get onetime
   cvar = gi.Cvar_Get( "snd_onetime", "", 0 );
   if ( cvar->integer )
      {
      music->SetOneShot( true );
      }

   // get yaw
   cvar = gi.Cvar_Get( "snd_yaw", "", 0 );
   music->angles[ YAW ] = cvar->value;
   music->setAngles();

   // get useangles
   cvar = gi.Cvar_Get( "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_Get( "snd_origin", "", 0 );
   sscanf( cvar->string, "%f %f %f", &tempvec[ 0 ], &tempvec[ 1 ], &tempvec[ 2 ] );
   reverb->setOrigin( tempvec );

   // get targetname
   cvar = gi.Cvar_Get( "snd_targetname", "", 0 );
   reverb->SetTargetName( cvar->string );

   // get width
   cvar = gi.Cvar_Get( "snd_width", "", 0 );
   reverb->mins[ 0 ] = -cvar->value;
   reverb->maxs[ 0 ] = cvar->value;

   // get length
   cvar = gi.Cvar_Get( "snd_length", "", 0 );
   reverb->mins[ 1 ] = -cvar->value;
   reverb->maxs[ 1 ] = cvar->value;

   // get height
   cvar = gi.Cvar_Get( "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_Get( "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_Get( "snd_reverbtype", "", 0 );
   reverbtype = cvar->integer;

   // get reverb level
   cvar = gi.Cvar_Get( "snd_reverblevel", "", 0 );
   reverblevel = cvar->value;

   if ( reverb->multiFaceted && currentFacet )
      {
      reverb->SetAltReverb( reverbtype, reverblevel );
      }
   else
      {
      reverb->SetReverb( reverbtype, reverblevel );
      }

   // get onetime
   cvar = gi.Cvar_Get( "snd_onetime", "", 0 );
   if ( cvar->integer )
      {
      reverb->SetOneShot( true );
      }

   // get yaw
   cvar = gi.Cvar_Get( "snd_yaw", "", 0 );
   reverb->angles[ YAW ] = cvar->value;
   reverb->setAngles();

   // get useangles
   cvar = gi.Cvar_Get( "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 );

   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_Get( "snd_reverbtype", "", 0 );
   reverbtype = cvar->integer;

   // get reverb level
   cvar = gi.Cvar_Get( "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 );
      }
   }


void SoundManager::ShowingSounds
   (
   Event *ev
   )

   {
   int i;
   Entity * ent;

   for( i = 1; i <= soundList.NumObjects(); i++ )
      {
      ent = soundList.ObjectAt( i );
      if ( ent->isSubclassOf( TriggerSpeaker ) )
         {
         TriggerSpeaker * speaker;

         speaker = ( TriggerSpeaker * )ent;
         if ( current == ent )
            {
            if ( speaker->volume != DEFAULT_VOL )
               G_DrawDebugNumber( speaker->origin + Vector( 0, 0, 10 ), speaker->volume, 0.5, 0, 1, 0, 2 );
            if ( speaker->min_dist != DEFAULT_MIN_DIST )
               G_DrawDebugNumber( speaker->origin + Vector( 0, 0, 20 ), speaker->min_dist, 0.5, 0, 0, 1, 0 );

            // falloff circles
            if ( speaker->min_dist != DEFAULT_VOL )
               {
               G_DebugCircle( speaker->origin, speaker->min_dist, 0, 1, 1, 1, true );
               G_DebugCircle( speaker->origin, 2*speaker->min_dist, 0, 0.5, 0.5, 0.5, true );
               G_DebugCircle( speaker->origin, 4*speaker->min_dist, 0, 0.25, 0.25, 0.25, true );
               }

            if ( speaker->isSubclassOf( RandomSpeaker ) )
               {
               RandomSpeaker * random;

               G_DebugPyramid( speaker->origin, 24, 1, 1, 0.5, 1 );
               random = ( RandomSpeaker * )ent;

               G_DrawDebugNumber( random->origin + Vector( 0, 0, 30 ), random->mindelay, 0.5, 0.25, 0, 1, 1 );
               G_DrawDebugNumber( random->origin + Vector( 0, 0, 38 ), random->maxdelay, 0.5, 0.25, 0, 1, 1 );
               G_DrawDebugNumber( random->origin + Vector( 0, 0, 46 ), random->chance, 0.5, 0.25, 0, 1, 2 );
               }
            else
               {
               G_DebugPyramid( speaker->origin, 24, 1, 1, 0, 1 );
               }
            }
         else
            {
            if ( speaker->isSubclassOf( RandomSpeaker ) )
               {
               G_DebugPyramid( speaker->origin, 24, 1, 0, 1, 1 );
               }
            else
               {
               G_DebugPyramid( speaker->origin, 24, 1, 0, 0, 1 );
               }
            }
         }
      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, 1, 1, 1 );
            else
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1, 1, 0, 1 );
            }
         else
            {
            if ( music->oneshot )
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0, 0.5, 0.5, 1 );
            else
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1, 0, 0, 1 );
            }

         if ( music->useTriggerDir )
            {
            Vector org;

            org = ent->origin;
            org[ 2 ] += 0.5f * ent->maxs[ 2 ];

            G_DebugArrow( org, music->GetTriggerDir(), 48, 0.5, 1, 1, 1 );
            }
         facet = music->GetMultiFaceted();
         if ( facet )
            {
            if ( current == ent )
               {
               G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + currentFacet ), 1, 1, 1, 1 );
               }
            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.5, 0.5, 1, 1 );
            else
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0, 0.5, 1, 1 );
            }
         else
            {
            if ( reverb->oneshot )
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0.5, 0, 1, 1 );
            else
               G_DebugBBox( ent->origin, ent->mins, ent->maxs, 0, 0, 1, 1 );
            }

         if ( reverb->useTriggerDir )
            {
            Vector org;

            org = ent->origin;
            org[ 2 ] += 0.5f * ent->maxs[ 2 ];

            G_DebugArrow( org, reverb->GetTriggerDir(), 48, 0.5, 1, 1, 1 );
            }
         facet = reverb->GetMultiFaceted();
         if ( facet )
            {
            if ( current == ent )
               {
               G_DebugHighlightFacet( ent->origin, ent->mins, ent->maxs, ( facet_t )( ( ( facet - 1 ) << 1 ) + currentFacet ), 1, 1, 1, 1 );
               }
            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;
   size_t         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", Director.GetString(music->current).c_str() );

      //
      // set fallback
      //
      buf += va( " fallback %s", Director.GetString(music->fallback).c_str() );

      if ( music->GetMultiFaceted() )
         {
         //
         // save out multi faceted
         //
         buf += va( " multifaceted %d", music->GetMultiFaceted() );

         //
         // set alt current
         //
         buf += va( " altcurrent %s", Director.GetString(music->altcurrent).c_str() );

         //
         // set alt fallback
         //
         buf += va( " altfallback %s", Director.GetString(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(), ( void * )buf.c_str(), buf.length() + 1 );
   gi.Printf( "done.\n" );
   }

void SoundManager::Save
   (
   Event *ev
   )

   {
   Save();
   }

void SoundManager::Load
   (
   void
   )
{
	str filename;
	intptr_t 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();

		Director.ExecuteThread( filename, "" );
	}
}

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