The-Simpsons-Hit-and-Run/game/code/sound/dialog/dialogpriorityqueue.cpp

537 lines
16 KiB
C++

//=============================================================================
// 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
}