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

1259 lines
40 KiB
C++

//=============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// File: dialoglist.cpp
//
// Description: Loads and maintains the list of dialog lines and conversations
// (which group multiple dialog lines, and potentially link
// conversations to other conversations that occur later).
//
// History: 01/09/2002 + Created -- Darren
//
//=============================================================================
//========================================
// System Includes
//========================================
#include <radnamespace.hpp>
#include <raddebugwatch.hpp>
#include <p3d/anim/skeleton.hpp>
//========================================
// Project Includes
//========================================
#include <sound/dialog/dialoglist.h>
#include <sound/dialog/dialogline.h>
#include <sound/dialog/conversationmatcher.h>
#include <sound/soundrenderer/idasoundresource.h>
#include <memory/srrmemory.h>
#include <mission/gameplaymanager.h>
#include <render/Enums/RenderEnums.h>
#include <gameflow/gameflow.h>
#include <worldsim/character/charactermanager.h>
//******************************************************************************
//
// Global Data, Local Data, Local Classes
//
//******************************************************************************
//
// Arbitrary buffer length for filenames
//
static const int FILENAME_BUFFER_LEN = 100;
//
// Stinky hack support
//
radKey32 DialogList::s_introKey = 0;
radKey32 DialogList::s_aztecKey = 0;
tUID DialogList::s_milhouseKey = 0;
tUID DialogList::s_nelsonKey = 0;
tUID DialogList::s_raceZombie1 = 0;
tUID DialogList::s_raceZombie2 = 0;
enum pedDialogType
{
PED_MALE1,
PED_MALE2,
PED_FEMALE1,
PED_FEMALE2,
PED_BOY1,
PED_BOY2,
PED_GIRL1,
PED_GIRL2,
PED_ZOMBIE1,
PED_ZOMBIE2,
PED_ZOMBIE3,
PED_ZOMBIE4,
PED_NUM_TYPES
};
enum skinDialogType
{
SKIN_APU,
SKIN_BART,
SKIN_HOMER,
SKIN_LISA,
SKIN_MARGE,
SKIN_BARNEY,
SKIN_MILHOUSE,
SKIN_NELSON,
SKIN_RALPH,
SKIN_CLETUS,
SKIN_ZOMBIE1,
SKIN_ZOMBIE2,
SKIN_ZOMBIE3,
SKIN_ZOMBIE4,
SKIN_OTTO,
SKIN_WILLIE,
SKIN_KEARNEY,
SKIN_SKINNER,
SKIN_GRANDPA,
SKIN_CBG,
SKIN_FRINK,
SKIN_SNAKE,
SKIN_SMITHERS,
SKIN_NUM_TYPES
};
struct pedTypeInfo
{
radInt64 pedUID;
const char* pedName;
pedDialogType dialogGroup;
};
static pedTypeInfo pedestrianNameTable[] =
{
{ 0, "boy", PED_BOY1 },
{ 0, "boy2", PED_BOY2 },
{ 0, "boy3", PED_BOY1 },
{ 0, "bum", PED_MALE1 },
{ 0, "busm1", PED_MALE2 },
{ 0, "busm2", PED_MALE1 },
{ 0, "busw1", PED_FEMALE1 },
{ 0, "const1", PED_MALE2 },
{ 0, "const2", PED_MALE1 },
{ 0, "farmr1", PED_MALE2 },
{ 0, "fem1", PED_FEMALE2 },
{ 0, "fem2", PED_FEMALE1 },
{ 0, "fem3", PED_FEMALE2 },
{ 0, "fem4", PED_FEMALE1 },
{ 0, "girl1", PED_GIRL1 },
{ 0, "girl2", PED_GIRL2 },
{ 0, "hooker", PED_FEMALE2 },
{ 0, "joger1", PED_FEMALE1 },
{ 0, "joger2", PED_MALE1 },
{ 0, "male1", PED_MALE2 },
{ 0, "male2", PED_MALE1 },
{ 0, "male3", PED_MALE2 },
{ 0, "male4", PED_MALE1 },
{ 0, "male5", PED_MALE2 },
{ 0, "male6", PED_MALE1 },
{ 0, "mobstr", PED_MALE2 },
{ 0, "nuclear", PED_MALE1 },
{ 0, "olady1", PED_FEMALE2 },
{ 0, "olady2", PED_FEMALE1 },
{ 0, "olady3", PED_FEMALE2 },
{ 0, "rednk1", PED_BOY2 },
{ 0, "rednk2", PED_BOY1 },
{ 0, "sail1", PED_MALE2 },
{ 0, "sail2", PED_MALE1 },
{ 0, "sail3", PED_MALE2 },
{ 0, "sail4", PED_MALE1 },
{ 0, "witch", PED_GIRL1 },
{ 0, "frankenstein", PED_BOY2 },
{ 0, "zfem1", PED_ZOMBIE3 },
{ 0, "zfem5", PED_ZOMBIE3 },
{ 0, "zmale1", PED_ZOMBIE1 },
{ 0, "zmale3", PED_ZOMBIE2 },
{ 0, "zmale4", PED_ZOMBIE4 }
};
static unsigned int pedestrianTableLength = sizeof( pedestrianNameTable ) / sizeof( pedTypeInfo );
struct skinTypeInfo
{
radInt64 skinUID;
const char* skinName;
skinDialogType dialogGroup;
};
static skinTypeInfo skinNameTable[] =
{
{ 0, "a_american", SKIN_APU },
{ 0, "a_army", SKIN_APU },
{ 0, "a_besharp", SKIN_APU },
{ 0, "b_football", SKIN_BART },
{ 0, "b_hugo", SKIN_BART },
{ 0, "b_man", SKIN_BART },
{ 0, "b_military", SKIN_BART },
{ 0, "b_ninja", SKIN_BART },
{ 0, "b_tall", SKIN_BART },
{ 0, "h_donut", SKIN_HOMER },
{ 0, "h_evil", SKIN_HOMER },
{ 0, "h_fat", SKIN_HOMER },
{ 0, "h_scuzzy", SKIN_HOMER },
{ 0, "h_stcrobe", SKIN_HOMER },
{ 0, "h_undrwr", SKIN_HOMER },
{ 0, "reward_homer", SKIN_HOMER },
{ 0, "l_cool", SKIN_LISA },
{ 0, "l_florida", SKIN_LISA },
{ 0, "l_jersey", SKIN_LISA },
{ 0, "m_pink", SKIN_MARGE },
{ 0, "m_police", SKIN_MARGE },
{ 0, "m_prison", SKIN_MARGE },
{ 0, "brn_unf", SKIN_BARNEY },
{ 0, "reward_barney", SKIN_BARNEY },
{ 0, "b_milhouse", SKIN_MILHOUSE },
{ 0, "b_nelson", SKIN_NELSON },
{ 0, "b_ralph", SKIN_RALPH },
{ 0, "b_cletus", SKIN_CLETUS },
{ 0, "b_zmale1", SKIN_ZOMBIE1 },
{ 0, "b_zmale3", SKIN_ZOMBIE2 },
{ 0, "b_zfem5", SKIN_ZOMBIE3 },
{ 0, "b_zmale4", SKIN_ZOMBIE4 },
{ 0, "b_zfem1", SKIN_ZOMBIE3 },
{ 0, "b_skinner", SKIN_SKINNER },
{ 0, "b_grandpa", SKIN_GRANDPA },
{ 0, "b_cbg", SKIN_CBG },
{ 0, "b_barney", SKIN_BARNEY },
{ 0, "b_frink", SKIN_FRINK },
{ 0, "b_snake", SKIN_SNAKE },
{ 0, "b_smithers", SKIN_SMITHERS },
{ 0, "reward_otto", SKIN_OTTO },
{ 0, "reward_willie", SKIN_WILLIE },
{ 0, "reward_kearney", SKIN_KEARNEY }
};
static unsigned int skinTableLength = sizeof( skinNameTable ) / sizeof( skinTypeInfo );
struct pedDialogGroupInfo
{
radInt64 pedUID;
const char* pedName;
};
//
// Size must be PED_NUM_TYPES
//
static pedDialogGroupInfo dialogGroupTable[] =
{
{ 0, "male1" },
{ 0, "male2" },
{ 0, "fem1" },
{ 0, "fem2" },
{ 0, "boy1" },
{ 0, "boy2" },
{ 0, "girl1" },
{ 0, "girl2" },
{ 0, "zombie1" },
{ 0, "zombie2" },
{ 0, "zombie3" },
{ 0, "zombie4" }
};
struct skinDialogGroupInfo
{
radInt64 charUID;
const char* charName;
};
//
// Size must be SKIN_NUM_TYPES
//
static skinDialogGroupInfo skinDialogGroupTable[] =
{
{ 0, "apu" },
{ 0, "bart" },
{ 0, "homer" },
{ 0, "lisa" },
{ 0, "marge" },
{ 0, "barney" },
{ 0, "milhouse" },
{ 0, "nelson" },
{ 0, "ralph" },
{ 0, "cletus" },
{ 0, "zombie1" },
{ 0, "zombie2" },
{ 0, "zombie3" },
{ 0, "zombie4" },
{ 0, "otto" },
{ 0, "willie" },
{ 0, "kearney" },
{ 0, "skinner" },
{ 0, "grandpa" },
{ 0, "cbg" },
{ 0, "frink" },
{ 0, "snake" },
{ 0, "smithers" }
};
//
// Debug flag
//
bool DialogList::s_showDialogSpew = false;
//******************************************************************************
//
// Public Member Functions
//
//******************************************************************************
//==============================================================================
// DialogList::DialogList
//==============================================================================
// Description: Constructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
DialogList::DialogList()
{
unsigned int i;
//
// Lazy initialization
//
if( s_introKey == 0 )
{
s_introKey = ::radMakeKey32( "intro" );
s_aztecKey = ::radMakeKey32( "aztec" );
s_milhouseKey = tEntity::MakeUID( "milhouse" );
s_nelsonKey = tEntity::MakeUID( "nelson" );
s_raceZombie1 = tEntity::MakeUID( "zmale3" );
s_raceZombie2 = tEntity::MakeUID( "zfem1" );
//
// Also do the tables of UIDs we use to identify peds and skins
//
for( i = 0; i < pedestrianTableLength; i++ )
{
pedestrianNameTable[i].pedUID = tEntity::MakeUID( pedestrianNameTable[i].pedName );
}
for( i = 0; i < PED_NUM_TYPES; i++ )
{
dialogGroupTable[i].pedUID = tEntity::MakeUID( dialogGroupTable[i].pedName );
}
for( i = 0; i < skinTableLength; i++ )
{
skinNameTable[i].skinUID = tEntity::MakeUID( skinNameTable[i].skinName );
}
for( i = 0; i < SKIN_NUM_TYPES; i++ )
{
skinDialogGroupTable[i].charUID = tEntity::MakeUID( skinDialogGroupTable[i].charName );
}
//
// Debug spew
//
radDbgWatchAddBoolean( &s_showDialogSpew, "Show Dialog Spew", "Sound Info" );
radDbgWatchAddFunction( "Print Dialog Coverage", &dumpDialogCoverage, this, "Sound Info" );
}
}
//==============================================================================
// DialogList::~DialogList
//==============================================================================
// Description: Destructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
DialogList::~DialogList()
{
}
//=============================================================================
// DialogList::OrganizeDialog
//=============================================================================
// Description: Go through the list of dialog resources, creating SelectableDialog
// objects organized as directed by the naming conventions.
//
// Parameters: namespaceObj -- object containing the list of dialog resources
// as read from the script file
//
// Return: void
//
//=============================================================================
void DialogList::OrganizeDialog( IRadNameSpace* namespaceObj )
{
int mission;
int level;
IDaSoundResource* resource;
DialogLine* newLine;
ConversationMatcher matcher;
SelectableDialogList* dialogList;
SelectableDialog* foundDialog;
//
// Go through the list of sound resources looking for dialog
//
resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetFirst( NULL) );
while( resource != NULL )
{
if( isIndividualLine( resource ) )
{
//
// Resource is dialog but not conversation. Create a DialogLine object
// to hold the information about it and store it in the appropriate list.
//
#ifdef RAD_GAMECUBE
newLine = new( GMA_GC_VMM ) DialogLine( resource );
#else
newLine = new( GMA_PERSISTENT ) DialogLine( resource );
#endif
if( newLine->IsLevelSpecific() )
{
dialogList = &(m_missionLists[newLine->GetLevel() - 1][newLine->GetMission()]);
}
else
{
dialogList = &m_genericDialogList;
}
//
// Search the list. If we've already got a dialog for the same situation, lump
// this one in with it, otherwise stash it straight into the list
//
foundDialog = searchDialogList( newLine->GetEvent(), newLine->GetCharacterUID(), 0,
*dialogList, 0, newLine->IsVillainLine(), false );
HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
if( foundDialog != NULL )
{
foundDialog->AddMatchingDialog( *newLine, *dialogList );
}
else
{
dialogList->push_back( newLine );
}
HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
}
else if( isConversationLine( resource ) )
{
//
// Resource is part of a conversation. Give it to the object
// responsible for matching the pieces together
//
matcher.AddNewLine( resource );
}
//
// Otherwise, this isn't dialog, so we don't have to do anything with it
//
//
// Next resource in the list
//
resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetNext( NULL) );
}
//
// Do a sanity check on the conversations
//
rAssert( matcher.AreAllConversationsComplete() );
//
// Add the completed conversations to the appropriate lists
//
for( level = 0; level < GameplayManager::MAX_LEVELS; level++ )
{
for( mission = 0; mission < GameplayManager::MAX_MISSIONS; mission++ )
{
matcher.AddConversationsToList( level + 1, mission, m_missionLists[level][mission] );
}
}
matcher.AddConversationsToList( SelectableDialog::NO_LEVEL, SelectableDialog::NO_MISSION, m_genericDialogList );
}
//=============================================================================
// DialogList::FindDialogForEvent
//=============================================================================
// Description: Search through the dialog lists to find something appropriate
// for the given dialog event
//
// Parameters: id - Event ID that we need to find dialog for
// character1 - character who will say the dialog
// character2 - if conversation, second character in conversation.
// NULL otherwise.
// charUID1 - if we're searching by UID, this is non-zero and
// character1 and character2 are NULL.
// charUID2 - second character, used for searching by UID
// convKey - name of conversation, 0 if not applicable
//
// Return: Const pointer to SelectableDialog object best matching the
// event, or NULL if nothing found
//
//=============================================================================
SelectableDialog* DialogList::FindDialogForEvent( EventEnum id,
Character* character1,
Character* character2,
tUID charUID1,
tUID charUID2,
radKey32 convKey,
bool isVillain )
{
int mission;
tUID char1UID;
tUID char2UID;
char nameBuffer[20];
unsigned int aztecNumber;
GameplayManager* gameplayMgr = NULL;
// For indexing purposes, levels count from zero.
int level;
SelectableDialog* dialogMatch = NULL;
Mission* missionObj;
if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND )
{
//
// Playing dialog in front end, so any level will do
//
level = 0;
missionObj = NULL;
}
else
{
gameplayMgr = GetGameplayManager();
rAssert( gameplayMgr != NULL );
level = gameplayMgr->GetCurrentLevelIndex() - RenderEnums::L1;
missionObj = gameplayMgr->GetCurrentMission();
}
//
// First, search the list for the current mission. If it's a conversation
// event, those only happen during missions even if we're in Sunday Drive
// (they're the conversations that start the missions) so just start looking there.
//
if( missionObj != NULL )
{
if( ( !(gameplayMgr->IsSuperSprint()) )
&& ( (!(missionObj->IsSundayDrive()) )
|| ( id == EVENT_CONVERSATION_INIT_DIALOG )
|| ( id == EVENT_TUTORIAL_DIALOG_PLAY ) ) )
{
if( id == EVENT_TUTORIAL_DIALOG_PLAY )
{
mission = DialogLine::TUTORIAL_MISSION_NUMBER;
}
else if( missionObj->IsBonusMission() )
{
mission = DialogLine::BONUS_MISSION_NUMBER;
}
else if( missionObj->IsRaceMission() )
{
mission = DialogLine::FIRST_RACE_MISSION_NUMBER +
( gameplayMgr->GetCurrentMissionNum() - GameplayManager::MAX_MISSIONS );
}
else
{
if( convKey == s_introKey )
{
//
// Stinky race missions. The "intro" conversation happens before we've
// started the race. I can't rename them to simple L1, since characters like Homer
// have multiple C_intro_*_L1.rsd lines, so there's a naming clash. Ugh.
// To make matters worse, since some of these conversations involve only Homer (or
// whoever the driver is), we need to check if either character is Milhouse, Nelson,
// Ralph, or their zombie counterparts
//
rAssert( character1 != NULL );
rAssert( character2 != NULL );
char1UID = getPuppetUID( character1 );
char2UID = getPuppetUID( character2 );
if( ( char1UID == s_milhouseKey )
|| ( char2UID == s_milhouseKey )
|| ( char1UID == s_raceZombie1 )
|| ( char2UID == s_raceZombie1 ) )
{
mission = DialogLine::FIRST_RACE_MISSION_NUMBER;
}
else if( ( char1UID == s_nelsonKey )
|| ( char2UID == s_nelsonKey )
|| ( char1UID == s_raceZombie2 )
|| ( char2UID == s_raceZombie2 ) )
{
mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 1;
}
else
{
//
// This had better be Ralph or zombie Ralph
//
mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 2;
}
}
else if( level == 0 )
{
//
// Stinky level 1, tutorial mission screws everything up
//
mission = gameplayMgr->GetCurrentMissionIndex();
}
else
{
mission = gameplayMgr->GetCurrentMissionIndex() + 1;
#ifdef RAD_E3
//
// E3 hack. L2M5 is our only mission, and it's going to
// come back as mission 1. Hack it to 5.
//
mission = 5;
#endif
}
if ( convKey == s_aztecKey )
{
//
// Another stinky hack. The teen at the Aztec needs randomized conversations.
// Conversations don't really randomize because the conversation builder assumes
// that identically-named conversations result from misnamed files. And the
// key isn't a straightforward randomization on the caller's end, since it's
// a scripted value. I'll handle it here.
//
aztecNumber = ( rand() % 4 ) + 1;
sprintf( nameBuffer, "aztec%d", aztecNumber );
convKey = ::radMakeKey32( nameBuffer );
}
}
if( s_showDialogSpew )
{
rTuneString( "Searching mission-specific dialog\n" );
}
if( character1 == NULL )
{
// Already have UIDs
dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][mission], convKey, isVillain );
}
else
{
// Take UID from character objects
dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][mission], convKey, isVillain );
}
}
}
if( dialogMatch == NULL )
{
//
// No mission-specific dialog, search the level-specific stuff
//
if( s_showDialogSpew )
{
rTuneString( "Searching level-specific dialog\n" );
}
if( character1 == NULL )
{
dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][0], convKey, isVillain );
}
else
{
dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][0], convKey, isVillain );
}
if( dialogMatch == NULL )
{
//
// No mission- or level-specific dialog, search the generic list
//
if( s_showDialogSpew )
{
rTuneString( "Searching generic dialog\n" );
}
if( character1 == NULL )
{
dialogMatch = searchDialogList( id, charUID1, charUID2, m_genericDialogList, convKey, isVillain );
}
else
{
dialogMatch = searchDialogList( id, character1, character2, m_genericDialogList, convKey, isVillain );
}
}
}
return( dialogMatch );
}
//=============================================================================
// DialogList::GetStinkySkinPointer
//=============================================================================
// Description: Given a UID, see if we can dig up a character for it looking
// through all the possible skins.
//
// Parameters: charUID - tUID of character
//
// Return: Character* if match found, NULL otherwise
//
//=============================================================================
Character* DialogList::GetStinkySkinPointer( tUID charUID )
{
int skinType;
unsigned int i;
Character* charPtr;
for( skinType = 0; skinType < SKIN_NUM_TYPES; skinType++ )
{
if( skinDialogGroupTable[skinType].charUID == charUID )
{
break;
}
}
if( skinType == SKIN_NUM_TYPES )
{
//
// No skin exists for given character
//
return( NULL );
}
//
// At this point, the character has skins. Look for a match.
//
for( i = 0; i < skinTableLength; i++ )
{
if( skinNameTable[i].dialogGroup == skinType )
{
charPtr = GetCharacterManager()->GetCharacterByName( skinNameTable[i].skinUID );
if( charPtr != NULL )
{
return( charPtr );
}
}
}
//
// No skins found
//
return( NULL );
}
//******************************************************************************
//
// Private Member Functions
//
//******************************************************************************
//=============================================================================
// DialogList::hasOneLinerPrefix
//=============================================================================
// Description: Determines if resource name belongs to one-liner dialog. This
// is deemed to be true if it starts with a valid role abbreviation.
//
// Parameters: name - name of resource
//
// Return: true if role field found, false otherwise
//
//=============================================================================
bool DialogList::hasOneLinerPrefix( const char* name )
{
return( ( name[1] == '_' )
&& ( ( name[0] == 'W' )
|| ( name[0] == 'D' )
|| ( name[0] == 'P' )
|| ( name[0] == 'V' ) ) );
}
//=============================================================================
// DialogList::isIndividualLine
//=============================================================================
// Description: Test for whether given resource is a dialog one-liner
//
// Parameters: resource - sound resource to test
//
// Return: true if one-liner, false otherwise
//
//=============================================================================
bool DialogList::isIndividualLine( IDaSoundResource* resource )
{
char tempBuffer[FILENAME_BUFFER_LEN];
char buffer[FILENAME_BUFFER_LEN];
//
// Get the first filename belonging to the resource. Don't bother checking
// for >1 file---if they exist, then the names had better be interchangable.
//
tempBuffer[0] = '\0';
resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN );
rAssert( strlen( tempBuffer ) > 0 );
DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN );
//
// Simple test: we'll call it a line if it has at least two underscores
// and no "C_" prefix
//
return( ( !hasConversationPrefix( buffer ) )
&& ( hasOneLinerPrefix( buffer ) )
&& ( underscoreCount( buffer ) > 1 ) );
}
//=============================================================================
// DialogList::isConversationLine
//=============================================================================
// Description: Test for whether given resource is part of a dialog
// conversation
//
// Parameters: resource - sound resource to test
//
// Return: true if conversation line, false otherwise
//
//=============================================================================
bool DialogList::isConversationLine( IDaSoundResource* resource )
{
char tempBuffer[FILENAME_BUFFER_LEN];
char buffer[FILENAME_BUFFER_LEN];
//
// Get the first filename belonging to the resource. Don't bother checking
// for >1 file---if they exist, then the names had better be interchangable.
//
tempBuffer[0] = '\0';
resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN );
rAssert( strlen( tempBuffer ) > 0 );
DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN );
//
// Test: line belongs to conversation if it has at least three underscores
// and a "C_" prefix
//
return( hasConversationPrefix( buffer ) &&
( underscoreCount( buffer ) > 3 ) );
}
//=============================================================================
// DialogList::underscoreCount
//=============================================================================
// Description: Return number of underscores in the given string
//
// Parameters: name - string to count in
//
// Return: number of underscores found
//
//=============================================================================
unsigned int DialogList::underscoreCount( const char* name )
{
unsigned int i = 0;
unsigned int count = 0;
while( name[i] != '\0' )
{
if( name[i] == '_' )
{
++count;
}
i++;
}
return( count );
}
//=============================================================================
// DialogList::searchDialogList
//=============================================================================
// Description: Comment
//
// Parameters: ( EventEnum id, tUID characterUID1, tUID characterUID2, SelectableDialog* list )
//
// Return: SelectableDialog
//
//=============================================================================
SelectableDialog* DialogList::searchDialogList( EventEnum id, Character* character1,
Character* character2, SelectableDialogList& list,
radKey32 convName, bool isVillain )
{
tUID UID1 = 0;
tUID UID2 = 0;
if( character1 == NULL )
{
UID1 = 0;
}
else
{
//
// Can't just get the character UID, since they're not guaranteed to be consistent
// with the model you see on the screen. Need the skeleton UID, it appears
//
UID1 = getPuppetUID( character1 );
}
if( character2 == NULL )
{
UID2 = 0;
}
else
{
UID2 = getPuppetUID( character2 );
}
return( searchDialogList( id, UID1, UID2, list, convName, isVillain, true ) );
}
//=============================================================================
// DialogList::searchDialogList
//=============================================================================
// Description: Comment
//
// Parameters: ( EventEnum id, tUID driverUID, SelectableDialogList& list )
//
// Return: SelectableDialog
//
//=============================================================================
SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1,
tUID characterUID2,
SelectableDialogList& list,
radKey32 convName,
bool isVillain )
{
return( searchDialogList( id, characterUID1, characterUID2, list, convName, isVillain, true ) );
}
//=============================================================================
// DialogList::searchDialogList
//=============================================================================
// Description: Search the given list for a SelectableDialog object with the
// given event ID.
//
// Parameters: id - event ID to find a match for
// list - list to search
//
// Return: pointer to SelectableDialog object matching the event ID if
// it exists, NULL otherwise
//
//=============================================================================
SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1,
tUID characterUID2, SelectableDialogList& list,
radKey32 convName, bool isVillain, bool fuzzyPedMatch )
{
char eventName[30];
char char1Name[30];
char char2Name[30];
char convBuffer[30];
char villain[3];
SelectableDialog* currentDialog;
SelectableDialogList::const_iterator iter = list.begin();
SelectableDialog* returnValue = NULL;
if( s_showDialogSpew )
{
//
// Print a message for the stuff we're trying to match with
//
DialogLine::FillEventName( eventName, 30, id );
DialogLine::FillCharacterName( char1Name, 30, characterUID1 );
DialogLine::FillCharacterName( char2Name, 30, characterUID2 );
if( convName != 0 )
{
sprintf( convBuffer, ", conv %d", convName );
}
else
{
convBuffer[0] = '\0';
}
if( isVillain )
{
villain[0] = 'V';
}
else
{
villain[0] = 'W';
}
villain[1] = '\0';
rTunePrintf( "Dialog: Looking for event %s, char1 %s, char2 %s %s %s\n",
eventName, char1Name, char2Name, villain, convBuffer );
}
for( ; iter != list.end(); ++iter )
{
currentDialog = *iter;
if( s_showDialogSpew )
{
//
// Print a message for the stuff we're currently looking at
//
DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
if( currentDialog->GetNumDialogLines() > 1 )
{
DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
}
else
{
char2Name[0] = '-';
char2Name[1] = '\0';
}
if( convName != 0 )
{
sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
}
else
{
convBuffer[0] = '\0';
}
if( currentDialog->IsVillainLine() )
{
villain[0] = 'V';
}
else
{
villain[0] = 'W';
}
villain[1] = '\0';
rTunePrintf( "Dialog: Matching against event %s, char1 %s, char2 %s %s %s\n",
eventName, char1Name, char2Name, villain, convBuffer );
}
if( ( currentDialog->GetEvent() == id )
&& ( currentDialog->IsVillainLine() == isVillain )
// If a conversation name is supplied, that has to match
&& ( ( convName == 0 )
|| ( currentDialog->GetConversationName() == convName ) ) )
{
if( currentDialog->GetNumDialogLines() == 1 )
{
//
// Match either character
//
if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch )
|| characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) )
{
returnValue = currentDialog;
}
}
else
{
//
// Multi-line dialog. Match both.
//
if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch )
&& characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) )
{
returnValue = currentDialog;
}
}
if( returnValue != NULL )
{
if( s_showDialogSpew )
{
rTunePrintf( "Dialog: Match found\n" );
}
//
// We're done
//
break;
}
}
}
return( returnValue );
}
//=============================================================================
// DialogList::characterMatches
//=============================================================================
// Description: Determine whether the given dialog matches the character
// given. UID of zero always matches.
//
// Parameters: characterObj - character to match
// dialog - dialog to match to
// fuzzyPedMatch - true if we want to fudge UIDs to group
// pedestrians, false otherwise
//
// Return: true if match, false otherwise
//
//=============================================================================
bool DialogList::characterMatches( tUID characterUID, SelectableDialog* dialog,
bool fuzzyPedMatch )
{
unsigned int i;
tUID effectiveUID; // Unix humour. Nyuck!
pedDialogType dialogType;
bool switchMade = false;
if( characterUID == static_cast< tUID >( 0 ) )
{
return( false );
}
//
// Argh!! We have a whole bunch of pedestrian UIDs which need to be mapped
// to eight dialog characters. If this actually shows in a profiler, we'll
// need to mark the peds in the Character objects when they're spawned somehow
// to avoid this search
//
//
// Double argh!! Now we've got a bunch of character skins that are breaking
// the dialog system. We have to search for those as well.
//
effectiveUID = characterUID;
if( fuzzyPedMatch )
{
for( i = 0; i < pedestrianTableLength; i++ )
{
if( effectiveUID == static_cast< tUID >( pedestrianNameTable[i].pedUID ) )
{
//
// Is ped, map new UID
//
//
// Another hack: zombie1/2 and zombie3/4 should be randomly chosen.
// TODO: leave zombie3 as zombie3 for E3.
//
dialogType = pedestrianNameTable[i].dialogGroup;
if( ( dialogType == PED_ZOMBIE1 )
&& ( dialog->GetEvent() != EVENT_CONVERSATION_INIT_DIALOG )
&& ( dialog->GetEvent() != EVENT_IN_GAMEPLAY_CONVERSATION ) )
{
if( ( rand() % 2 ) == 0 )
{
dialogType = PED_ZOMBIE2;
}
}
effectiveUID = dialogGroupTable[dialogType].pedUID;
switchMade = true;
break;
}
}
if( !switchMade )
{
//
// Not a ped, check for skins
//
for( i = 0; i < skinTableLength; i++ )
{
if( effectiveUID == static_cast< tUID >( skinNameTable[i].skinUID ) )
{
//
// Is skin, map new UID
//
effectiveUID = skinDialogGroupTable[skinNameTable[i].dialogGroup].charUID;
break;
}
}
}
}
return( dialog->UsesCharacter( effectiveUID ) );
}
//=============================================================================
// DialogList::getPuppetUID
//=============================================================================
// Description: Get UID for Choreo puppet for character
//
// Parameters: ( Character* characterPtr )
//
// Return: tUID if skeleton found, 0 otherwise
//
//=============================================================================
tUID DialogList::getPuppetUID( Character* characterPtr )
{
const char* modelName;
rAssert( characterPtr != NULL );
modelName = GetCharacterManager()->GetModelName( characterPtr );
if( modelName != NULL )
{
return( tEntity::MakeUID( modelName ) );
}
else
{
return( 0 );
}
}
void DialogList::dumpDialogCoverage( void* userData )
{
#ifndef RAD_RELEASE
SelectableDialogList::const_iterator iter;
int i, j;
SelectableDialog* currentDialog;
char eventName[30];
char char1Name[30];
char char2Name[30];
char convBuffer[30];
DialogList* listObj = static_cast<DialogList*>(userData);
for( i = 0; i < GameplayManager::MAX_LEVELS; i++ )
{
for( j = 0; j < GameplayManager::MAX_MISSIONS+1; j++ )
{
rTunePrintf( "\nDialogue for level %d mission %d list\n", i, j );
iter = listObj->m_missionLists[i][j].begin();
for( ; iter != listObj->m_missionLists[i][j].end(); ++iter )
{
currentDialog = *iter;
if( currentDialog != NULL )
{
DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
if( currentDialog->GetNumDialogLines() > 1 )
{
DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
}
else
{
char2Name[0] = '-';
char2Name[1] = '\0';
}
sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ",
eventName, char1Name, char2Name, convBuffer );
currentDialog->PrintPlayedStatus();
}
}
}
}
rTuneString( "\nGeneric dialogue list:\n" );
iter = listObj->m_genericDialogList.begin();
for( ; iter != listObj->m_genericDialogList.end(); ++iter )
{
currentDialog = *iter;
if( currentDialog != NULL )
{
DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
if( currentDialog->GetNumDialogLines() > 1 )
{
DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
}
else
{
char2Name[0] = '-';
char2Name[1] = '\0';
}
sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ",
eventName, char1Name, char2Name, convBuffer );
currentDialog->PrintPlayedStatus();
}
}
#endif
}