diff options
Diffstat (limited to 'game/code/sound/dialog/dialogpriorityqueue.cpp')
-rw-r--r-- | game/code/sound/dialog/dialogpriorityqueue.cpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/game/code/sound/dialog/dialogpriorityqueue.cpp b/game/code/sound/dialog/dialogpriorityqueue.cpp new file mode 100644 index 0000000..5a57510 --- /dev/null +++ b/game/code/sound/dialog/dialogpriorityqueue.cpp @@ -0,0 +1,536 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogpriorityqueue.cpp +// +// Description: Responsible for managing the outstanding dialog playback requests. +// When the DialogCoordinator needs to play dialog, it hands the +// PlayableDialog object off, and the DialogPriorityQueue determines +// if it can be played, or if it should wait until some other dialog +// completes. When a PlayableDialog is ready for playback, it gets +// handed to the SimpsonsSoundPlayer. +// +// History: 04/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radtime.hpp> +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogpriorityqueue.h> + +#include <sound/dialog/dialogqueueelement.h> +#include <sound/dialog/selectabledialog.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundmanager.h> + +#include <memory/srrmemory.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Probability of optional play success as chance in 256 +// +static const unsigned int OPL_PROB = 64; + +//#define DEBUG_QUEUE_REFCOUNT + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogPriorityQueue::DialogPriorityQueue +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogPriorityQueue::DialogPriorityQueue() : +#ifdef SOUND_DEBUG_INFO_ENABLED + m_debugPage( 3, GetSoundManager()->GetDebugDisplay() ), +#endif + m_nowPlaying( NULL ), + m_permitQueueAdvance( true ) +{ +} + +//============================================================================== +// DialogPriorityQueue::~DialogPriorityQueue +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogPriorityQueue::~DialogPriorityQueue() +{ +} + +//============================================================================= +// DialogPriorityQueue::AddDialogToQueue +//============================================================================= +// Description: Place dialog on queue and play if possible +// +// Parameters: dialog - dialog to add to queue +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::AddDialogToQueue( SelectableDialog& dialog, rmt::Vector* posn ) +{ + DialogPriority priority; + DialogQueueElement* queueElement; + unsigned int diceRoll; + + // + // Check the priority on the dialog, and use it to find the appropriate + // spot in the queue. + // + priority = DialogQueueElement::CalculateDialogPriority( dialog ); + + if( priority == OccasionalPlayLine ) + { + // + // Random play + // + diceRoll = ( rand() % 100 ); + if( diceRoll >= DialogQueueElement::CalculateDialogProbability( dialog ) ) + { + return; + } + } + + // + // Ducking for dialog + // + if( m_nowPlaying == NULL ) + { + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, false ); + GetSoundManager()->MuteNISPlayers(); + } + + // + // Don't bother playing dialog if we're already playing the same thing. + // This can happen with collision events that get triggered zillions of + // times + // + if( ( m_nowPlaying == NULL ) + || !( m_nowPlaying->DialogMatches( &dialog ) ) ) + { + queueElement = new ( GMA_TEMP ) DialogQueueElement( &dialog ); + + if( priority == MustPlayImmediately ) + { + // + // Special case. Place this guy at the head of the queue and kill + // whatever's playing right now. + // + if( m_nowPlaying ) + { + // + // If we don't halt the dialog queue, the stop callback will advance + // it on us and we get two dialog lines + // + m_permitQueueAdvance = false; + + m_nowPlaying->StopDialog(); +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "AddDialogToQueue %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + // + // One more check. The StopDialog() above may already have made m_nowPlaying + // go away + // + if( m_nowPlaying != NULL ) + { + m_nowPlaying->Release(); + } + + m_permitQueueAdvance = true; + } + + m_nowPlaying = queueElement; + playDialog( posn ); + } + else + { + // + // Stick it in the list + // + queueElement->AddToQueue( &m_dialogQueue, posn ); + } + } + +#ifndef RAD_RELEASE + // + // Mark dialog as played for coverage testing purposes + // + dialog.MarkAsPlayed(); +#endif +} + +//============================================================================= +// DialogPriorityQueue::StopCurrentDialog +//============================================================================= +// Description: If we have something playing, stop it and yank it off the +// queue +// +// Parameters: None +// +// Return: true if there was dialog to stop, false otherwise +// +//============================================================================= +bool DialogPriorityQueue::StopCurrentDialog() +{ + bool dialogStopped = false; + + if( m_nowPlaying ) + { + // + // Stop it and let the callback do the rest + // + m_nowPlaying->StopDialog(); + dialogStopped = true; + } + + return( dialogStopped ); +} + +//============================================================================= +// DialogPriorityQueue::StopAllDialog +//============================================================================= +// Description: Kill the queue. This'll most likely be done when gameplay +// ends. +// +// Parameters: None +// +// Return: true if there was dialog to stop, false otherwise +// +//============================================================================= +bool DialogPriorityQueue::StopAllDialog() +{ + DialogQueueElement* current; + bool dialogStopped = false; + + // + // Just in case we're still in a paused state. Stopping paused dialogue doesn't + // seem to give us our stop callbacks + // + UnpauseDialog(); + + m_permitQueueAdvance = false; + + while( !m_dialogQueue.empty() ) + { + current = m_dialogQueue.front(); + current->RemoveSelfFromList(); + current->Release(); + } + + if( m_nowPlaying != NULL ) + { + m_nowPlaying->StopDialog(); + + // + // Check again to make sure that the dialog didn't remove itself. + // Since the queue was emptied first, nothing else should replace + // the currently playing dialog afterward. + // + if( m_nowPlaying != NULL ) + { +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "StopAllDialog %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + m_nowPlaying->Release(); + m_nowPlaying = NULL; + + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true ); + GetSoundManager()->UnmuteNISPlayers(); + } + + dialogStopped = true; + } + + m_permitQueueAdvance = true; + + // + // Since we don't seem to get the OnPlaybackComplete callback for the queue + // element, throw a couple of shutup events in case someone was mouth flapping + // + GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP ); + GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP ); + + return( dialogStopped ); +} + +//============================================================================= +// DialogPriorityQueue::PauseDialog +//============================================================================= +// Description: Pause the dialog players +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::PauseDialog() +{ + m_player1.Pause(); + m_player2.Pause(); + m_positionalPlayer1.Pause(); + m_positionalPlayer2.Pause(); +} + +//============================================================================= +// DialogPriorityQueue::UnpauseDialog +//============================================================================= +// Description: Unpause the dialog players +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::UnpauseDialog() +{ + if( m_player1.IsPaused() ) + { + m_player1.Continue(); + } + if( m_player2.IsPaused() ) + { + m_player2.Continue(); + } + if( m_positionalPlayer1.IsPaused() ) + { + m_positionalPlayer1.Continue(); + } + if( m_positionalPlayer2.IsPaused() ) + { + m_positionalPlayer2.Continue(); + } +} + +//============================================================================= +// DialogPriorityQueue::OnDialogLineComplete +//============================================================================= +// Description: Called when a line of dialog in the currently playing +// conversation is complete +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::OnDialogLineComplete() +{ + // + // TODO: Stop the mouth flapping and eye blinking + // +} + +//============================================================================= +// DialogPriorityQueue::OnDialogComplete +//============================================================================= +// Description: Called when the currently playing SelectableDialog is +// complete +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::OnDialogComplete() +{ + // + // TODO: Stop the mouth flapping and eye blinking + // + + // + // Get rid of the currently playing element + // +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "OnDialogComplete %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + m_nowPlaying->Release(); + if( !m_dialogQueue.empty() && m_permitQueueAdvance ) + { + m_nowPlaying = m_dialogQueue.front(); + m_nowPlaying->RemoveSelfFromList(); + playDialog( m_nowPlaying->GetPosition() ); + } + else + { + m_nowPlaying = NULL; + + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true ); + GetSoundManager()->UnmuteNISPlayers(); + } +} + +//============================================================================= +// DialogPriorityQueue::Service +//============================================================================= +// Description: Do servicing stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::ServiceOncePerFrame() +{ + DialogQueueElement::Service(); + + // + // Just to be safe, don't update the positional players here. The update + // function just does stuff for moving sounds and pausing out-of-range + // stuff, none of which applies here. It's theoretically safe to do the + // right thing and service these players, but we're way too close to final + // to change the status quo. + // + //m_positionalPlayer1.ServiceOncePerFrame(); + //m_positionalPlayer2.ServiceOncePerFrame(); + + advanceQueue(); + + serviceDebugPage(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogPriorityQueue::advanceQueue +//============================================================================= +// Description: Advance the queue if non-empty and nothing's playing. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::advanceQueue() +{ + if( ( m_nowPlaying == NULL ) && ( !m_dialogQueue.empty() ) && m_permitQueueAdvance ) + { + m_nowPlaying = m_dialogQueue.front(); + m_nowPlaying->RemoveSelfFromList(); + playDialog( m_nowPlaying->GetPosition() ); + } +} + +//============================================================================= +// DialogPriorityQueue::playDialog +//============================================================================= +// Description: Start a dialog line playing with the correct players +// +// Parameters: posn - position of speaker for positional dialogue. NULL +// if non-positional +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::playDialog( rmt::Vector* posn ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + if( posn != NULL ) + { + // + // Before starting playback, set up the positional stuff + // + // + // Get the positionalSoundSettings object for the wasp sound + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( "posn_dialog_settings" ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + m_positionalPlayer1.SetParameters( parameters ); + + m_positionalPlayer1.SetPosition( posn->x, posn->y, posn->z ); + + m_nowPlaying->PlayDialog( m_positionalPlayer1, m_positionalPlayer2, this, this ); + } + else + { + rTuneAssertMsg( false, "No min/max for positional dialogue? Bad, call Esan." ); + + // + // Handle gracefully in release + // + m_nowPlaying->PlayDialog( m_player1, m_player2, this, this ); + } + } + else + { + m_nowPlaying->PlayDialog( m_player1, m_player2, this, this ); + } +} + +//============================================================================= +// DialogPriorityQueue::serviceDebugPage +//============================================================================= +// Description: Update the SoundInfo data available in Watcher +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::serviceDebugPage() +{ +#ifdef SOUND_DEBUG_INFO_ENABLED + int i; + int queueLength; + DialogQueueType::const_iterator iter; + + if( m_nowPlaying != NULL ) + { + // + // For the debug page, nowPlaying is part of the queue + // + queueLength = m_dialogQueue.size() + 1; + m_debugPage.SetQueueLength( queueLength ); + + m_nowPlaying->FillDebugInfo( m_debugPage, 0 ); + i = 1; + for( iter = m_dialogQueue.begin(); iter != m_dialogQueue.end(); ++iter ) + { + (*iter)->FillDebugInfo( m_debugPage, i++ ); + } + } + else + { + m_debugPage.SetQueueLength( 0 ); + } +#endif +} |