Files
gta5_build_patches/game/audio/scriptaudioentity.cpp
T
Akkariin a4f85d47ac Init
2024-01-01 01:47:09 +08:00

9767 lines
358 KiB
C++

//
// audio/scriptaudioentity.cpp
//
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
//
#include "scriptaudioentity.h"
#include "frontendaudioentity.h"
#include "planeaudioentity.h"
#include "ambience/ambientaudioentity.h"
#include "music/musicplayer.h"
#include "northaudioengine.h"
#include "radioaudioentity.h"
#include "policescanner.h"
#include "speechmanager.h"
#include "debugaudio.h"
#include "audioengine/engine.h"
#include "audioengine/engineutil.h"
#include "audioengine/soundfactory.h"
#include "audiosoundtypes/simplesound.h"
#include "audiohardware/waveslot.h"
#include "audiohardware/driver.h"
#include "audio/caraudioentity.h"
#include "debug/debug.h"
#include "debug/debugglobals.h"
#include "dynamicmixer.h"
#include "text/messages.h"
#include "peds/ped.h"
#include "peds/PedPhoneComponent.h"
#include "peds/popzones.h"
#include "control/replay/Audio/ScriptAudioPacket.h"
#include "scene/RefMacros.h"
#include "script/script.h"
#include "system/FileMgr.h"
#include "game/zones.h"
#include "vehicles/automobile.h"
#include "Vfx/Misc/Fire.h"
#include "network/NetworkInterface.h"
#include "network/Events/NetworkEventTypes.h"
#include "game/user.h"
#include "text/TextConversion.h"
#include "debugaudio.h"
#include <ctime>
AUDIO_OPTIMISATIONS()
RAGE_DEFINE_SUBCHANNEL(Audio,Conversation)
RAGE_DEFINE_SUBCHANNEL(Audio,NetworkAudio)
//OPTIMISATIONS_OFF()
namespace rage
{
XPARAM(audiowidgets);
}
PF_PAGE(ScriptedSpeechTrackerPage, "Scripted Speech Tracker Page");
PF_GROUP(ScriptedSpeechStats);
PF_LINK(ScriptedSpeechTrackerPage, ScriptedSpeechStats);
PF_VALUE_INT(TimeToPreloadScriptedSpeech, ScriptedSpeechStats);
PF_VALUE_INT(TimeTilTriggeredSpeechPlayed, ScriptedSpeechStats);
XPARAM(audiodesigner);
PARAM(enableConvInterrupt, "Enable scripted conversation interrupts.");
PARAM(disableConvInterrupt, "Disable scripted conversation interrupts.");
PARAM(conversationspew, "Ensable scripted conversation spew");
PARAM(noAbortingFailedConv, "Do not abort conversations that fail to load.");
PARAM(noOverlapLipsyncAdjustment, "Do not adjust conversation overlap times for lipsync predelay.");
PARAM(trackMissingDialogue, "Output info on missing scripted speech lines to a timestamp-named CSV file.");
PARAM(trackMissingDialogueMP, "Output info on missing scripted speech lines to a timestamp-named CSV file (including MP).");
PARAM(displayScriptedSpeechLine, "Debug draw the current playing scripted speech line.");
PARAM(useRobotSpeechForAllLines, "If not set, robot speech will only be used for non-placeholder conversations");
PARAM(scriptStreamSpew, "Ensable scripted stream spew");
PARAM(scriptSoundsDebug, "Ensable scripted sounds on screen debug");
audScriptAudioEntity g_ScriptAudioEntity;
extern audAmbientAudioEntity g_AmbientAudioEntity;
extern const char* g_CarCodeUnitTypeStrings[26];
extern f32 g_HealthLostForHighPain;
audHashActionTable audScriptAudioEntity::sm_ScriptActionTable;
s32 audScriptAudioEntity::sm_ScriptInChargeOfTheStream = -1;
const s32 g_PhoneOverlapTime = 250;
u32 g_TimeToWaitForInterruptToPrepare = 2500;
NOTFINAL_ONLY(bool g_EnablePlaceholderDialogue = true);
#if __BANK
bool audScriptAudioEntity::sm_DrawScriptTriggeredSounds = false;
bool audScriptAudioEntity::sm_DrawScriptStream = false;
bool audScriptAudioEntity::sm_InWorldDrawScriptTriggeredSounds = false;
bool audScriptAudioEntity::sm_LogScriptTriggeredSounds = false;
bool audScriptAudioEntity::sm_DrawScriptBanksLoaded = false;
bool audScriptAudioEntity::sm_FilterScriptTriggeredSounds = false;
char audScriptAudioEntity::sm_ScriptTriggerOverride[64] = "\0";
char audScriptAudioEntity::sm_ScriptedLineDebugInfo[128] = "\0";
bool audScriptAudioEntity::sm_DrawReplayScriptSpeechSlots = false;
bool g_FakeConvAnimTrigger = false;
bool g_AnimTriggersControllingConversation = false;
bool g_ConversationDebugSpew = false;
bool g_ScriptedStreamSpew = false;
bool g_ForceMobileInterference = false;
bool g_DisplayScriptedLineInfo = false;
bool g_DisplayScriptSoundIdInfo = false;
bool g_DisplayScriptBankUsage = false;
bool g_DrawScriptSpeechSlots = false;
f32 g_ScriptSoundIdInfoScroll = 0.0f;
static char g_AmbientSpeechDebugContext[128] = "CAR_HIT_PED";
static char g_AmbientSpeechDebugVoice[128] = "MICHAEL_NORMAL";
static char g_AmbientSpeechDebugParams[128] = "SPEECH_PARAMS_STANDARD";
f32 g_AmbientSpeechDebugPosX = 0.0f;
f32 g_AmbientSpeechDebugPosY = 0.0f;
f32 g_AmbientSpeechDebugPosZ = 0.0f;
bool g_TrackMissingDialogue = false;
bool g_TrackMissingDialogueMP = false;
#endif
bool g_ForceScriptedInterrupts = false;
bool g_DoNotAbortFailedConversations = false;
bool g_AdjustOverlapForLipsyncPredelay = true;
bool g_AdjustTriggerTimeForZeroPlaytime = true;
bool g_AdjustTriggerForZeroEvenIfMarkedOverlap = true;
u32 g_MinPedsHitForRunnedOverReaction = 3;
u32 g_TimespanToHitPedForRunnedOverReaction = 6000;
u32 g_MinTimeBetweenLastCollAndRunnedOverReaction = 2000;
u32 g_TimeToWaitBetweenRunnedOverReactions = 5000;
u32 g_AdditionalScriptedSpeechPause = 0;
bool g_DuckForConvAfterPreload = false;
bool g_DebugPlayingScriptSounds = false;
s32 g_MissionCompleteDelay = 1500;
extern s32 g_NextFireStartOffset;
extern bool g_MuteGameWorldAndPositionedRadioForTV;
bool g_AllowUpsideDownConversations = false;
bool g_AllowVehicleOnFireConversations = false;
bool g_AllowVehicleDrowningConversations = false;
s32 g_StopAmbientAnimTime = 1000;
bool g_DebugScriptedConvs = false;
const u32 g_FrontendGameMobileRingingHash = ATSTRINGHASH("FRONTEND_GAME_MOBILE_RINGING", 0x0980d4ab6);
const u32 g_MobilePreRingHash = ATSTRINGHASH("MOBILE_PRERING_SOUND", 0x0b88f89fb);
const u32 g_MobileGetSignalHash = ATSTRINGHASH("MOBILE_GET_SIGNAL_SOUND", 0x0428e734b);
f32 g_MinLowPainBikeReaction = 45.0f;
f32 g_MinLowReactionDamage = 11.0f;
f32 g_MinMediumReactionDamage = 20.0f;
f32 g_MinHighReactionDamage = 100.0f;
u32 g_TimeToPauseConvOnCollision = 1500;
u32 g_MinTimeBetweenCollisionReactions = 5000;
u32 g_MinTimeBetweenCollisionTriggers = 5000;
u32 g_MaxTimeToAllocateSlot = 5000;
bool g_DisableInterrupts = false;
bool g_DisableAirborneInterrupts = false;
bool g_AllowMuliWantedInterrupts = false;
s32 g_MinRagdollTimeBeforeConvPause = 500;
s32 g_MinNotRagdollTimeBeforeConvRestart = 3000;
u32 g_MinTimeCarInAirForInterrupt = 700;
u32 g_MinTimeCarOnGroundForInterrupt = 10000;
u32 g_MinTimeCarOnGroundToRestart = 750;
u32 g_MinAirBorneTimeToKillConversation = 8000;
u32 g_MaxAirBorneTimeToApplyRestartOffset = 4000;
f32 g_MinCarRollForInterrupt = -0.4f;
#if GTA_REPLAY
tReplayScriptedSpeechSlots audScriptAudioEntity::sm_ReplayScriptedSpeechSlot[AUD_REPLAY_SCRIPTED_SPEECH_SLOTS];
tReplayScriptedSpeechSlots audScriptAudioEntity::sm_ReplayScriptedSpeechAlreadyPlayed[AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS];
char audScriptAudioEntity::sm_CurrentStreamName[128];
u32 audScriptAudioEntity::sm_CurrentStartOffset = 0;
char audScriptAudioEntity::sm_CurrentSetName[128];
#endif
#if __BANK
int g_InterruptSpeakerNum = 0;
bool g_ForceNonPlayerInterrupt = false;
char g_szLowInterruptContext[256];
char g_szMediumInterruptContext[256];
char g_szHighInterruptContext[256];
#endif
AircraftWarningSettings* g_AircraftWarningSettings = NULL;
//u32 g_AircraftWarningPHash = ATPARTIALSTRINGHASH("AIRCRAFT_WARNING", 0xF65A0A93);
u32 g_AircraftWarningMale1Hash = ATSTRINGHASH("AIRCRAFT_WARNING_MALE_01", 0xA65A6402);
u32 g_AircraftWarningFemale1Hash = ATSTRINGHASH("AIRCRAFT_WARNING_FEMALE_01", 0x85A08F9C);
bool g_EnableAircraftWarningSpeech = false;
const u32 g_BarkPHash = ATPARTIALSTRINGHASH("BARK", 0xB0EF9479);
// Create flag map
#if RSG_BANK
#define SCRIPT_AUDIO_FLAG(n,h) ATSTRINGHASH(#n,h), #n,
#else
#define SCRIPT_AUDIO_FLAG(n,h) ATSTRINGHASH(#n,h),
#endif
const audScriptAudioFlags::tFlagNames audScriptAudioFlags::s_FlagNames[audScriptAudioFlags::kNumScriptAudioFlags] = {
#include "scriptaudioflags.h"
};
#undef SCRIPT_AUDIO_FLAG
void audScriptAudioEntity::InitClass()
{
// default handler handles cases where a sound has to be triggered with no special logic
sm_ScriptActionTable.SetDefaultActionHandler(&audScriptAudioEntity::DefaultScriptActionHandler);
// phone call functions
sm_ScriptActionTable.AddActionHandler("MOBILE_PRERING", &g_ScriptAudioEntity.MobilePreRingHandler);
// m_ScriptActionTable.AddActionHandler("START_MOBILE_PHONE_RINGING", &g_ScriptAudioEntity.StartMobilePhoneRinging);
// m_ScriptActionTable.AddActionHandler("START_MOBILE_PHONE_CALLING", &g_ScriptAudioEntity.StartMobilePhoneCalling);
// m_ScriptActionTable.AddActionHandler("STOP_MOBILE_PHONE_RINGING", &g_ScriptAudioEntity.StopMobilePhoneRinging);
}
void audScriptAudioEntity::Init()
{
naAudioEntity::Init();
audEntity::SetName("audScriptAudioEntity");
m_SpeechAudioEntity.Init(NULL);
m_ScriptInChargeOfAudio = -1;
BANK_ONLY(m_CurrentScriptName = "");
m_MobileInterferenceSound = NULL;
m_CellphoneRingSound = NULL;
m_ScriptedConversationInProgress = false;
m_DuckingForScriptedConversation = false;
m_IsScriptedConversationZit = false;
m_ScriptedConversationSpeechSound = NULL;
m_PlayingScriptedConversationLine = -1;
m_IsScriptedConversationAMobileCall = false;
m_DisplaySubtitles = false;
m_CloneConversation = false;
m_AddSubtitlesToBriefing = false;
m_PauseConversation = false;
m_PausedForRagdoll = false;
m_PauseWaitingOnCurrentLine = false;
m_ShouldRestartConvAtMarker = false;
m_AbortScriptedConversationRequested = false;
m_HighestLoadedLine = -1;
m_HighestLoadingLine = -1;
m_PreLoadingSpeechLine = -1;
m_ScriptedConversationOverlapTriggerTime = 0;
m_OverlapTriggerTimeAdjustedForPredelay = false;
m_DisableReplayScriptStreamRecording = false;
m_ScriptedConversationSfx = NULL;
m_CurrentPreloadSet = 0;
m_LastMobileInterferenceTime = 0;
m_NextTimeScriptedConversationCanStart = 0;
m_TimeLastScriptedConvFinished = 0;
m_TimeLastBeepOffPlayed = 0;
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_LINES; i++)
{
m_ScriptedConversation[i].Speaker = -1;
strcpy(m_ScriptedConversation[i].Context, "");
strcpy(m_ScriptedConversation[i].Subtitle, "");
m_ScriptedConversation[i].Listener = -1;
m_ScriptedConversation[i].SpeechSlotIndex = -1;
m_ScriptedConversation[i].Volume = AUD_SPEECH_NORMAL;
m_ScriptedConversation[i].SpeechSlotIndex = 0;
#if !__FINAL
m_ScriptedConversation[i].TimePrepareFirstRequested = 0;
#endif
m_ScriptedConversationBuffer[i].Speaker = -1;
strcpy(m_ScriptedConversationBuffer[i].Context, "");
strcpy(m_ScriptedConversationBuffer[i].Subtitle, "");
m_ScriptedConversationBuffer[i].Listener = -1;
m_ScriptedConversationBuffer[i].SpeechSlotIndex = -1;
m_ScriptedConversationBuffer[i].Volume = AUD_SPEECH_NORMAL;
m_ScriptedConversationBuffer[i].SpeechSlotIndex = 0;
}
m_ScriptedConversationNoAudioDelay = 0;
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
m_Ped[i] = NULL;
strcpy(m_VoiceName[i], "");
m_PedBuffer[i] = NULL;
strcpy(m_VoiceNameBuffer[i], "");
m_PedIsInvolvedInCurrentConversation[i] = false;
}
DYNAMICMIXER.StartScene(ATSTRINGHASH("CONVERSATION_TEST_SCENE", 0x8ba71b0a), &m_ConversationDuckingScene);
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
m_Interrupter = NULL;
m_InterruptingContext[0] = '\0';
m_InterruptingVoiceNameHash = 0;
NOTFINAL_ONLY(m_InterruptingPlaceholderVoiceNameHash = 0;)
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_TimeOfLastCollisionReaction = 0;
m_TimeOfLastCollisionTrigger = 0;
m_TimeToLowerCollisionLevel = 0;
m_SpeechRestartOffset = 0;
m_LineCheckedForRestartOffset = -1;
m_InterruptTriggered = false;
m_TimeLastFrame = 0;
m_TimeElapsedSinceLastUpdate = 0;
m_ConversationIsInterruptible = true;
m_TimePlayersCarWasLastOnTheGround = 0;
m_TimePlayersCarWasLastInTheAir = 0;
m_TimeOfLastInAirInterrupt = 0;
m_TimeLastRegisteredCarInAir = 0;
m_ScriptSetNoDucking = false;
m_HeadSetBeepOnLineIndex = -1;
m_HeadSetBeepOffCachedVolume = 0.f;
m_TimeOfLastCarPedCollision = 0;
m_TimeLastTriggeredPedCollisionScreams = 0;
for(int i=0; i<m_CarPedCollisions.GetMaxCount(); i++)
{
m_CarPedCollisions[i].Victim = NULL;
m_CarPedCollisions[i].TimeOfCollision = 0;
}
// Set up our slots
for (int i=0; i<AUD_MAX_SCRIPT_BANK_SLOTS; i++)
{
char slotName[32];
sprintf(slotName, "AMBIENT_SCRIPT_SLOT_%d", i);
m_BankSlots[i].BankSlot = audWaveSlot::FindWaveSlot(slotName);
m_BankSlots[i].BankId = AUD_INVALID_BANK_ID;
m_BankSlots[i].BankNameHash = g_NullSoundHash;
m_BankSlots[i].ReferenceCount = 0;
m_BankSlots[i].HintLoaded = false;
m_BankSlots[i].State = AUD_SCRIPT_BANK_LOADED;
m_BankSlots[i].TimeWeStartedWaitingToFinish = 0;
for(int j=0; j<AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
m_BankSlots[i].NetworkScripts[j].Invalidate();
}
Assertf(m_BankSlots[i].BankSlot, "Failed to setup bank slot '%s'", slotName);
}
// Cellphone-related stuff
m_MobileRingType = AUD_CELLPHONE_NORMAL;
m_LastDisplayedTextBlock = -1;
for (s32 j=0; j<2; j++)
{
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
m_ScriptedSpeechSlot[i][j].ScriptedSpeechSound = NULL;
m_ScriptedSpeechSlot[i][j].AssociatedClip = NULL;
m_ScriptedSpeechSlot[i][j].AssociatedDic = NULL;
char slotName[32];
sprintf(slotName, "SCRIPT_SPEECH_%d", (i+(j*AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD)));
m_ScriptedSpeechSlot[i][j].WaveSlot = audWaveSlot::FindWaveSlot(slotName);
//Assert(m_ScriptedSpeechSlot[i][j].WaveSlot);
m_ScriptedSpeechSlot[i][j].LipsyncPredelay = -1;
m_ScriptedSpeechSlot[i][j].PauseTime = 0;
m_ScriptedSpeechSlot[i][j].VariationNumber = -1;
}
}
m_StreamSlot = NULL;
m_StreamSound = NULL;
m_StreamType = AUD_PLAYSTREAM_UNKNOWN;
m_StreamPosition.Set(ORIGIN);
#if GTA_REPLAY
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
m_StreamVariableNameHash[i] = g_NullSoundHash;
m_StreamVariableValue[i] = 0.0f;
}
#endif
m_StreamEnvironmentGroup = NULL;
m_ScriptStreamState = AUD_SCRIPT_STREAM_IDLE;
m_LoadedStreamRef.SetInvalid();
m_PreviewRingtoneSound = NULL;
m_PoliceScannerSceneHash = 0;
m_PoliceScannerSceneScriptIndex = -1;
m_PoliceScannerIsApplied = false;
m_MissionCompleteSound = NULL;
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_MissionCompleteTimeOut = 0;
m_MissionCompleteBankSlot = NULL;
m_ExtraConvLines = 0;
m_ConversationCodeLetter = 'A';
m_ConversationCodeNumber = 1;
safecpy(m_ConversationUserName, "UNKNOWN");
m_ConversationLocation.Set(ORIGIN);
if(PARAM_enableConvInterrupt.Get())
g_DisableInterrupts = false;
if(PARAM_disableConvInterrupt.Get())
g_DisableInterrupts = true;
if(PARAM_noAbortingFailedConv.Get())
g_DoNotAbortFailedConversations = true;
if(PARAM_noOverlapLipsyncAdjustment.Get())
g_AdjustOverlapForLipsyncPredelay = false;
#if __BANK
if(PARAM_trackMissingDialogue.Get())
g_TrackMissingDialogue = true;
if(PARAM_trackMissingDialogueMP.Get())
g_TrackMissingDialogueMP = true;
if(PARAM_displayScriptedSpeechLine.Get())
g_DisplayScriptedLineInfo = true;
#endif
sysMemZeroBytes<sizeof(m_FlagState)>(&m_FlagState);
m_AnimTriggersControllingConversation = false;
m_AnimTriggerReported = false;
m_IsConversationPlaceholder = false;
m_WaitingForFirstLineToPrepareToDuck = false;
m_BlockDeathJingle = false;
m_ScriptedSpeechTimerPaused = false;
m_ScriptOwnedEnvGroup.Reset();
m_AircraftWarningSpeechEnt.Init(NULL);
m_AircraftWarningSpeechEnt.SetAmbientVoiceName(g_AircraftWarningMale1Hash, true);
InitAircraftWarningInfo();
m_TimeOfLastCollisionCDI = 0;
m_AmbientSpeechAudioEntity.Init(NULL);
m_TimeEnteringSlowMo = 0;
m_TimeExitingSlowMo = 0;
m_InSlowMoForPause = false;
m_InSlowMoForSpecialAbility = false;
m_TimeOfLastChopCollisionReaction = 0;
#if GTA_REPLAY
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_SLOTS; i++)
{
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound = NULL;
sm_ReplayScriptedSpeechSlot[i].VariationNumber = -1;
sm_ReplayScriptedSpeechSlot[i].WaveSlot = NULL;
sm_ReplayScriptedSpeechSlot[i].VoiceNameHash = 0;
sm_ReplayScriptedSpeechSlot[i].ContextNameHash = 0;
sm_ReplayScriptedSpeechSlot[i].TimeStamp = 0;
sm_ReplayScriptedSpeechSlot[i].WaveSlotIndex = -1;
}
#endif
#if __BANK
if(PARAM_conversationspew.Get())
g_ConversationDebugSpew = true;
if(PARAM_scriptStreamSpew.Get())
g_ScriptedStreamSpew = true;
if(PARAM_scriptSoundsDebug.Get())
{
sm_DrawScriptTriggeredSounds = true;
sm_InWorldDrawScriptTriggeredSounds = true;
}
m_TimeLoadingScriptBank = 0;
m_FileHandle = INVALID_FILE_HANDLE;
m_DialogueFileHandle = INVALID_FILE_HANDLE;
m_HasInitializedDialogueFile = false;
safecpy(g_szLowInterruptContext, "");
safecpy(g_szMediumInterruptContext, "CRASH_GENERIC_INTERRUPT");
safecpy(g_szHighInterruptContext, "CRASH_GENERIC_INTERRUPT");
#endif
#if !__FINAL
m_TimeTriggeredSpeechPlayed = 0;
m_TimeSpeechTriggered = 0;
m_ShouldSetTimeTriggeredSpeechPlayedStat = false;
#endif
}
void audScriptAudioEntity::Shutdown()
{
m_SpeechAudioEntity.Shutdown();
m_AircraftWarningSpeechEnt.Shutdown();
m_AmbientSpeechAudioEntity.Shutdown();
StopStream();
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->Stop();
audEntity::Shutdown();
#if __BANK
if(m_DialogueFileHandle != INVALID_FILE_HANDLE)
{
CFileMgr::CloseFile(m_DialogueFileHandle);
m_DialogueFileHandle = INVALID_FILE_HANDLE;
}
#endif
}
void audScriptAudioEntity::InitAircraftWarningInfo()
{
safecpy(m_AircraftWarningInfo[AUD_AW_TARGETED_LOCK].Context, "TARGETED_LOCK");
safecpy(m_AircraftWarningInfo[AUD_AW_MISSLE_FIRED].Context, "MISSLE_FIRED");
safecpy(m_AircraftWarningInfo[AUD_AW_ACQUIRING_TARGET].Context, "ACQUIRING_TARGET");
safecpy(m_AircraftWarningInfo[AUD_AW_TARGET_ACQUIRED].Context, "TARGET_ACQUIRED");
safecpy(m_AircraftWarningInfo[AUD_AW_ALL_CLEAR].Context, "ALL_CLEAR");
safecpy(m_AircraftWarningInfo[AUD_AW_PLANE_WARNING_STALL].Context, "PLANE_WARNING_STALL");
safecpy(m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_LOW].Context, "ALTITUDE_WARNING_LOW");
safecpy(m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_HIGH].Context, "ALTITUDE_WARNING_HIGH");
safecpy(m_AircraftWarningInfo[AUD_AW_ENGINE_1_FIRE].Context, "ENGINE_1_FIRE");
safecpy(m_AircraftWarningInfo[AUD_AW_ENGINE_2_FIRE].Context, "ENGINE_2_FIRE");
safecpy(m_AircraftWarningInfo[AUD_AW_ENGINE_3_FIRE].Context, "ENGINE_3_FIRE");
safecpy(m_AircraftWarningInfo[AUD_AW_ENGINE_4_FIRE].Context, "ENGINE_4_FIRE");
safecpy(m_AircraftWarningInfo[AUD_AW_DAMAGED_SERIOUS].Context, "DAMAGED_SERIOUS");
safecpy(m_AircraftWarningInfo[AUD_AW_DAMAGED_CRITICAL].Context, "DAMAGED_CRITICAL");
safecpy(m_AircraftWarningInfo[AUD_AW_OVERSPEED].Context, "OVERSPEED");
safecpy(m_AircraftWarningInfo[AUD_AW_TERRAIN].Context, "TERRAIN");
safecpy(m_AircraftWarningInfo[AUD_AW_PULL_UP].Context, "PULL_UP");
safecpy(m_AircraftWarningInfo[AUD_AW_LOW_FUEL].Context, "LOW_FUEL");
m_ShouldUpdateAircraftWarning = false;
InitAircraftWarningRaveSettings();
}
void audScriptAudioEntity::InitAircraftWarningRaveSettings()
{
g_AircraftWarningSettings = audNorthAudioEngine::GetObject<AircraftWarningSettings>("AIRCRAFT_WARNING_SETTINGS");
if(g_AircraftWarningSettings)
{
m_AircraftWarningInfo[AUD_AW_TARGETED_LOCK].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->TargetedLock.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_TARGETED_LOCK].MinTimeBetweenPlay = g_AircraftWarningSettings->TargetedLock.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_MISSLE_FIRED].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->MissleFired.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_MISSLE_FIRED].MinTimeBetweenPlay = g_AircraftWarningSettings->MissleFired.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ACQUIRING_TARGET].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->AcquiringTarget.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ACQUIRING_TARGET].MinTimeBetweenPlay = g_AircraftWarningSettings->AcquiringTarget.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_TARGET_ACQUIRED].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->TargetAcquired.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_TARGET_ACQUIRED].MinTimeBetweenPlay = g_AircraftWarningSettings->TargetAcquired.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ALL_CLEAR].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->AllClear.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ALL_CLEAR].MinTimeBetweenPlay = g_AircraftWarningSettings->AllClear.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_PLANE_WARNING_STALL].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->PlaneWarningStall.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_PLANE_WARNING_STALL].MinTimeBetweenPlay = g_AircraftWarningSettings->PlaneWarningStall.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_LOW].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->AltitudeWarningLow.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_LOW].MinTimeBetweenPlay = g_AircraftWarningSettings->AltitudeWarningLow.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_HIGH].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->AltitudeWarningHigh.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ALTITUDE_WARNING_HIGH].MinTimeBetweenPlay = g_AircraftWarningSettings->AltitudeWarningHigh.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_1_FIRE].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Engine_1_Fire.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_1_FIRE].MinTimeBetweenPlay = g_AircraftWarningSettings->Engine_1_Fire.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_2_FIRE].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Engine_2_Fire.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_2_FIRE].MinTimeBetweenPlay = g_AircraftWarningSettings->Engine_2_Fire.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_3_FIRE].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Engine_3_Fire.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_3_FIRE].MinTimeBetweenPlay = g_AircraftWarningSettings->Engine_3_Fire.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_4_FIRE].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Engine_4_Fire.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_ENGINE_4_FIRE].MinTimeBetweenPlay = g_AircraftWarningSettings->Engine_4_Fire.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_DAMAGED_SERIOUS].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->DamagedSerious.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_DAMAGED_SERIOUS].MinTimeBetweenPlay = g_AircraftWarningSettings->DamagedSerious.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_DAMAGED_CRITICAL].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->DamagedCritical.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_DAMAGED_CRITICAL].MinTimeBetweenPlay = g_AircraftWarningSettings->DamagedCritical.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_OVERSPEED].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Overspeed.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_OVERSPEED].MinTimeBetweenPlay = g_AircraftWarningSettings->Overspeed.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_TERRAIN].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->Terrain.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_TERRAIN].MinTimeBetweenPlay = g_AircraftWarningSettings->Terrain.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_PULL_UP].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->PullUp.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_PULL_UP].MinTimeBetweenPlay = g_AircraftWarningSettings->PullUp.MinTimeBetweenPlay;
m_AircraftWarningInfo[AUD_AW_LOW_FUEL].MaxTimeBetweenTriggerAndPlay = g_AircraftWarningSettings->LowFuel.MaxTimeBetweenTriggerAndPlay;
m_AircraftWarningInfo[AUD_AW_LOW_FUEL].MinTimeBetweenPlay = g_AircraftWarningSettings->LowFuel.MinTimeBetweenPlay;
}
else
naWarningf("Failed to find aircraft warning rave settings. Using default values.");
}
u32 g_TimeBetweenCollisionReactionAndThresholdDowngrade = 15000;
void audScriptAudioEntity::UpdateConversation()
{
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
m_TimeElapsedSinceLastUpdate = currentTime - m_TimeLastFrame;
#if __BANK && !RSG_ORBIS
if(g_FakeConvAnimTrigger)
{
HandleConversaionAnimTrigger(NULL);
g_FakeConvAnimTrigger = false;
}
if(!g_TrackMissingDialogue && !g_TrackMissingDialogueMP && m_DialogueFileHandle != INVALID_FILE_HANDLE)
{
CFileMgr::CloseFile(m_DialogueFileHandle);
m_DialogueFileHandle = INVALID_FILE_HANDLE;
m_HasInitializedDialogueFile = false;
}
#endif
m_SpeechAudioEntity.Update();
m_AircraftWarningSpeechEnt.Update();
m_AmbientSpeechAudioEntity.Update();
// Update any conversations
SlowMoType slowMoType = audNorthAudioEngine::GetActiveSlowMoMode();
bool isSlowMo = audNorthAudioEngine::IsInSlowMo() || fwTimer::IsSystemPaused();
CPed *player = CGameWorld::FindLocalPlayer();
if (player && player->GetSpecialAbility())
{
if(m_InSlowMoForSpecialAbility && slowMoType == AUD_SLOWMO_GENERAL)
{
// Only set m_InSlowMoForSpecialAbility to false if we are no longer in special ability
m_InSlowMoForSpecialAbility = !player->GetSpecialAbility()->IsActive() || !audNorthAudioEngine::IsPlayerSpecialAbilityFadingOut();
isSlowMo = isSlowMo && !audNorthAudioEngine::IsPlayerSpecialAbilityFadingOut();
}
}
if(isSlowMo && !g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::AllowScriptedSpeechInSlowMo))
{
if(m_ColLevel != AUD_INTERRUPT_LEVEL_SLO_MO)
{
if(m_TimeEnteringSlowMo == 0)
m_TimeEnteringSlowMo = currentTime;
if(currentTime - m_TimeEnteringSlowMo > 250 || slowMoType == AUD_SLOWMO_PAUSEMENU)
{
RegisterSlowMoInterrupt(slowMoType);
}
}
m_TimeExitingSlowMo = currentTime;
}
else if(!isSlowMo)
{
m_TimeEnteringSlowMo = 0;
if(m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO && currentTime - m_TimeExitingSlowMo > (m_InSlowMoForPause ? 1000 : 500))
StopSlowMoInterrupt();
}
if(audNorthAudioEngine::IsAudioPaused() && !g_AudioEngine.GetSoundManager().IsPaused(4))
g_AudioEngine.GetSoundManager().Pause(4);
else if(!audNorthAudioEngine::IsAudioPaused() && g_AudioEngine.GetSoundManager().IsPaused(4) && !m_ScriptedSpeechTimerPaused)
{
m_TimeToRestartConversation = g_AudioEngine.GetTimeInMilliseconds() + 500;
g_AudioEngine.GetSoundManager().UnPause(4);
}
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if(playerVehicle)
{
bool notACar = playerVehicle->GetVehicleType() != VEHICLE_TYPE_CAR &&
playerVehicle->GetVehicleType() != VEHICLE_TYPE_BIKE &&
playerVehicle->GetVehicleType() != VEHICLE_TYPE_QUADBIKE &&
playerVehicle->GetVehicleType() != VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE;
if (playerVehicle->GetNumContactWheels() != 0 || notACar)
{
m_TimePlayersCarWasLastOnTheGround = currentTime;
}
else
{
m_TimePlayersCarWasLastInTheAir = currentTime;
}
}
// Update headset beep state based on the current script flags before processing the conversation
if(!IsFlagSet(audScriptAudioFlags::EnableHeadsetBeep))
{
m_HeadSetBeepOnLineIndex = -1;
m_TimeLastScriptedConvFinished = 0;
m_TimeLastBeepOffPlayed = 0;
}
if (m_ScriptedConversationInProgress)
{
if(m_ScriptSetNoDucking && m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
ProcessScriptedConversation(currentTime);
}
else if (m_TimeLastScriptedConvFinished + 200 < audNorthAudioEngine::GetCurrentTimeInMs())
{
if(m_HeadSetBeepOnLineIndex >= 0 && !IsFlagSet(audScriptAudioFlags::DisableHeadsetOffBeep))
{
audSoundInitParams beepInitParams;
beepInitParams.Pan = 0;
beepInitParams.Predelay = 300;
beepInitParams.Volume = m_HeadSetBeepOffCachedVolume;
CreateAndPlaySound(ATSTRINGHASH("DLC_HEISTS_HEADSET_CALL_END_BEEP", 0x6511F36C),&beepInitParams);
m_TimeLastBeepOffPlayed = audNorthAudioEngine::GetCurrentTimeInMs();
//Reset the cached volume for next time.
m_HeadSetBeepOffCachedVolume = 0.f;
}
m_HeadSetBeepOnLineIndex = -1;
m_TimeLastScriptedConvFinished = 0;
}
m_TimeLastFrame = currentTime;
}
void audScriptAudioEntity::PreUpdateService(u32 timeInMs)
{
#if GTA_REPLAY
UpdateReplayScriptedSpeechSlots();
#endif
// Update our script bank slots and sounds, to manage their states
UpdateScriptBankSlots(timeInMs);
UpdateScriptSounds(timeInMs);
UpdateMissionComplete();
UpdateCollisionInfo(timeInMs);
if(m_ShouldUpdateAircraftWarning)
UpdateAircraftWarning();
if(m_ScriptStreamState == AUD_SCRIPT_STREAM_PLAYING)
{
if(!m_StreamSound)
{
if(m_StreamSlot)
{
m_StreamSlot->Free();
m_StreamSlot = NULL;
m_StreamType = AUD_PLAYSTREAM_UNKNOWN;
}
m_ScriptStreamState = AUD_SCRIPT_STREAM_IDLE;
m_StreamEnvironmentGroup = NULL;
#if GTA_REPLAY
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
m_StreamVariableNameHash[i] = g_NullSoundHash;
m_StreamVariableValue[i] = 0.0f;
}
#endif
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] State: Playing but no sound was found, free stuff and set the state to idle");
}
#endif
}
else
{
if(m_StreamTrackerEntity)
{
m_StreamSound->SetRequestedPosition(m_StreamTrackerEntity->GetTransform().GetPosition());
// If it was played on a ped/vehicle, we'd share that envgroup and those positions are updated,
// otherwise we need to update the interior information and position ourselves
if(m_StreamEnvironmentGroup && !m_StreamTrackerEntity->GetIsTypePed() && !m_StreamTrackerEntity->GetIsTypeVehicle())
{
m_StreamEnvironmentGroup->SetSource(m_StreamTrackerEntity->GetTransform().GetPosition());
m_StreamEnvironmentGroup->SetInteriorInfoWithEntity(m_StreamTrackerEntity);
}
}
}
}
// Make sure the interference gets faded out if the radio does
if(m_MobileInterferenceSound)
{
m_MobileInterferenceSound->SetRequestedVolume(g_RadioAudioEntity.GetVolumeOffset());
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if(!playerVehicle || playerVehicle->GetVehicleAudioEntity()->GetAudioVehicleType() != AUD_VEHICLE_CAR || !g_RadioAudioEntity.IsVehicleRadioOn())
{
m_MobileInterferenceSound->StopAndForget();
}
}
}
void audScriptAudioEntity::PostUpdate()
{
for (int i=0; i<AUD_MAX_SCRIPTS; i++)
{
if(m_Scripts[i].ScriptThreadId && m_Scripts[i].m_ScriptisShuttingDown)
{
m_Scripts[i].m_ScriptShutDownDelay -= (fwTimer::GetTimeInMilliseconds()-fwTimer::GetPrevElapsedTimeInMilliseconds());
RemoveScript(m_Scripts[i].ScriptThreadId);
}
}
for (s32 i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
// clear out network triggered sounds where the calling script has terminated
if (m_ScriptSounds[i].NetworkId != audScriptSound::INVALID_NETWORK_ID)
{
if(m_ScriptSounds[i].ScriptId.IsValid() && !CTheScripts::GetScriptHandlerMgr().IsScriptActive(m_ScriptSounds[i].ScriptId))
{
naDebugf3("PostUpdate: Stopping Sound - SoundID:%d, NetworkID:%d", i, m_ScriptSounds[i].NetworkId);
// This sound belongs to this script, so stop it (if it still exists).
if (m_ScriptSounds[i].Sound)
{
m_ScriptSounds[i].Sound->StopAndForget();
}
ResetScriptSound(i);
}
}
if(m_ScriptSounds[i].ScriptIndex >= 0 || m_ScriptSounds[i].NetworkId != audScriptSound::INVALID_NETWORK_ID)
{
// This is currently used - do we need an explicit 'in use' flag rather than just this? (script slot reuse on orphanable sounds causing a problem?)
if(!m_ScriptSounds[i].ScriptHasReference)
{
// The script hasn't got an explicit reference to this sound, so if the sound in it's finished, we can remove the association and free the slot.
if (!m_ScriptSounds[i].Sound)
{
ResetScriptSound(i);
}
}
}
}
// clear out bank references where the calling script has terminated
for (s32 i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
for (s32 j=0; j<AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
if(m_BankSlots[i].NetworkScripts[j].IsValid() && !CTheScripts::GetScriptHandlerMgr().IsScriptActive(m_BankSlots[i].NetworkScripts[j]))
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 - looks like something leaked");
m_BankSlots[i].ReferenceCount--;
naDisplayf("PostUpdate: Dereferencing Bank - BankSlot:%d, NetSlot:%d, BankID:%d, Ref:%d", i, j, m_BankSlots[i].BankId, m_BankSlots[i].ReferenceCount);
BANK_ONLY(LogNetworkScriptBankRequest("PostUpdate: Dereferencing Bank - ",m_BankSlots[i].NetworkScripts[j].GetScriptName(),m_BankSlots[i].BankId,i));
m_BankSlots[i].NetworkScripts[j].Invalidate();
}
}
}
#if __ASSERT
for(const audScriptBankSlot &slot : m_BankSlots)
{
if(slot.BankSlot && slot.State == AUD_SCRIPT_BANK_LOADED && slot.BankId != AUD_INVALID_BANK_ID)
{
if(!audWaveSlot::VerifyLoadedBankWaveSlot(slot.BankSlot, slot.BankId))
{
const audWaveSlot* waveSlot = audWaveSlot::FindLoadedBankWaveSlot(slot.BankId);
if(slot.BankSlot != waveSlot)
{
// if we are going to hit the assert, wait for 10 milliseconds in case we have a timing issue
// and we haven't given enough time to the waveslot code to fill in the table.
sysIpcSleep(30);
if(!audWaveSlot::VerifyLoadedBankWaveSlot(slot.BankSlot, slot.BankId))
{
waveSlot = audWaveSlot::FindLoadedBankWaveSlot(slot.BankId);
naAssertf(slot.BankSlot == waveSlot,"Script loaded banks fell out of step with audWaveSlot::sm_LoadedBankTable, [Script bank: %s, slot: %s, id: %u, loaded id: %u] [LoadedBank: %s, slot: %s, id: %u]",
slot.BankSlot->GetLoadedBankName(),
slot.BankSlot->GetSlotName(),
slot.BankId,
slot.BankSlot->GetLoadedBankId(),
waveSlot ? waveSlot->GetCachedLoadedBankName(): "",
waveSlot ? waveSlot->GetSlotName() : "",
waveSlot ? waveSlot->GetLoadedBankId() : 0);
}
}
}
}
}
#endif
}
void audScriptAudioEntity::UpdateMissionComplete()
{
#if GTA_REPLAY
if(CReplayMgr::IsEditorActive())
{
StopMissionCompleteSound();
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
#endif
switch(m_MissionCompleteState)
{
case AUD_MISSIONCOMPLETE_PREPARING:
m_ShouldShowMissionCompleteUI = false;
if(!m_MissionCompleteSound)
{
audSoundInitParams initParams;
m_MissionCompleteBankSlot = AllocateScriptBank();
if(!m_MissionCompleteBankSlot)
{
m_MissionCompleteTimeOut += (fwTimer::GetTimeStepInMilliseconds());
// we failed to create the sound
if(m_MissionCompleteTimeOut > g_MaxTimeToAllocateSlot)
{
naErrorf("Reached time out when trying to allocate a script slot for the mission complete sound.");
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
}
break;
}
else
{
initParams.WaveSlot = m_MissionCompleteBankSlot;
initParams.TimerId = m_MissionCompleteTimerId; // defaults to unpausable; needs to be stopped by the frontend music
CreateSound_PersistentReference(m_MissionCompleteHash, &m_MissionCompleteSound, &initParams);
}
}
if(m_MissionCompleteSound)
{
audPrepareState prepareState = m_MissionCompleteSound->Prepare(m_MissionCompleteBankSlot, true);
if(prepareState == AUD_PREPARED)
{
m_MissionCompleteFadeTime = ~0U;
m_MissionCompleteHitTime = 0;
m_MissionCompleteTimeOut = 0;
const WrapperSound *wrapperSound = SOUNDFACTORY.DecompressMetadata<WrapperSound>(m_MissionCompleteHash);
if(wrapperSound)
{
const MultitrackSound *multitrack = SOUNDFACTORY.DecompressMetadata<MultitrackSound>(wrapperSound->SoundRef);
if(multitrack)
{
const SimpleSound *simple1 = SOUNDFACTORY.DecompressMetadata<SimpleSound>(multitrack->SoundRef[0].SoundId);
const SimpleSound *simple2 = SOUNDFACTORY.DecompressMetadata<SimpleSound>(multitrack->SoundRef[1].SoundId);
if(simple1 && simple2)
{
audWaveRef waveRef;
audWaveMarkerList markers;
u32 sampleRate = 0;
if(waveRef.Init(m_MissionCompleteBankSlot, simple1->WaveRef.WaveName))
{
waveRef.FindMarkerData(markers);
const audWaveFormat *format = waveRef.FindFormat();
if(format)
{
sampleRate = format->SampleRate;
}
}
if(markers.GetCount() == 0)
{
if(waveRef.Init(m_MissionCompleteBankSlot, simple2->WaveRef.WaveName))
{
const audWaveFormat *format = waveRef.FindFormat();
if(format)
{
sampleRate = format->SampleRate;
}
}
}
for(u32 i=0; i< markers.GetCount(); i++)
{
const u32 fadeHash = ATSTRINGHASH("FADE", 0x06e9c2bb6);
const u32 hitHash = ATSTRINGHASH("UIHIT", 0xD8846402);
if(markers[i].categoryNameHash == fadeHash)
{
m_MissionCompleteFadeTime = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
}
else if (markers[i].categoryNameHash == hitHash)
{
m_MissionCompleteHitTime = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
}
}
}
}
}
// we're now prepared; go into a waiting state until the delay is up
m_MissionCompleteState = AUD_MISSIONCOMPLETE_DELAY;
}
else if(prepareState == AUD_PREPARE_FAILED)
{
m_MissionCompleteSound->StopAndForget();
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
else if ( prepareState == AUD_PREPARING)
{
// we can also time out when preparing.
m_MissionCompleteTimeOut += (fwTimer::GetTimeStepInMilliseconds());
// we failed to create the sound
if(m_MissionCompleteTimeOut > g_MaxTimeToAllocateSlot)
{
naErrorf("Reached time out when preparing the mission complete sound.");
m_MissionCompleteSound->StopAndForget();
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
break;
}
}
else
{
// we failed to create the sound
naErrorf("Failed to create mission complete sound");
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
break;
case AUD_MISSIONCOMPLETE_DELAY:
m_ShouldShowMissionCompleteUI = false;
if(!IsFlagSet(audScriptAudioFlags::HoldMissionCompleteWhenPrepared))
{
if(IsFlagSet(audScriptAudioFlags::AvoidMissionCompleteDelay) || (fwTimer::GetTimeInMilliseconds() > m_MissionCompleteTriggerTime + g_MissionCompleteDelay))
{
if(m_MissionCompleteSound)
{
m_MissionCompleteSound->Play();
m_MissionCompleteState = AUD_MISSIONCOMPLETE_PLAYING;
}
else
{
naErrorf("No mission complete sound during AUD_MISSIONCOMPLETE_DELAY state");
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
}
}
else
{
m_MissionCompleteTriggerTime = fwTimer::GetTimeInMilliseconds();
}
break;
case AUD_MISSIONCOMPLETE_PLAYING:
if(!m_MissionCompleteSound)
{
m_MissionCompleteState = AUD_MISSIONCOMPLETE_IDLE;
m_ShouldShowMissionCompleteUI = true;
if(FreeUpScriptBank(m_MissionCompleteBankSlot))
{
m_MissionCompleteBankSlot = NULL;
}
}
else
{
const s32 playTimeMs = (s32)m_MissionCompleteSound->GetCurrentPlayTime(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(m_MissionCompleteTimerId));
if(playTimeMs >= (s32)m_MissionCompleteHitTime)
{
m_ShouldShowMissionCompleteUI = true;
}
}
break;
default:
break;
}
}
void audScriptAudioEntity::TriggerMissionComplete(const char * missionCompleteId, const s32 timerId /* = 3 */)
{
char missionCompleteName[64];
formatf(missionCompleteName, "SCRIPTED_MISSION_COMPLETE_%s", missionCompleteId);
if(m_MissionCompleteSound)
{
naErrorf("Called TriggerMissionComplete with %s while %s is already playing", missionCompleteName, m_MissionCompleteSound->GetName());
}
if(naVerifyf(m_MissionCompleteState == AUD_MISSIONCOMPLETE_IDLE, "In TriggerMissionComplete we should be in AUD_MISSIONCOMPLETE_DELAY (%d) but our state is %d", AUD_MISSIONCOMPLETE_IDLE, m_MissionCompleteState))
{
#if !__NO_OUTPUT
if(!g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataPtr(missionCompleteName))
{
audErrorf("Invalid mission complete identifier: %s (failed to find %s)", missionCompleteId, missionCompleteName);
}
#endif
m_MissionCompleteHash = atStringHash(missionCompleteName);
m_MissionCompleteState = AUD_MISSIONCOMPLETE_PREPARING;
m_MissionCompleteTriggerTime = fwTimer::GetTimeInMilliseconds();
m_MissionCompleteTimeOut = 0;
m_MissionCompleteTimerId = timerId;
m_ShouldShowMissionCompleteUI = false;
}
}
bool audScriptAudioEntity::IsPreparingMissionComplete() const
{
return (m_MissionCompleteSound && m_MissionCompleteSound->GetPlayState() == AUD_SOUND_PREPARING);
}
void audScriptAudioEntity::StopMissionCompleteSound()
{
if(m_MissionCompleteSound)
{
m_MissionCompleteSound->StopAndForget();
}
}
bool audScriptAudioEntity::IsPlayingMissionComplete() const
{
return (m_MissionCompleteSound && m_MissionCompleteSound->GetPlayState() == AUD_SOUND_PLAYING);
}
f32 audScriptAudioEntity::ComputeRadioVolumeForMissionComplete() const
{
if(m_MissionCompleteState == AUD_MISSIONCOMPLETE_PREPARING || m_MissionCompleteState == AUD_MISSIONCOMPLETE_DELAY)
{
const u32 timeInMs = fwTimer::GetTimeInMilliseconds();
const s32 timeTilTrigger = (s32)m_MissionCompleteTriggerTime + (s32)g_MissionCompleteDelay - (s32)timeInMs;
dev_s32 radioFadeOutTime = 500;
if(timeTilTrigger > radioFadeOutTime)
{
return 0.f;
}
else
{
const f32 interp = Max(0.f, (timeTilTrigger / (f32)radioFadeOutTime));
return audDriverUtil::ComputeDbVolumeFromLinear(interp);
}
}
else if(m_MissionCompleteSound && m_MissionCompleteState == AUD_MISSIONCOMPLETE_PLAYING)
{
const s32 playTimeMs = (s32)m_MissionCompleteSound->GetCurrentPlayTime(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(m_MissionCompleteTimerId));
if(playTimeMs < (s32)m_MissionCompleteFadeTime)
{
return -100.f;
}
else
{
const s32 duration = m_MissionCompleteSound->ComputeDurationMsIncludingStartOffsetAndPredelay(NULL);
if(playTimeMs >= duration)
{
return 0.f;
}
else
{
f32 fadeInterp = ((f32)(playTimeMs - (s32)m_MissionCompleteFadeTime) / (f32)(duration - (s32)m_MissionCompleteFadeTime));
return audDriverUtil::ComputeDbVolumeFromLinear(fadeInterp);
}
}
}
return 0.f;
}
void audScriptAudioEntity::PlayNPCPickup(const u32 pickupHash, const Vector3 &pickupPosition)
{
audSoundInitParams initParams;
initParams.Position = pickupPosition;
audSoundSet soundSet;
audMetadataRef pickupSoundRef = g_NullSoundRef;
soundSet.Init(ATSTRINGHASH("HUD_FRONTEND_WEAPONS_PICKUPS_NPC_SOUNDSET", 0xF35C567B));
pickupSoundRef = soundSet.Find(pickupHash);
if(pickupSoundRef == g_NullSoundRef)
{
soundSet.Init(ATSTRINGHASH("HUD_FRONTEND_VEHICLE_PICKUPS_NPC_SOUNDSET", 0xF5E3A26A));
pickupSoundRef = soundSet.Find(pickupHash);
if(pickupSoundRef == g_NullSoundRef)
{
soundSet.Init(ATSTRINGHASH("HUD_FRONTEND_STANDARD_PICKUPS_NPC_SOUNDSET", 0x1D46A6A2));
pickupSoundRef = soundSet.Find(pickupHash);
}
}
CreateDeferredSound(pickupSoundRef, NULL,&initParams);
}
void audScriptAudioEntity::SetDesiredPedWallaDensity(f32 desiredDensity, f32 desiredDensityApplyFactor, bool interior)
{
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
ASSERT_ONLY(bool success = ) g_AmbientAudioEntity.SetScriptDesiredPedDensity(desiredDensity, desiredDensityApplyFactor, m_Scripts[scriptIndex].ScriptThreadId, interior);
audAssertf(success, "Script %s attempting to set ped walla density when another thread is already doing so", CTheScripts::GetCurrentScriptName());
}
void audScriptAudioEntity::SetDesiredPedWallaSoundSet(const char* soundSetName, bool interior)
{
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
ASSERT_ONLY(bool success = ) g_AmbientAudioEntity.SetScriptWallaSoundSet(atStringHash(soundSetName), m_Scripts[scriptIndex].ScriptThreadId, interior);
audAssertf(success, "Script %s failed to set walla sound set to %s - is it a valid sound set name?", CTheScripts::GetCurrentScriptName(), soundSetName);
}
void audScriptAudioEntity::UpdateScriptBankSlots(u32 UNUSED_PARAM(timeInMs))
{
// No need to do anything - as bank requests are called repeatedly, we can sort everything from there.
#if GTA_REPLAY
if(CReplayMgr::IsRecording())
{
CReplayMgr::RecordFx<CPacketSndLoadScriptBank>(CPacketSndLoadScriptBank());
}
#endif
}
void audScriptAudioEntity::UpdateScriptSounds(u32 UNUSED_PARAM(timeInMs))
{
for (s32 i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
if (m_ScriptOwnedEnvGroup.IsSet(i) && m_ScriptSounds[i].ScriptIndex >= 0 && m_ScriptSounds[i].Sound && m_ScriptSounds[i].EnvironmentGroup)
{
if(m_ScriptSounds[i].Entity)
{
// removed the assert and added if check to avoid setting the environment group, which should update automatically for peds and vehicles
//naAssertf(!(m_ScriptSounds[i].Entity->GetIsTypePed() || m_ScriptSounds[i].Entity->GetIsTypeVehicle()),
// "We shouldn't own an environmentGroup if we're playing off an entity that already has an environmentGroup. Sound: %s", m_ScriptSounds[i].Sound->GetName());
if(!(m_ScriptSounds[i].Entity->GetIsTypePed() || m_ScriptSounds[i].Entity->GetIsTypeVehicle()))
{
m_ScriptSounds[i].EnvironmentGroup->SetSource(m_ScriptSounds[i].Entity->GetTransform().GetPosition());
m_ScriptSounds[i].EnvironmentGroup->SetInteriorInfoWithEntity(m_ScriptSounds[i].Entity);
}
}
else
{
m_ScriptSounds[i].EnvironmentGroup->SetSource(m_ScriptSounds[i].Sound->GetRequestedPosition());
}
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && m_ScriptSounds[i].Sound && m_ScriptSounds[i].ShouldRecord)
{
RecordUpdateScriptSlotSound(m_ScriptSounds[i].SoundSetHash, m_ScriptSounds[i].SoundNameHash, &m_ScriptSounds[i].InitParams, m_ScriptSounds[i].Entity, m_ScriptSounds[i].Sound);
}
if(CReplayMgr::ShouldRecord() && m_ScriptSounds[i].Sound && (m_ScriptSounds[i].VariableNameHash[0] != g_NullSoundHash || m_ScriptSounds[i].VariableNameHash[1] != g_NullSoundHash))
{
CReplayMgr::RecordPersistantFx<CPacketScriptSetVarOnSound>(
CPacketScriptSetVarOnSound(m_ScriptSounds[i].VariableNameHash, m_ScriptSounds[i].VariableValue),
CTrackedEventInfo<tTrackedSoundType>(m_ScriptSounds[i].Sound));
}
#endif
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && m_StreamSound && (m_StreamVariableNameHash[0] != g_NullSoundHash || m_StreamVariableNameHash[1] != g_NullSoundHash))
{
CReplayMgr::RecordPersistantFx<CPacketScriptSetVarOnSound>(
CPacketScriptSetVarOnSound(m_StreamVariableNameHash, m_StreamVariableValue),
CTrackedEventInfo<tTrackedSoundType>(m_StreamSound));
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && m_StreamSound && !m_DisableReplayScriptStreamRecording)
{
u32 currentOffset = m_StreamSound->GetCurrentPlayTime();
switch(m_StreamType)
{
case AUD_PLAYSTREAM_ENTITY:
// Stream tracker entity may have been destroyed since the stream started, in which case it isn't valid to record a packet referencing it.
if(m_StreamTrackerEntity)
{
CReplayMgr::ReplayRecordPlayStreamFromEntity(m_StreamTrackerEntity, m_StreamSound, sm_CurrentStreamName, currentOffset, sm_CurrentSetName);
}
break;
case AUD_PLAYSTREAM_POSITION:
CReplayMgr::ReplayRecordPlayStreamFromPosition(m_StreamPosition, m_StreamSound, sm_CurrentStreamName, currentOffset, sm_CurrentSetName);
break;
case AUD_PLAYSTREAM_FRONTEND:
CReplayMgr::ReplayRecordPlayStreamFrontend(m_StreamSound, sm_CurrentStreamName, currentOffset, sm_CurrentSetName);
break;
default:
break;
}
}
#endif
#endif
}
void audScriptAudioEntity::HandleScriptAudioEvent(u32 hash, CEntity* entity)
{
audScriptAudioEntity::sm_ScriptActionTable.InvokeActionHandler(hash, entity);
}
void audScriptAudioEntity::DefaultScriptActionHandler(u32 hash, void *context)
{
CEntity* entity = (CEntity*)context;
naEnvironmentGroup* environmentGroup = NULL;
audTracker* tracker = NULL;
if (entity)
{
switch (entity->GetType())
{
case ENTITY_TYPE_PED:
{
CPed* ped = (CPed*) entity;
environmentGroup = ped->GetPedAudioEntity()->GetEnvironmentGroup(true);
tracker = ((CPed*)entity)->GetPlaceableTracker();
break;
}
case ENTITY_TYPE_VEHICLE:
{
CVehicle* vehicle = (CVehicle*) entity;
environmentGroup = vehicle->GetVehicleAudioEntity()->GetEnvironmentGroup();
tracker = ((CVehicle*)entity)->GetPlaceableTracker();
break;
}
case ENTITY_TYPE_OBJECT:
{
environmentGroup = g_ScriptAudioEntity.CreateScriptEnvironmentGroup(entity, entity->GetTransform().GetPosition());
tracker = ((CObject*)entity)->GetPlaceableTracker();
break;
}
}
}
// As a default, play the sound, with occlusion group and tracker, where we have one.
audSoundInitParams initParams;
initParams.EnvironmentGroup = environmentGroup;
initParams.Tracker = tracker;
g_ScriptAudioEntity.CreateAndPlaySound(hash, &initParams);
}
void audScriptAudioEntity::UpdateCollisionInfo(u32 timeInMs)
{
if(m_TimeToLowerCollisionLevel != 0 && g_AudioEngine.GetTimeInMilliseconds() > m_TimeToLowerCollisionLevel)
{
switch(m_ColLevel)
{
case AUD_INTERRUPT_LEVEL_COLLISION_HIGH:
m_ColLevel = AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM;
m_TimeToLowerCollisionLevel += 10000;
break;
case AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM:
m_ColLevel = AUD_INTERRUPT_LEVEL_COLLISION_LOW;
m_TimeToLowerCollisionLevel += 10000;
break;
case AUD_INTERRUPT_LEVEL_COLLISION_LOW:
m_ColLevel = AUD_INTERRUPT_LEVEL_COLLISION_NONE;
m_TimeToLowerCollisionLevel = 0;
break;
default:
break;
}
}
//Only do this check a couple times a second, and only if we've hit a ped recently
if(m_TimeOfLastCarPedCollision != 0 && fwTimer::GetFrameCount() % 15 == 0 &&
timeInMs - m_TimeLastTriggeredPedCollisionScreams > g_TimeToWaitBetweenRunnedOverReactions
)
{
u8 numPedsHitRecently= 0;
for(int i=0; i<m_CarPedCollisions.GetMaxCount(); i++)
{
if(m_CarPedCollisions[i].TimeOfCollision > timeInMs - g_TimespanToHitPedForRunnedOverReaction)
numPedsHitRecently++;
}
if(numPedsHitRecently >= g_MinPedsHitForRunnedOverReaction)
{
audSpeechAudioEntity::TriggerCollisionScreams();
m_TimeLastTriggeredPedCollisionScreams = timeInMs;
}
}
}
void audScriptAudioEntity::ProcessScriptedConversation(u32 timeInMs)
{
#if !__FINAL
if(m_ShouldSetTimeTriggeredSpeechPlayedStat)
{
PF_SET(TimeTilTriggeredSpeechPlayed, m_TimeTriggeredSpeechPlayed - m_TimeSpeechTriggered);
m_ShouldSetTimeTriggeredSpeechPlayedStat = false;
}
#endif
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0 && !m_ScriptedConversation[m_PlayingScriptedConversationLine].DisplayedDebug)
{
m_ScriptedConversation[m_PlayingScriptedConversationLine].DisplayedDebug = true;
Displayf("[Conv Spew] ProcessScriptedConversation %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
if(m_ScriptedConversationOverlapTriggerTime == 0 && m_OverlapTriggerTimeAdjustedForPredelay && !m_InterruptTriggered)
{
m_ScriptedConversationOverlapTriggerTime = timeInMs;
}
m_DuckingForScriptedConversation = false; // only set later if we're saying something
//char convDebug[128] = "";
//formatf(convDebug, sizeof(convDebug), "Processing: line: %d; preload: %d; highestLoaded: %d", m_PlayingScriptedConversationLine, m_PreLoadingSpeechLine, m_HighestLoadedLine);
//grcDebugDraw::PrintToScreenCoors(convDebug,15,15);
if (g_AudioEngine.GetSoundManager().IsPaused(4))
{
if(m_ScriptedConversationOverlapTriggerTime != 0 && m_TimeTrueOverlapWasSet == 0)
m_ScriptedConversationOverlapTriggerTime += timeInMs - m_TimeLastFrame;
return;
}
if(m_ExternalStreamConversationSFXPlaying)
{
if(m_ScriptedConversationSfx)
return;
else
{
m_ExternalStreamConversationSFXPlaying = false;
}
}
// Block peds ambient anims, as appropriate
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i])
{
if (m_BlockAnimTime[i] > timeInMs)
{
m_Ped[i]->SetAmbientAnimsBlocked(true);
}
else if (m_BlockAnimTime[i] > 0)
{
m_Ped[i]->SetAmbientAnimsBlocked(true);
}
}
}
if(m_TimeToInterrupt != 0 && m_TimeToInterrupt < timeInMs)
{
naConvDebugf1("Pausing/Interrupting conversation - m_TimeToInterrupt (%u) < timeInMs (%u)", m_TimeToInterrupt, timeInMs);
PauseScriptedConversation(false, true);
InterruptConversation();
}
if(m_TimeToRestartConversation != 0 && m_TimeToRestartConversation < timeInMs && !ShouldWePauseScriptedConversation(timeInMs, true))
{
naConvDebugf1("Restarting Conversation - m_TimeToRestartConversation (%u) < timeInMs (%u)", m_TimeToRestartConversation, timeInMs);
//Now that we kill the conversation in ShouldWePause...if the car has been flipped or in the air too long, this may be false and we need
// to return immediately.
if(!m_ScriptedConversationInProgress)
{
return;
}
//adjust overlap time
if(m_ScriptedConversationOverlapTriggerTime != 0 && m_TimeTrueOverlapWasSet == 0)
{
m_ScriptedConversationOverlapTriggerTime += timeInMs - m_TimeLastFrame;
naConvDebugf1("Adding %u to make trigger time %u at point 1 %s %u", timeInMs - m_TimeLastFrame, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
if(m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO)
{
return;
}
RestartScriptedConversation(true);
}
if(m_InterruptTriggered && m_TimeToRestartConversation == 0)
{
audSpeechAudioEntity* speechAudioEntity = &m_SpeechAudioEntity;
if( m_Interrupter && m_Interrupter->GetSpeechAudioEntity())
{
speechAudioEntity = m_Interrupter->GetSpeechAudioEntity();
}
if(speechAudioEntity)
{
if(m_CheckInterruptPreloading)
{
bool playTimeOverZero = false;
if(!speechAudioEntity->IsPreloadedSpeechReady() && timeInMs - m_InterruptSpeechRequestTime > g_TimeToWaitForInterruptToPrepare)
{
naWarningf("Interrupt speech taking too long to load. Killing it and moving on with the conversation.");
speechAudioEntity->StopSpeech();
m_ScriptedConversationOverlapTriggerTime = timeInMs;
m_TimeToRestartConversation = timeInMs;
return;
}
else if(speechAudioEntity->IsPreloadedSpeechPlaying(&playTimeOverZero) && playTimeOverZero)
{
m_CheckInterruptPreloading = false;
}
}
if(!speechAudioEntity->IsAmbientSpeechPlaying() && !ShouldWePauseScriptedConversation(timeInMs, true))
{
m_ScriptedConversationOverlapTriggerTime = timeInMs;
m_TimeToRestartConversation = timeInMs;
return;
}
}
else
{
m_InterruptTriggered = false;
}
}
if(g_AdjustOverlapForLipsyncPredelay && m_PlayingScriptedConversationLine >= 0)
{
AdjustOverlapForLipsyncPredelay(timeInMs);
}
// don't play for a wee while after the last conversation - 250ms - this should stop any wave slot batchload asserts
// also allows a wee bit of time - 250ms - for ambient anim blocking.
if (timeInMs < m_NextTimeScriptedConversationCanStart)
{
return;
}
// Also wait until any of the chars have stopped speaking - this'll just be ambient speech - and add on a wee bit of time after that too.
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i] && m_PedIsInvolvedInCurrentConversation[i])
{
if (m_Ped[i]->GetSpeechAudioEntity() && m_Ped[i]->GetSpeechAudioEntity()->IsAmbientSpeechPlaying())
{
m_NextTimeScriptedConversationCanStart = Max(m_NextTimeScriptedConversationCanStart, timeInMs+250);
return;
}
if (m_Ped[i]->GetSpeechAudioEntity() && m_Ped[i]->GetSpeechAudioEntity()->IsPainPlaying())
{
if( m_PlayingScriptedConversationLine == -1 || (m_PlayingScriptedConversationLine >= 0 && strcmp(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, "heat_CMAC") != 0)) // there is a dude rag dolling in pain screaming that's fucking shit up
{
// if it's the next ped to speak, we don't want to say anything for a good few seconds
s16 nextLineToPlay = m_PlayingScriptedConversationLine+1;
if (nextLineToPlay >= 0 && nextLineToPlay < AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
if (m_ScriptedConversation[nextLineToPlay].Speaker == i)
{
m_NextTimeScriptedConversationCanStart = Max(m_NextTimeScriptedConversationCanStart, timeInMs+3000);
ShouldWePauseScriptedConversation(timeInMs); //Call this so that we pause if it's ragdoll pain, like getting thrown from a bike
return;
}
}
}
}
}
}
/*
u32 zeroBlocked = 0;
u32 oneBlocked = 0;
if (m_Ped[0] && m_BlockAnimTime[0] > timeInMs)
{
zeroBlocked = 1;
}
if (m_Ped[1] && m_BlockAnimTime[1] > timeInMs)
{
oneBlocked = 1;
}
char convDebug[128] = "";
formatf(convDebug, sizeof(convDebug), "%d:%d", zeroBlocked, oneBlocked);
grcDebugDraw::PrintToScreenCoors(convDebug,35,35);
*/
// see if we're in an upside down car
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if (playerVehicle && playerVehicle->IsUpsideDown()&& playerVehicle->GetVehicleType()!=VEHICLE_TYPE_BOAT)
{
m_CarUpsideDownTimer += fwTimer::GetTimeStepInMilliseconds();
}
else
{
m_CarUpsideDownTimer = 0;
}
// See if there's a good reason why we should abort
if (ShouldWeAbortScriptedConversation())
{
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] AbortScriptedConversation %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
AbortScriptedConversation(false BANK_ONLY(,"reason above."));
// think it's best to return here - we clean everything up in FinishConversation anyway, and don't want to do any preloading
return;
}
// See if there's a reason to pause the conversation - we do this instead of aborting most times now, and simply return, so we
// don't kick off another line
if (ShouldWePauseScriptedConversation(timeInMs))
{
return;
}
// If we're currently pausing the conversation, just do nothing at all. We don't update anything normally, so if we're still
// playing the line we called pause on, it'll merrily carry on playing.
if (m_PauseConversation)
{
// formatf(convDebug, sizeof(convDebug), "Paused");
// grcDebugDraw::PrintToScreenCoors(convDebug,20,20);
// We also need to cancel subtitles if no lines are playing.
// In the case where a conv is paused, but the line allowed to play out, this is the only place we're able to detect that.
if (m_PlayingScriptedConversationLine>=0) // Check we've actually started the conversation
{
// See if they're still talking
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker>=0)
{
if(m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
if (!m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->IsScriptedSpeechPlaying())
{
m_PauseWaitingOnCurrentLine = false;
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
if (m_LastDisplayedTextBlock>0 && (m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES-1 || !m_ScriptedConversation[m_PlayingScriptedConversationLine+1].DoNotRemoveSubtitle))
{
#if __BANK
sm_ScriptedLineDebugInfo[0] = '\0';
#endif
CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
m_LastDisplayedTextBlock = -1; // to avoid trying to cancel the same subtitle repeatedly.
// Don't think losing our memory of playing it will ever be a problem.
}
}
}
else if(!m_SpeechAudioEntity.IsScriptedSpeechPlaying())
{
m_PauseWaitingOnCurrentLine = false;
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
if (m_LastDisplayedTextBlock>0 && (m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES-1 || !m_ScriptedConversation[m_PlayingScriptedConversationLine+1].DoNotRemoveSubtitle))
{
#if __BANK
sm_ScriptedLineDebugInfo[0] = '\0';
#endif
CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
m_LastDisplayedTextBlock = -1; // to avoid trying to cancel the same subtitle repeatedly.
// Don't think losing our memory of playing it will ever be a problem.
}
}
}
}
else
{
if(!HandleCallToPreloading(timeInMs, true))
return;
}
return;
}
m_DuckingForScriptedConversation = true; // canceled later if we're having a wee break between lines - doing it here gives us a little
// If we're waiting on something to be preloaded, keep preloading until we're done
naConvDebugf1("Processing: line: %d; preload: %d; highestLoaded: %d highestLoading %d", m_PlayingScriptedConversationLine, m_PreLoadingSpeechLine, m_HighestLoadedLine, m_HighestLoadingLine);
if(!HandleCallToPreloading(timeInMs))
{
return;
}
if(m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
naConvAssertf(0,"Too many lines in this conversation. Aborting. First line: %s", m_ScriptedConversation[0].Context);
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] AbortScriptedConversation - Too many lines %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
AbortScriptedConversation(false BANK_ONLY(,"reason above."));
return;
}
// Currently playing line m_PlayingScriptedConversationLine
// Total quick fudge, as we need to do all this stuff properly and differently later anyway.
// For now, just see if we're currently playing our one sound - if not (it's been nulled) we see
// if there's more to come, and play the next one if there is.
// We now need to see if the previous line has finished - which is now being played via one of
// two different audio entities.
bool playingDialogue = false;
s32 playTime = -1;
s32 length = -1;
if (m_PlayingScriptedConversationLine>=0 && m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0) // Check we've actually started the conversation
{
// See if they're still talking
if (m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
playingDialogue = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->IsScriptedSpeechPlaying(&length, &playTime);
}
else
{
playingDialogue = m_SpeechAudioEntity.IsScriptedSpeechPlaying(&length, &playTime);
// Sanity check that if we're playing frontend speech, not from a ped, that it doesn't have speaker gestures
#if !__FINAL
if (playingDialogue)
{
u32 speakerGestureHash;
u32 speakerGestureDuration;
if(m_SpeechAudioEntity.GetLastMetadataHash(AUD_SPEECH_METADATA_CATEGORY_GESTURE_SPEAKER, speakerGestureHash, speakerGestureDuration))
{
// We've got a speaker gesture on frontend speech - this is likely a bug
naConvWarningf("Speaker-gestured frontend speech: line: %d; voice: %s; context: %s", m_PlayingScriptedConversationLine,
m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker], m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
naConvErrorf("Gestured speech playing frontend - please assign bug to level designer");
}
}
#endif
}
// if we're playing dialogue, print some debug info on how far through we are
if (playingDialogue)
{
// char convDebug[128] = "";
// formatf(convDebug, sizeof(convDebug), "length: %d; playtime: %d", length, playTime);
// grcDebugDraw::PrintToScreenCoors(convDebug,25,25);
// See if we're about to finish, and make sure the next speaker (if not us) has their ambient anims disabled,
// by setting their off time to a bit ahead. Current speaker's always a bit ahead, so the general setting code will sort it
naConvAssertf(m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker>=0 &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS,
"Speaker num is of bounds of m_BlockAnimTime");
m_BlockAnimTime[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] = timeInMs + g_StopAmbientAnimTime;
if (length>-1 && playTime>-1 && ((length-playTime) < g_StopAmbientAnimTime))
{
// Mark our next speaker to not do ambient anims for a while, and us too.
s32 nextLine = m_PlayingScriptedConversationLine+1;
if (nextLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES && m_ScriptedConversation[nextLine].Speaker >= 0)
{
m_BlockAnimTime[m_ScriptedConversation[nextLine].Speaker] = timeInMs + g_StopAmbientAnimTime;
}
}
if(playTime>0 && !m_HasShownSubtitles)
{
ShowSubtitles();
m_HasShownSubtitles = true;
}
if(g_AdjustTriggerTimeForZeroPlaytime && playTime == 0 && m_ScriptedConversationOverlapTriggerTime != 0 &&
(m_TimeTrueOverlapWasSet == 0 || g_AdjustTriggerForZeroEvenIfMarkedOverlap))
{
m_ScriptedConversationOverlapTriggerTime += timeInMs - m_TimeLastFrame;
naConvDebugf1("Adding %u to make trigger time %u at point 10 %s %u", timeInMs - m_TimeLastFrame, m_ScriptedConversationOverlapTriggerTime,
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, g_AudioEngine.GetTimeInMilliseconds());
}
}
// We're also playing dialogue if we're playing an sfx
if (m_ScriptedConversationSfx)
{
playingDialogue = true;
}
}
// if we've been requested to abort, and have finished playing, stop the conversation.
if (m_AbortScriptedConversationRequested && !playingDialogue)
{
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] m_AbortScriptedConversationRequested %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
FinishScriptedConversation();
return;
}
bool animTriggersControllingConv = m_AnimTriggersControllingConversation BANK_ONLY(|| g_AnimTriggersControllingConversation);
// We might also want to play the next line because we've got an overlap - explicitly check for that, and flag it.
bool timeToOverlap = false;
if (!animTriggersControllingConv && !m_AbortScriptedConversationRequested && m_OverlapTriggerTimeAdjustedForPredelay &&
!m_TriggeredOverlapSpeech && m_ScriptedConversationOverlapTriggerTime > 0 && m_ScriptedConversationOverlapTriggerTime<timeInMs)
{
timeToOverlap = true;
// and skip the next line, as it's the overlap cmd we've already processed
//m_PlayingScriptedConversationLine++;
}
/*
if (playingDialogue)
{
formatf(convDebug, sizeof(convDebug), "playingDialogue: %d", m_PlayingScriptedConversationLine);
grcDebugDraw::PrintToScreenCoors(convDebug,25,25);
}
if (m_ScriptedConversationNoAudioDelay>=timeInMs)
{
formatf(convDebug, sizeof(convDebug), "delay");
grcDebugDraw::PrintToScreenCoors(convDebug,30,30);
}
*/
bool playNextLine = false;
if(animTriggersControllingConv)
{
if(m_AnimTriggerReported)
{
Assertf(m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES, "Too many triggers in anim triggered conversation. Going to overrun the conversation array.");
s16 nextNonSFXLine = m_PlayingScriptedConversationLine+1;
for( ; nextNonSFXLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES; nextNonSFXLine++)
{
if(!IsContextAnSFXCommand(m_ScriptedConversation[nextNonSFXLine].Context))
break;
}
//make sure first line starts, and also allow overlapping if speakers are different
if(m_PlayingScriptedConversationLine < 0)
{
naConvDebugf1("Reacting to m_AnimTriggerReported 1. %u", g_AudioEngine.GetTimeInMilliseconds());
//need to skip first line pauses, but will be incrementing this value in a sece, so set to nextNonSFXLine minus 1
m_PlayingScriptedConversationLine = nextNonSFXLine - 1;
playNextLine = true;
m_AnimTriggerReported = false;
}
else if(nextNonSFXLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[nextNonSFXLine].Speaker != -1 &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker != m_ScriptedConversation[nextNonSFXLine].Speaker
)
{
naConvDebugf1("Reacting to m_AnimTriggerReported 2. %u", g_AudioEngine.GetTimeInMilliseconds());
playNextLine = true;
m_AnimTriggerReported = false;
}
else
{
s32 length = -1;
s32 playtime = -1;
CPed* ped = m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() : NULL;
Assertf(ped, "NULL ped conversation speaker in anim-triggered conversation.");
if (ped && ped->GetSpeechAudioEntity())
{
if(!ped->GetSpeechAudioEntity()->IsScriptedSpeechPlaying(&length, &playtime))
{
naConvDebugf1("Reacting to m_AnimTriggerReported 3. %u", g_AudioEngine.GetTimeInMilliseconds());
playNextLine = true;
m_AnimTriggerReported = false;
}
else
{
ped->GetSpeechAudioEntity()->OrphanScriptedSpeechEcho();
//make sure we ignore lipsync predelay on the next line, for stitching purposes
m_TreatNextLineToPlayAsUrgent = true;
naConvDebugf1("Play next line is true, but returning early 2. %u", g_AudioEngine.GetTimeInMilliseconds());
return;
}
}
}
}
//make sure if the last line triggered was the last line of the conversation, we make sure to finish the conversation once the line
// is over
else if((m_PlayingScriptedConversationLine+1 < AUD_MAX_SCRIPTED_CONVERSATION_LINES) &&
m_PlayingScriptedConversationLine >= 0 &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 &&
((m_ScriptedConversation[m_PlayingScriptedConversationLine + 1].Speaker) < 0)
)
{
CPed* ped = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker];
Assertf(ped, "NULL ped conversation speaker in anim-triggered conversation.");
if (ped && ped->GetSpeechAudioEntity() && !ped->GetSpeechAudioEntity()->IsScriptedSpeechPlaying())
{
naConvDebugf1("Reacting to m_AnimTriggerReported 4. %u", g_AudioEngine.GetTimeInMilliseconds());
playNextLine = true;
m_AnimTriggerReported = false;
}
}
}
else
{
if(m_PlayingScriptedConversationLine < 0)
{
if(IsContextAnSFXCommand(m_ScriptedConversation[0].Context))
{
u32 pauseTime = GetPauseTimeForConversationLine(&(m_ScriptedConversation[0].Context[4]));
if(pauseTime > 0)
{
m_PlayingScriptedConversationLine++;
m_OverlapTriggerTimeAdjustedForPredelay = true;
m_ScriptedConversationOverlapTriggerTime = timeInMs + pauseTime;
}
else
playNextLine = true;
}
else
playNextLine = true;
}
else
playNextLine = m_PlayingScriptedConversationLine < 0 || timeToOverlap;
}
if (playNextLine)
{
naConvDisplayf("Play next line is true. %u", g_AudioEngine.GetTimeInMilliseconds());
// We're not playing anything, so if we were, remove any listeners - I think this is the safest easiest place to do this.
// Because we sometimes cheat and advance on m_PlayingScriptedConversationLine, just go through them all all remove ANY.
// Note that we now might be playing stuff, as this could be an overlap line.
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i])
{
m_Ped[i]->SetSpeakerListenedTo(NULL);
m_Ped[i]->SetGlobalSpeakerListenedTo(NULL);
m_Ped[i]->SetSpeakerListenedToSecondary(NULL);
}
}
// The line we were just playing's finished, so see if we've more to play
m_PlayingScriptedConversationLine++;
// store a local flag that we've kicked off a sfx, so things don't break if there isn't the right sound to play
bool justTriggeredSFX = false;
if ((m_PlayingScriptedConversationLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES) &&
((m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker) >= 0))
{
bool isSFXContext = IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
if ( isSFXContext &&
GetOverlapOffsetTime(&m_ScriptedConversation[m_PlayingScriptedConversationLine].Context[4]) < 0 &&
GetPauseTimeForConversationLine(&m_ScriptedConversation[m_PlayingScriptedConversationLine].Context[4]) <= 0)
{
// It's not a pause command, so play a sound of the same name, from the ped if we have one
audSoundInitParams initParams;
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener < AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener >= 0)
{
// We have a listener id - do we have a real ped
CPed* listenerPed = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener];
if (listenerPed)
{
initParams.Tracker = listenerPed->GetPlaceableTracker();
}
}
if( (m_HeadSetBeepOnLineIndex == m_PlayingScriptedConversationLine) && !IsFlagSet(audScriptAudioFlags::DisableHeadsetOnBeep) )
{
CPed* ped = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker];
if (ped && ped->GetSpeechAudioEntity())
{
audSoundInitParams beepInitParams;
beepInitParams.Pan = 0;
Vector3 speakerPos = Vector3(0.f,0.f,0.f);
if(ped->GetSpeechAudioEntity()->GetPositionForSpeechSound(speakerPos,(audSpeechVolume)m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume,
m_NullPedSpeakerLocations[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_NullPedSpeakerEntities[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]))
{
f32 distanceToPed = Max((VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - speakerPos).Mag(),0.f);
beepInitParams.Volume = ped->GetSpeechAudioEntity()->ComputePositionedHeadsetVolume(ped, AUD_SPEECH_TYPE_SCRIPTED,distanceToPed,true);
m_HeadSetBeepOffCachedVolume = beepInitParams.Volume;
}
CreateAndPlaySound(ATSTRINGHASH("DLC_HEISTS_HEADSET_CALL_START_BEEP", 0xE3C11EED),&beepInitParams);
initParams.Predelay = 300;
}
}
if(!naConvVerifyf(!m_ScriptedConversationSfx, "Trying to stop scripted sfx but there isn't any"))
{
m_ScriptedConversationSfx->StopAndForget();
}
CreateAndPlaySound_Persistent(&m_ScriptedConversation[m_PlayingScriptedConversationLine].Context[4], &m_ScriptedConversationSfx, &initParams);
if(CReplayMgr::ShouldRecord())
{
char context[64];
strncpy(context, m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, 10);
context[6] = 0;
if(atStringHash(context) != ATSTRINGHASH("EXPOW_", 0x9DB49833))
{
RecordUpdateScriptSlotSound(g_NullSoundHash, atStringHash(&m_ScriptedConversation[m_PlayingScriptedConversationLine].Context[4]), &initParams, NULL, m_ScriptedConversationSfx);
}
}
justTriggeredSFX = true;
m_ScriptedConversationOverlapTriggerTime = 0; //We'll return early as long as the conversation SFX sound isn't null
m_OverlapTriggerTimeAdjustedForPredelay = false;
m_TriggeredOverlapSpeech = false;
// Now see if the next line is an overlap, and set our overlap time appropriately
//every line will now be treated as if it were overlapping, to compensate for lipsync warmup time.
s32 overlapTime = 0;
u32 nextLine = m_PlayingScriptedConversationLine+1;
if ((nextLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES) &&
((m_ScriptedConversation[nextLine].Speaker) >= 0))
{
if (IsContextAnSFXCommand(m_ScriptedConversation[nextLine].Context))
{
overlapTime = GetOverlapOffsetTime(&m_ScriptedConversation[nextLine].Context[4]);
}
}
if(overlapTime > 0)
{
m_ScriptedConversationOverlapTriggerTime = timeInMs + (u32)overlapTime;
m_TimeTrueOverlapWasSet = timeInMs;
naConvDebugf1("Setting trigger time to %u with overlap %u %s %u", m_ScriptedConversationOverlapTriggerTime, (u32)overlapTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
else
{
m_ScriptedConversationOverlapTriggerTime = 0;
}
}
else if(m_PlayingScriptedConversationLine > 0)
{
if(isSFXContext) //must be overlap or pause, which we've already accounted for, but still need to increment line
m_PlayingScriptedConversationLine++;
if(m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] AbortScriptedConversation - Too many lines %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
naConvAssertf(0,"Too many lines in this conversation. Aborting. First line: %s", m_ScriptedConversation[0].Context);
AbortScriptedConversation(false BANK_ONLY(,"reason above."));
return;
}
naConvAssertf(!IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context), "Two SFX contexts found in a row. This could mess up conversation logic.");
//since we now have to overlap all lines to compensate for lipsync predelay, things get a little crazy when the same speaker
// has two lines in a row. So if they do and they are still talking, do this
CPed* nextSpeakingPed = m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() : NULL;
if( (nextSpeakingPed && nextSpeakingPed->GetSpeechAudioEntity() && nextSpeakingPed->GetSpeechAudioEntity()->IsScriptedSpeechPlaying()) ||
(!nextSpeakingPed && m_SpeechAudioEntity.IsScriptedSpeechPlaying())
)
{
if(nextSpeakingPed && nextSpeakingPed->GetSpeechAudioEntity())
nextSpeakingPed->GetSpeechAudioEntity()->OrphanScriptedSpeechEcho();
m_PlayingScriptedConversationLine--;
naConvDebugf1("Decrementing playing conversation line 1. %u", g_AudioEngine.GetTimeInMilliseconds());
if(isSFXContext) //undo having accounted for the pause/overlap
{
m_PlayingScriptedConversationLine--;
naConvDebugf1("Decrementing playing conversation line 2. %u", g_AudioEngine.GetTimeInMilliseconds());
}
//make sure we ignore lipsync predelay on the next line, for stitching purposes
m_TreatNextLineToPlayAsUrgent = true;
#if __BANK
if(g_ConversationDebugSpew)
{
char voiceName[64] = "\0";
char waveName[64] = "\0";
if(nextSpeakingPed && nextSpeakingPed->GetSpeechAudioEntity())
{
nextSpeakingPed->GetSpeechAudioEntity()->GetCurrentlyPlayingScriptedVoiceName(voiceName);
nextSpeakingPed->GetSpeechAudioEntity()->GetCurrentlyPlayingWaveName(waveName);
}
else if(!nextSpeakingPed && m_SpeechAudioEntity.IsScriptedSpeechPlaying())
{
m_SpeechAudioEntity.GetCurrentlyPlayingScriptedVoiceName(voiceName);
m_SpeechAudioEntity.GetCurrentlyPlayingWaveName(waveName);
}
naDisplayf("[Conv Spew] Setting m_TreatNextLineToPlayAsUrgent == true. m_PlayingScriptedConversationLine: %d CurrentlyPlayingVoice: %s CurrentlyPlayingWave: %s",
m_PlayingScriptedConversationLine, voiceName, waveName);
}
#endif
m_ScriptedConversationOverlapTriggerTime += m_TimeElapsedSinceLastUpdate;
naConvDebugf1("Play next line is true, but returning early 2. %u", g_AudioEngine.GetTimeInMilliseconds());
return;
}
}
}
if(!animTriggersControllingConv)
{
// if ( pauseTime>0 && !animTriggersControllingConv)
// {
// if (m_LastDisplayedTextBlock>0)
// {
// CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
// }
// // and stop ducking the radio for a wee bit
// m_DuckingForScriptedConversation = false;
//#if __BANK
// if(g_ConversationDebugSpew)
// naDisplayf("[Conv Spew] Play next line is true, but returning early 3. %u", g_AudioEngine.GetTimeInMilliseconds());
//#endif
// return;
// }
// else
if (justTriggeredSFX)
{
naConvDebugf1("Play next line is true, but returning early 4. %u", g_AudioEngine.GetTimeInMilliseconds());
return;
}
}
if(justTriggeredSFX && animTriggersControllingConv)
m_PlayingScriptedConversationLine++;
if ((m_PlayingScriptedConversationLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES) &&
((m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker) >= 0))
{
// We've more to play
if (g_AudioEngine.IsAudioEnabled() && (strcmp(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, "")!=0))
{
// See if we have more available already loaded - if not, kick off a bunch of loads, and wait until that's done
if (m_HighestLoadedLine<m_PlayingScriptedConversationLine)
{
// PreLoadScriptedSpeech(m_PlayingScriptedConversationLine);
m_PlayingScriptedConversationLine--; // because next time in, we'll ++ it again. Horrid hack.
naConvDebugf1("Decrementing playing conversation line 3. %u", g_AudioEngine.GetTimeInMilliseconds());
// Note that we're actually waiting on a preload
m_WaitingForPreload = true;
naConvAssertf((m_PlayingScriptedConversationLine+1)>=0 && (m_PlayingScriptedConversationLine+1)<AUD_MAX_SCRIPTED_CONVERSATION_LINES, "Out of bounds");
if (m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Speaker >= 0 &&
m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Speaker < AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS)
{
m_BlockAnimTime[m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Speaker] = timeInMs + g_StopAmbientAnimTime;
}
m_ScriptedConversationOverlapTriggerTime += m_TimeElapsedSinceLastUpdate;
// formatf(convDebug, sizeof(convDebug), "Preload");
// grcDebugDraw::PrintToScreenCoors(convDebug,20,20);
naConvDebugf1("Play next line is true, but returning early 5. %u", g_AudioEngine.GetTimeInMilliseconds());
return; // think this is fine - we'll just come in again next frame, and keep checking until it's loaded
}
// Work out who our listener is, if we have one
CPed* listenerPed = NULL;
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener < AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener >= 0)
{
// We have a listener id - do we have a real ped
listenerPed = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Listener];
}
// See what variation we're playing, a use that for subtitling
// u32 context=0, fullContext=0;
// Find the appropriate speech audio entity
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 && m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker])
{
CPed* ped = m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker];
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex > -1 &&
ped->GetSpeechAudioEntity())
{
s32 predelay = 0;
if( (m_HeadSetBeepOnLineIndex == m_PlayingScriptedConversationLine) && !IsFlagSet(audScriptAudioFlags::DisableHeadsetOnBeep) )
{
audSoundInitParams beepInitParams;
beepInitParams.Pan = 0;
Vector3 speakerPos = Vector3(0.f,0.f,0.f);
if(ped->GetSpeechAudioEntity()->GetPositionForSpeechSound(speakerPos,(audSpeechVolume)m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume,
m_NullPedSpeakerLocations[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_NullPedSpeakerEntities[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]))
{
f32 distanceToPed = Max((VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - speakerPos).Mag(),0.f);
beepInitParams.Volume = ped->GetSpeechAudioEntity()->ComputePositionedHeadsetVolume(ped, AUD_SPEECH_TYPE_SCRIPTED,distanceToPed,true);
m_HeadSetBeepOffCachedVolume = beepInitParams.Volume;
}
CreateAndPlaySound(ATSTRINGHASH("DLC_HEISTS_HEADSET_CALL_START_BEEP", 0xE3C11EED),&beepInitParams);
predelay = 300;
}
m_ScriptedConversationVariation = m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].VariationNumber;
ped->GetSpeechAudioEntity()->PlayScriptedSpeech(m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context,
m_ScriptedConversationVariation,
m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].WaveSlot,
(audSpeechVolume)m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume,
false,
(m_ShouldRestartConvAtMarker && m_LineCheckedForRestartOffset == m_PlayingScriptedConversationLine) ? m_SpeechRestartOffset : 0,
m_NullPedSpeakerLocations[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_NullPedSpeakerEntities[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
0,
m_ScriptedConversation[m_PlayingScriptedConversationLine].Audibility,
m_ScriptedConversation[m_PlayingScriptedConversationLine].Headset,
m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].AssociatedClip,
m_ScriptedConversation[m_PlayingScriptedConversationLine].isPadSpeakerRoute,
predelay
);
m_OverlapTriggerTimeAdjustedForPredelay = m_ShouldRestartConvAtMarker && m_LineCheckedForRestartOffset == m_PlayingScriptedConversationLine &&
m_PlayingScriptedConversationLine + 1 < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
GetOverlapOffsetTime(&(m_ScriptedConversation[m_PlayingScriptedConversationLine + 1].Context[4])) >= 0;
m_MarkerPredelayApplied = m_OverlapTriggerTimeAdjustedForPredelay ? m_SpeechRestartOffset : 0;
m_SpeechRestartOffset = 0;
m_LineCheckedForRestartOffset = -1;
m_LastSpeakingPed = ped;
m_TriggeredOverlapSpeech = true;
m_TreatNextLineToPlayAsUrgent = false;
m_FirstLineHasStarted = true;
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] 1.Playing conversation %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
naConvDisplayf("Playing line conversation %s %u", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, g_AudioEngine.GetTimeInMilliseconds());
#if !__FINAL
m_TimeSpeechTriggered = timeInMs;
#endif
if( ( m_CloneConversation ) && ( NetworkInterface::IsGameInProgress() ) )
{
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] Clone conversation %s %d", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context,
m_ScriptedConversationVariation);
}
#endif
// See what variation number we just played, and send that across the network too.
CPedConversationLineEvent::Trigger( ped,
ped->GetSpeechAudioEntity()->GetCurrentlyPlayingVoiceNameHash(),
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context,
m_ScriptedConversationVariation,
true );
}
if (ped->IsLocalPlayer() && !m_ScriptedConversation[m_PlayingScriptedConversationLine].Headset &&
!g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::DoNotBlipScriptedSpeech))
{
CMiniMap::CreateSonarBlipAndReportStealthNoise(ped, ped->GetTransform().GetPosition(), CMiniMap::sm_Tunables.Sonar.fSoundRange_ObjectCollision, HUD_COLOUR_BLUEDARK);
}
}
else
{
m_ScriptedConversationVariation = 0;
}
// If we have a listener, make them listen to this ped
if (listenerPed)
{
listenerPed->SetSpeakerListenedTo((CEntity*)ped);
}
// Mark the other peds in the same vehicle as the secondary listener.
if(ped->GetIsInVehicle())
{
CVehicle* pSpeakerVehicle = ped->GetMyVehicle();
for(int i = 0; i < pSpeakerVehicle->GetSeatManager()->GetMaxSeats(); i++)
{
CPed* pPedInVehicle = pSpeakerVehicle->GetSeatManager()->GetPedInSeat(i);
if(pPedInVehicle && pPedInVehicle != ped && pPedInVehicle!= listenerPed)
{
pPedInVehicle->SetSpeakerListenedToSecondary((CEntity*)ped);
}
}
}
}
else
{
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex > -1)
{
s32 predelay = 0;
if( (m_HeadSetBeepOnLineIndex == m_PlayingScriptedConversationLine) && !IsFlagSet(audScriptAudioFlags::DisableHeadsetOnBeep) )
{
audSoundInitParams beepInitParams;
beepInitParams.Pan = 0;
Vector3 speakerPos = Vector3(0.f,0.f,0.f);
if(m_SpeechAudioEntity.GetPositionForSpeechSound(speakerPos,(audSpeechVolume)m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume,
m_NullPedSpeakerLocations[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_NullPedSpeakerEntities[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]))
{
f32 distanceToPed = Max((VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - speakerPos).Mag(),0.f);
beepInitParams.Volume = m_SpeechAudioEntity.ComputePositionedHeadsetVolume(nullptr, AUD_SPEECH_TYPE_SCRIPTED,distanceToPed,true);
m_HeadSetBeepOffCachedVolume = beepInitParams.Volume;
}
CreateAndPlaySound(ATSTRINGHASH("DLC_HEISTS_HEADSET_CALL_START_BEEP", 0xE3C11EED),&beepInitParams);
predelay = 300;
}
m_ScriptedConversationVariation = m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].VariationNumber;
m_SpeechAudioEntity.PlayScriptedSpeech(m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context,
m_ScriptedConversationVariation,
m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].WaveSlot,
(audSpeechVolume)m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume,
false,
(m_ShouldRestartConvAtMarker && m_LineCheckedForRestartOffset == m_PlayingScriptedConversationLine) ? m_SpeechRestartOffset : 0,
m_NullPedSpeakerLocations[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_NullPedSpeakerEntities[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
0,
m_ScriptedConversation[m_PlayingScriptedConversationLine].Audibility,
m_ScriptedConversation[m_PlayingScriptedConversationLine].Headset,
m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].AssociatedClip,
m_ScriptedConversation[m_PlayingScriptedConversationLine].isPadSpeakerRoute,
predelay
);
m_OverlapTriggerTimeAdjustedForPredelay = m_ShouldRestartConvAtMarker && m_LineCheckedForRestartOffset == m_PlayingScriptedConversationLine &&
m_PlayingScriptedConversationLine + 1 < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
GetOverlapOffsetTime(&(m_ScriptedConversation[m_PlayingScriptedConversationLine + 1].Context[4])) >= 0;
m_MarkerPredelayApplied = m_OverlapTriggerTimeAdjustedForPredelay ? m_SpeechRestartOffset : 0;
m_SpeechRestartOffset = 0;
m_LineCheckedForRestartOffset = -1;
m_LastSpeakingPed = NULL;
m_TriggeredOverlapSpeech = true;
m_TreatNextLineToPlayAsUrgent = false;
m_FirstLineHasStarted = true;
#if __BANK
if(NetworkInterface::IsGameInProgress() && m_PlayingScriptedConversationLine >= 0)
{
Displayf("[Conv Spew] 2.Playing conversation %s", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context);
}
#endif
naConvDisplayf("Playing line conversation %s %u", m_ScriptedConversation[m_PlayingScriptedConversationLine].Context, g_AudioEngine.GetTimeInMilliseconds());
#if !__FINAL
m_TimeSpeechTriggered = timeInMs;
#endif
}
else
{
m_ScriptedConversationVariation = 0;
}
// If we have a listener, make them listen to the global speech entity
if (listenerPed)
{
// We only have one ped, so let's make that one listen to the global speech audio entity
listenerPed->SetGlobalSpeakerListenedTo(&m_SpeechAudioEntity);
}
}
// Now see if the next line is an overlap, and set our overlap time appropriately
//every line will now be treated as if it were overlapping, to compensate for lipsync warmup time.
s32 overlapTime = 0;
u32 nextLine = m_PlayingScriptedConversationLine+1;
if ((nextLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES) &&
((m_ScriptedConversation[nextLine].Speaker) >= 0))
{
if (IsContextAnSFXCommand(m_ScriptedConversation[nextLine].Context))
{
overlapTime = GetOverlapOffsetTime(&m_ScriptedConversation[nextLine].Context[4]);
}
}
if(overlapTime > 0)
{
m_TimeTrueOverlapWasSet = timeInMs;
m_ScriptedConversationOverlapTriggerTime = timeInMs + (u32)overlapTime;
}
else
{
m_ScriptedConversationOverlapTriggerTime = 0;
m_TimeTrueOverlapWasSet = 0;
}
m_OverlapTriggerTimeAdjustedForPredelay = false;
m_TriggeredOverlapSpeech = false;
}
else
{
m_ScriptedConversationNoAudioDelay = (timeInMs + 5000);
}
m_HasShownSubtitles = false;
if (m_ScriptedConversationVariation==0)
{
// We didn't find any valid line - this is probably just because the speech isn't yet recorded/generated,
// so just use the first one, and act as if we're running with -noaudio
m_ScriptedConversationVariation = 1;
m_ScriptedConversationOverlapTriggerTime = (timeInMs + 3000);
m_ScriptedConversationNoAudioDelay = (timeInMs + 3000);
m_OverlapTriggerTimeAdjustedForPredelay = true;
ShowSubtitles(true);
m_HasShownSubtitles = true;
}
}
else
{
// We don't have any more speech to play
// Call our tidy up fn
// Need this ugly check now that we're compensating for lipsync predelay. Otherwise, some conversations end early
for(int i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; ++i)
{
if(m_Ped[i] && m_Ped[i]->GetSpeechAudioEntity() && m_Ped[i]->GetSpeechAudioEntity()->IsScriptedSpeechPlaying())
{
m_PlayingScriptedConversationLine--; //to avoid array overrun
naConvDebugf1("Decrementing playing conversation line 4. %u", g_AudioEngine.GetTimeInMilliseconds());
return;
}
}
FinishScriptedConversation();
}
}
}
s32 audScriptAudioEntity::GetLipsyncPredelay(tScriptedSpeechSlots& scriptedSlot
#if !__FINAL
, int slotNumber
#endif
)
{
if(!scriptedSlot.ScriptedSpeechSound || !scriptedSlot.WaveSlot)
{
naAssertf(0, "Null sound or waveslot passed to audScriptAudioEntity::GetLipsyncPredelay");
return 0;
}
if(scriptedSlot.AssociatedDic)
{
MEM_USE_USERDATA(MEMUSERDATA_VISEME); // THIS IS A DIRTY HACK to get around the non-stadnard Viseme allocation scheme
scriptedSlot.AssociatedDic->Release();
scriptedSlot.AssociatedDic = NULL;
}
#if !__FINAL
bool failedToInitWaveRef = false;
#endif
scriptedSlot.AssociatedClip = NULL;
scriptedSlot.AssociatedDic = audSpeechAudioEntity::GetVisemeData((void **)&(scriptedSlot.AssociatedClip), scriptedSlot.ScriptedSpeechSound
#if !__FINAL
, failedToInitWaveRef
#endif
);
u32 predelay = 0;
if(scriptedSlot.AssociatedClip)
{
if(scriptedSlot.AssociatedClip->GetProperties())
{
const crProperty* property = scriptedSlot.AssociatedClip->GetProperties()->FindProperty("TimeOfPreDelay");
if(property)
{
const crPropertyAttribute* attribute = property->GetAttribute("TimeOfPreDelay");
if(attribute && attribute->GetType() == crPropertyAttribute::kTypeFloat)
{
const crPropertyAttributeFloat* attributeFloat = static_cast<const crPropertyAttributeFloat*>(attribute);
predelay = (u32)(attributeFloat->GetFloat()*1000);
}
}
}
}
#if !__FINAL
if(failedToInitWaveRef)
{
naConvErrorf("Failed to init waveRef when trying to get lipsync predelay for scripted speech. Please include next few lines of output and send bug to audio.");
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
if (m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound)
{
if(i == slotNumber)
{
naConvErrorf("!!!NEXT SLOT IS THE ONE THAT FAILED!!!");
}
naConvErrorf("ScriptedSpeechSlotInfo Waveslot: %s, LoadedWave: %u, context: %s, voicename: %s",
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].WaveSlot->GetSlotName(),
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].WaveSlot->GetLoadedWaveNameHash(),
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].Context,
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].VoiceName
);
naConvErrorf("SoundInfo Waveslot: %p, WaveNameHash: %u BankId: %u",
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound->GetWaveSlot()->GetSlotName(),
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound->GetWaveNameHash(),
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound->GetBankId());
}
}
}
#endif
//// HACK: Delete the viseme clip dictionary
//if (pDictionary)
//{
// MEM_USE_USERDATA(MEMUSERDATA_VISEME); // THIS IS A DIRTY HACK to get around the non-stadnard Viseme allocation scheme
// pDictionary->Release();
//}
return predelay;
}
void audScriptAudioEntity::AdjustOverlapForLipsyncPredelay(u32 timeInMs)
{
if(!m_OverlapTriggerTimeAdjustedForPredelay && m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
if(m_ScriptedConversationOverlapTriggerTime == 0 || m_TimeTrueOverlapWasSet != 0)
{
s32 length = -1;
if(!IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context))
{
s32 playTime = -1;
if (m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
if(!m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->IsScriptedSpeechPlaying(&length, &playTime))
{
m_ScriptedConversationOverlapTriggerTime = timeInMs;
m_OverlapTriggerTimeAdjustedForPredelay = true;
}
}
else
{
m_SpeechAudioEntity.IsScriptedSpeechPlaying(&length, &playTime);
}
if(length <= 0)
{
return;
}
//adjust true overlaps for time it takes preceeding line to prepare
if(m_TimeTrueOverlapWasSet != 0)
{
m_ScriptedConversationOverlapTriggerTime += (timeInMs - m_TimeTrueOverlapWasSet);
m_TimeTrueOverlapWasSet = 0;
naConvDebugf1("Setting trigger time to %u, adjust true overlap for loading time %s %u", m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
else
{
m_ScriptedConversationOverlapTriggerTime = timeInMs + length;
naConvDebugf1("Setting trigger time to %u with length %u %s %u", m_ScriptedConversationOverlapTriggerTime, length,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
}
else if(m_ScriptedConversationSfx)
{
if(m_TimeTrueOverlapWasSet != 0)
{
m_ScriptedConversationOverlapTriggerTime += (timeInMs - m_TimeTrueOverlapWasSet);
m_TimeTrueOverlapWasSet = 0;
naConvDebugf1("Setting trigger time to %u, adjust true overlap for loading time %s %u", m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
else
{
bool isLooping = false;
length = m_ScriptedConversationSfx->ComputeDurationMsIncludingStartOffsetAndPredelay(&isLooping);
if(isLooping)
{
//This catches external stream sounds. In this case, we have no choice but to disallow interrupts and just wait until the sound
// has played
m_ScriptedConversationOverlapTriggerTime = timeInMs;
m_ExternalStreamConversationSFXPlaying = true;
m_OverlapTriggerTimeAdjustedForPredelay = true;
return;
}
if(length <= 0)
{
return;
}
m_ScriptedConversationOverlapTriggerTime = timeInMs + length;
m_OverlapTriggerTimeAdjustedForPredelay = true;
return;
}
}
}
s32 nextLineSpeechSlotIndex = -1;
if(m_PlayingScriptedConversationLine+1 < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Speaker >= 0 &&
!m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Speaker] &&
!IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Context))
{
naConvDebugf1("Setting conversation predelay adjust true without setting time 1. %u", g_AudioEngine.GetTimeInMilliseconds());
m_OverlapTriggerTimeAdjustedForPredelay = true;
}
else if(m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex >= 0)
{
s32 thisLineSpeechSlotIndex = m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex;
nextLineSpeechSlotIndex = thisLineSpeechSlotIndex + 1;
u32 thisLinePreloadSet = m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet;
u32 nextLinePreloadSet = thisLinePreloadSet;
if(nextLineSpeechSlotIndex >= AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD)
{
nextLinePreloadSet = (m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet + 1) % 2;
nextLineSpeechSlotIndex = nextLineSpeechSlotIndex % AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD;
naConvAssertf(nextLineSpeechSlotIndex == 0, "nextLineSpeechSlotIndex is not zero when adjusting overlap for lipsync predelay and overshooting PreloadSet.");
}
if(m_ScriptedSpeechSlot[nextLineSpeechSlotIndex][nextLinePreloadSet].LipsyncPredelay != -1)
{
u32 nextNonSFXLine = m_PlayingScriptedConversationLine;
nextNonSFXLine++;
while(nextNonSFXLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
if(
strcmp(m_ScriptedConversation[nextNonSFXLine].Context, "") == 0 ||
!IsContextAnSFXCommand(m_ScriptedConversation[nextNonSFXLine].Context)
)
{
break;
}
nextNonSFXLine++;
}
if(nextNonSFXLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES ||
(m_ScriptedConversation[nextNonSFXLine].Speaker >= 0 &&
m_Ped[m_ScriptedConversation[nextNonSFXLine].Speaker] &&
!m_Ped[m_ScriptedConversation[nextNonSFXLine].Speaker]->m_nDEflags.bFrozen))
{
m_ScriptedConversationOverlapTriggerTime -= m_ScriptedSpeechSlot[nextLineSpeechSlotIndex][nextLinePreloadSet].LipsyncPredelay;
}
#if !__NO_OUTPUT
else
{
naConvDebugf1("Frozen ped found for speaker. Not really applying predelay at point 3. %s %u",
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
#endif
m_ScriptedConversationOverlapTriggerTime += m_ScriptedSpeechSlot[thisLineSpeechSlotIndex][thisLinePreloadSet].PauseTime;
//make sure to move this forward if we are restarting the preceding line at an interrupt marker.
m_ScriptedConversationOverlapTriggerTime -= m_MarkerPredelayApplied;
if(nextNonSFXLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[nextNonSFXLine].Speaker != m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker)
{
m_ScriptedConversationOverlapTriggerTime += g_AdditionalScriptedSpeechPause;
}
naConvDebugf1("Adding %u and subtracting %u to make trigger time %u at point 3 %s %u", m_ScriptedSpeechSlot[thisLineSpeechSlotIndex][thisLinePreloadSet].PauseTime,
m_ScriptedSpeechSlot[nextLineSpeechSlotIndex][nextLinePreloadSet].LipsyncPredelay,
m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
m_OverlapTriggerTimeAdjustedForPredelay = true;
}
else if(m_PlayingScriptedConversationLine+1 >= AUD_MAX_SCRIPTED_CONVERSATION_LINES ||
strcmp(m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Context, "") == 0 ||
m_ScriptedConversation[m_PlayingScriptedConversationLine+1].FailedToLoad ||
IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine+1].Context)
)
{
naConvDebugf1("Setting conversation predelay adjust true without setting time 2. %u", g_AudioEngine.GetTimeInMilliseconds());
m_OverlapTriggerTimeAdjustedForPredelay = true;
}
}
}
}
void audScriptAudioEntity::ShowSubtitles(bool forceShow)
{
if(m_PlayingScriptedConversationLine < 0 || m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES ||
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context[0] == '\0' ||
IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context))
{
return;
}
// Display subtitles
char *pString;
const char *subtitle = m_ScriptedConversation[m_PlayingScriptedConversationLine].Subtitle;
char subtitleWithVariation[40];
formatf(subtitleWithVariation, "%s_%02d", subtitle, m_ScriptedConversationVariation);
if (TheText.DoesTextLabelExist(subtitleWithVariation))
{
pString = TheText.Get(subtitleWithVariation);
}
else
{
// subtitle with variation doesn't exist, so try it without variation
if (!TheText.DoesTextLabelExist(subtitle))
{
naConvWarningf("Subtitle doesn't exist - with or without _XX. (%s) Tried to find variation %s", subtitle, subtitleWithVariation);
}
pString = TheText.Get(subtitle);
}
if(pString && (forceShow || m_ScriptedConversation[m_PlayingScriptedConversationLine].IsPlaceholder) )
{
if(naConvVerifyf(pString[0] == '~' && pString[2] == '~', "Trying to force an invalid subtitle %s.", pString))
{
pString = pString + 3; //skip the leading ~z~
}
}
s32 TextBlock = TheText.GetBlockContainingLastReturnedString();
if (m_DisplaySubtitles)
{
uiDebugf3("Adding To Brief: Dialogue Line --- %s --- Spoken By --- %s --- During Mission -- %s", pString, m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker], CPauseMenu::GetCurrentMissionLabel());
if (CPauseMenu::GetMenuPreference(PREF_SUBTITLES) || (CMessages::IsThisTextSwitchedOffByFrontendSubtitleSetting(pString) == false) )
{ // Graeme - This is something that Imran asked for (Bug 568480). It seems like a bad idea to me. It means that the game will behave differently depending on whether subtitles are off or on.
// If subtitles are switched off in the pause menu then the idea is that scripts can be written so that god text is shown while the conversation plays.
// I need to avoid calling AddMessage so that the god text doesn't get cleared by a conversation subtitle that would never be shown anyway.
if(m_SizeOfSubtitleSubStringArray == 0)
{
CMessages::AddMessage( pString, TextBlock, forceShow ? 3000 : 10000, true,
m_AddSubtitlesToBriefing && !m_ScriptedConversation[m_PlayingScriptedConversationLine].DoNotRemoveSubtitle, PREVIOUS_BRIEF_NO_OVERRIDE, NULL, 0, NULL, 0, false,
atStringHash(m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]), atStringHash(CPauseMenu::GetCurrentMissionLabel()) );
}
else
{
CMessages::AddMessage( pString, TextBlock, forceShow ? 3000 : 10000, true,
m_AddSubtitlesToBriefing && !m_ScriptedConversation[m_PlayingScriptedConversationLine].DoNotRemoveSubtitle, PREVIOUS_BRIEF_NO_OVERRIDE, NULL, 0, m_ArrayOfSubtitleSubStrings, m_SizeOfSubtitleSubStringArray, false,
atStringHash(m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]), atStringHash(CPauseMenu::GetCurrentMissionLabel()) );
}
}
else
{
CSubtitleMessage PreviousBrief;
PreviousBrief.Set(pString, TextBlock,
NULL, 0,
NULL, 0,
false, true, atStringHash(m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]), atStringHash(CPauseMenu::GetCurrentMissionLabel()));
CMessages::AddToPreviousBriefArray(PreviousBrief, PREVIOUS_BRIEF_FORCE_DIALOGUE);
}
m_LastDisplayedTextBlock = TextBlock;
}
#if __BANK
if(g_DisplayScriptedLineInfo)
{
formatf(sm_ScriptedLineDebugInfo, "voicename:%s context:%s variation:%u", m_VoiceName[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker],
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context,
m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex >= 0 ?
m_ScriptedSpeechSlot[m_ScriptedConversation[m_PlayingScriptedConversationLine].SpeechSlotIndex][m_ScriptedConversation[m_PlayingScriptedConversationLine].PreloadSet].VariationNumber : 0);
}
#endif
}
bool audScriptAudioEntity::IsPreloadedScriptedSpeechReady()
{
if(m_PreLoadingSpeechLine > -1 || m_HighestLoadedLine > -1)
{
audPrepareState preloadPrepareState = PreLoadScriptedSpeech(m_PreLoadingSpeechLine);
if (preloadPrepareState!=AUD_PREPARED)
{
if(preloadPrepareState == AUD_PREPARE_FAILED && !g_DoNotAbortFailedConversations)
{
#if __BANK
char txt[64];
formatf(txt,"Scripted speech lines failed to prepare. Aborting conversation.");
#endif
naConvAssertf(0,"Scripted speech lines failed to prepare. Aborting conversation.");
AbortScriptedConversation(false BANK_ONLY(,txt));
}
return false;
}
return true;
}
return false;
}
bool audScriptAudioEntity::HandleCallToPreloading(u32 timeInMs, bool isPaused)
{
if (m_PreLoadingSpeechLine>-1)
{
audPrepareState preloadPrepareState = PreLoadScriptedSpeech(m_PreLoadingSpeechLine);
if (preloadPrepareState!=AUD_PREPARED)
{
// formatf(convDebug, sizeof(convDebug), "Waiting for preload");
// grcDebugDraw::PrintToScreenCoors(convDebug,20,20);
if(preloadPrepareState == AUD_PREPARE_FAILED && !g_DoNotAbortFailedConversations)
{
#if __BANK
char txt[128];
formatf(txt,"Scripted speech lines failed to prepare. Aborting conversation.");
#endif
naConvAssertf(0,"Scripted speech lines failed to prepare. Aborting conversation.");
AbortScriptedConversation(false BANK_ONLY(,txt));
return false;
}
// Exit out if we're waiting on that line being preloaded - we're probably not, unless this is the first line of the conv
if (m_WaitingForPreload)
{
return false;
}
}
if(m_WaitingForFirstLineToPrepareToDuck && !isPaused)
{
m_WaitingForFirstLineToPrepareToDuck = false;
if(m_ConversationDuckingScene && !m_ScriptSetNoDucking)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 1.f);
}
}
else
{
if(m_ConversationIsSfxOnly && m_HighestLoadedLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES - 1 && !m_ScriptedConversationSfx)
{
AbortScriptedConversation(false BANK_ONLY(,"Aborting from HandleCallToPreloading"));
return false;
}
// See if we should be preloading - are we playing something not far behind the highest loaded thing
if (m_HighestLoadedLine < (m_PlayingScriptedConversationLine+3))
{
// Special-case the first line, so we can quickly block ambient anims
if (m_PlayingScriptedConversationLine==-1 && !isPaused)
{
if(g_DuckForConvAfterPreload)
m_WaitingForFirstLineToPrepareToDuck = true;
if( m_ScriptedConversation[0].Speaker >= 0 &&
m_ScriptedConversation[0].Speaker < AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS &&
m_Ped[m_ScriptedConversation[0].Speaker])
{
m_BlockAnimTime[m_ScriptedConversation[0].Speaker] = timeInMs + g_StopAmbientAnimTime;
m_Ped[m_ScriptedConversation[0].Speaker]->SetAmbientAnimsBlocked(true);
}
}
audPrepareState preloadPrepareState = PreLoadScriptedSpeech(m_HighestLoadedLine+1);
if(preloadPrepareState == AUD_PREPARE_FAILED)
return false;
}
}
return true;
}
void audScriptAudioEntity::FinishScriptedConversation()
{
naConvDebugf1("FinishScriptedConversation()");
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
m_ScriptedConversationInProgress = false;
m_DuckingForScriptedConversation = false;
m_IsScriptedConversationZit = false;
m_PlayingScriptedConversationLine = -1;
m_ScriptedConversationNoAudioDelay = 0;
m_AbortScriptedConversationRequested = false;
m_HighestLoadedLine = -1;
m_PreLoadingSpeechLine = -1;
m_ScriptedConversationOverlapTriggerTime = 0;
m_OverlapTriggerTimeAdjustedForPredelay = false;
m_TriggeredOverlapSpeech = false;
m_NextTimeScriptedConversationCanStart = g_AudioEngine.GetTimeInMilliseconds()+250;
m_AnimTriggersControllingConversation = false;
m_IsConversationPlaceholder = false;
m_InterruptTriggered = false;
m_ScriptSetNoDucking = false;
m_IsScriptedConversationAMobileCall = false;
for (s32 j=0; j<2; j++)
{
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
if (m_ScriptedSpeechSlot[i][j].ScriptedSpeechSound)
{
m_ScriptedSpeechSlot[i][j].ScriptedSpeechSound->StopAndForget();
// This won't actually stop any playing speech, as that's just the preload sound - need to explicitly tell all peds to stop talking
}
if(m_ScriptedSpeechSlot[i][j].AssociatedDic)
{
MEM_USE_USERDATA(MEMUSERDATA_VISEME); // THIS IS A DIRTY HACK to get around the non-stadnard Viseme allocation scheme
m_ScriptedSpeechSlot[i][j].AssociatedDic->Release();
m_ScriptedSpeechSlot[i][j].AssociatedDic = NULL;
}
m_ScriptedSpeechSlot[i][j].AssociatedClip = NULL;
m_ScriptedSpeechSlot[i][j].LipsyncPredelay = -1;
m_ScriptedSpeechSlot[i][j].PauseTime = 0;
m_ScriptedSpeechSlot[i][j].VariationNumber = -1;
#if !__FINAL
m_ScriptedSpeechSlot[i][j].VoiceName[0] = '\0';
m_ScriptedSpeechSlot[i][j].Context[0] = '\0';
#endif
}
}
SetAllPedsHavingAConversation(false);
if (m_LastDisplayedTextBlock>0)
{
#if __BANK
sm_ScriptedLineDebugInfo[0] = '\0';
#endif
CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
}
// Clear up our references
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i])
{
if (m_BlockAnimTime[i] > 0)
{
m_Ped[i]->SetAmbientAnimsBlocked(false);
}
m_Ped[i]->RemoveSpeakerListenedTo();
m_Ped[i]->RemoveSpeakerListenedToSecondary();
if(m_Ped[i]->GetIsInVehicle())
{
CVehicle* pSpeakerVehicle = m_Ped[i]->GetMyVehicle();
for(int i = 0; i < pSpeakerVehicle->GetSeatManager()->GetMaxSeats(); i++)
{
CPed* pPedInVehicle = pSpeakerVehicle->GetSeatManager()->GetPedInSeat(i);
if(pPedInVehicle && pPedInVehicle != m_Ped[i])
{
pPedInVehicle->RemoveSpeakerListenedToSecondary();
}
}
}
if (m_Ped[i]->GetSpeechAudioEntity())
m_Ped[i]->GetSpeechAudioEntity()->StopPlayingScriptedSpeech();
// Remove the old registered reference
// NOTE: this function can be called from script directly or from the audio update thread, we only want to defer tidyref
// when this is running on the audio thread
m_Ped[i] = NULL;
}
m_PedIsInvolvedInCurrentConversation[i] = false;
}
// Stop playing anything from our frontend speech audio entity
m_SpeechAudioEntity.StopPlayingScriptedSpeech();
// Check we don't have a sound playing
if (m_ScriptedConversationSfx)
{
m_ScriptedConversationSfx->StopAndForget();
}
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_TimeLastScriptedConvFinished = audNorthAudioEngine::GetCurrentTimeInMs();
}
audPrepareState audScriptAudioEntity::PreLoadScriptedSpeech(s16 lineToStartLoadingFrom)
{
if(!g_AudioEngine.IsAudioEnabled())
{
m_HighestLoadedLine = AUD_MAX_SCRIPTED_CONVERSATION_LINES;
return AUD_PREPARED;
}
if (lineToStartLoadingFrom<0)
{
naConvErrorf("Invalid line to start loading from");
return AUD_PREPARED;
}
// If we aren't already preloading, clear out all the old line-slot-pairs, set up the new ones, and kick off the loads.
// We'll only have valid sounds in the slots if we're preloading.
if (m_PreLoadingSpeechLine>-1)
{
// See how many lines there are remaining, and load that many
audSpeechSoundInfo speechSoundInfo[AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD];
u32 numSpeechSounds = 0;
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
if (m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound)
{
speechSoundInfo[numSpeechSounds].sound = m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound;
speechSoundInfo[numSpeechSounds].slot = m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].WaveSlot;
if(speechSoundInfo[numSpeechSounds].slot && speechSoundInfo[numSpeechSounds].slot->GetReferenceCount() == (u32)-1)
{
#if __BANK
char txt[64];
formatf(txt,"Found scripted speech slot with -1 ref count. Aborting conversations.");
#endif
naConvAssertf(0,"Found scripted speech slot with -1 ref count. Aborting conversations.");
AbortScriptedConversation(false BANK_ONLY(,txt));
return AUD_PREPARE_FAILED;
}
numSpeechSounds++;
}
}
audPrepareState prepareState = audSpeechSound::BatchPrepare(speechSoundInfo, numSpeechSounds);
if (prepareState == AUD_PREPARED)
{
#if !__FINAL
u32 currentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
for (s16 conversationLine = lineToStartLoadingFrom; conversationLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES && m_ScriptedConversation[conversationLine].TimePrepareFirstRequested != 0; conversationLine++)
{
if(currentTime > m_ScriptedConversation[conversationLine].TimePrepareFirstRequested &&
currentTime - m_ScriptedConversation[conversationLine].TimePrepareFirstRequested> 1000)
{
naConvWarningf("Scripted speech line %s taking too long to load. Took %u ms", m_ScriptedConversation[conversationLine].Context,
currentTime - m_ScriptedConversation[conversationLine].TimePrepareFirstRequested);
}
}
#endif
// We've loaded everything - clear our preparing sounds, and flag that we're done
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
if (m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound)
{
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].LipsyncPredelay = GetLipsyncPredelay(m_ScriptedSpeechSlot[i][m_CurrentPreloadSet]
#if !__FINAL
, i
#endif
);
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound->StopAndForget();
}
m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound = NULL;
}
#if !__FINAL
if(lineToStartLoadingFrom >= 0 && lineToStartLoadingFrom < AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
PF_SET(TimeToPreloadScriptedSpeech, currentTime - m_ScriptedConversation[lineToStartLoadingFrom].TimePrepareFirstRequested);
}
#endif
m_PreLoadingSpeechLine = -1;
m_CurrentPreloadSet = (m_CurrentPreloadSet+1) % 2;
m_WaitingForPreload = false;
m_HighestLoadedLine = m_HighestLoadingLine;
return AUD_PREPARED;
}
// else, do nothing, we're still loading
return prepareState;
}
else
{
m_PreLoadingSpeechLine = lineToStartLoadingFrom;
// We've not started preparing yet. Clear out our line-slot pairings, set up new ones, create speech sounds, and prepare
// Assert if we've any sounds - they should only exist whilst preparing
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
naConvAssertf(!m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound, "Still have sounds in the preload slots when they should be cleared out");
}
u32 scriptedSpeechSlot = 0;
s16 conversationLine = lineToStartLoadingFrom;
while (scriptedSpeechSlot<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD && conversationLine<AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
#if !__FINAL
//little extra time for the first line to load
m_ScriptedConversation[conversationLine].TimePrepareFirstRequested = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
#endif
if (m_ScriptedConversation[conversationLine].Speaker >= 0 && !IsContextAnSFXCommand(m_ScriptedConversation[conversationLine].Context))
{
m_ScriptedConversation[conversationLine].SpeechSlotIndex = scriptedSpeechSlot;
m_ScriptedConversation[conversationLine].PreloadSet = m_CurrentPreloadSet;
char* voiceName = m_VoiceName[m_ScriptedConversation[conversationLine].Speaker];
char* context = m_ScriptedConversation[conversationLine].Context;
s32 variationNum = m_SpeechAudioEntity.GetRandomVariation(atStringHash(voiceName), context, NULL, NULL);
#if !__FINAL
char placeholderVoiceName[128] = "";
if(variationNum <= 0)
{
if(!CFileMgr::ShouldForceReleaseAudioPacklist())
{
if (voiceName
&& g_EnablePlaceholderDialogue
&& (!m_IsConversationPlaceholder || PARAM_useRobotSpeechForAllLines.Get()))
{
formatf(placeholderVoiceName, sizeof(placeholderVoiceName), "%s_PLACEHOLDER", voiceName);
variationNum = m_SpeechAudioEntity.GetRandomVariation(atStringHash(placeholderVoiceName), context, NULL, NULL);
if (variationNum <=0)
{
Displayf("Missing scripted speech: voice: %s; context: %s", voiceName, context);
}
voiceName = placeholderVoiceName;
m_ScriptedConversation[conversationLine].IsPlaceholder = true;
}
}
#if __BANK
if(!m_HasInitializedDialogueFile)
{
static bool dialogueFileFailedToOpen = false;
if(g_TrackMissingDialogue && m_DialogueFileHandle == INVALID_FILE_HANDLE && !dialogueFileFailedToOpen &&
(!NetworkInterface::IsGameInProgress() || g_TrackMissingDialogueMP))
{
const char* pPathName = NULL;
PARAM_trackMissingDialogue.Get(pPathName);
if(strlen(pPathName) == 0)
{
#if RSG_ORBIS
formatf(m_DialogueFileName, "common:/audMissingDialogue_%u_%u.csv", audSpeechSound::GetMetadataManager().GetAssetChangelist(), time(0));
#else
formatf(m_DialogueFileName, "common:/audMissingDialogue_%u_%ui.csv", audSpeechSound::GetMetadataManager().GetAssetChangelist(), std::time(0));
#endif
}
else
{
#if RSG_ORBIS
formatf(m_DialogueFileName, "%s/audMissingDialogue_%u_%u.csv",pPathName, audSpeechSound::GetMetadataManager().GetAssetChangelist(), time(0));
#else
formatf(m_DialogueFileName, "%s/audMissingDialogue_%u_%u.csv",pPathName, audSpeechSound::GetMetadataManager().GetAssetChangelist(), std::time(0));
#endif
}
m_DialogueFileHandle = CFileMgr::OpenFileForWriting(m_DialogueFileName);
if(m_DialogueFileHandle != INVALID_FILE_HANDLE)
{
const char *header = "VoiceName,FileName,SpeakerID\r\n";
CFileMgr::Write(m_DialogueFileHandle, header, istrlen(header));
CFileMgr::CloseFile(m_DialogueFileHandle);
m_DialogueFileHandle = CFileMgr::OpenFileForAppending(m_DialogueFileName);
}
else
{
dialogueFileFailedToOpen = true;
}
}
m_HasInitializedDialogueFile = true;
}
if(m_DialogueFileHandle != INVALID_FILE_HANDLE && (!NetworkInterface::IsGameInProgress() || g_TrackMissingDialogueMP))
{
char buf[256];
formatf(buf,256,"%s,%s,%u\r\n",m_VoiceName[m_ScriptedConversation[conversationLine].Speaker],
m_ScriptedConversation[conversationLine].Context,m_ScriptedConversation[conversationLine].Speaker);
CFileMgr::Write(m_DialogueFileHandle, buf, istrlen(buf));
CFileMgr::CloseFile(m_DialogueFileHandle);
m_DialogueFileHandle = CFileMgr::OpenFileForAppending(m_DialogueFileName);
}
#endif
}
#endif
//naCErrorf(variationNum>=0, "Missing scripted speech: voice: %s; context: %s", voiceName, context);
m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].VariationNumber = variationNum;
audSoundInitParams initParams;
initParams.WaveSlot = m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].WaveSlot;
CreateSound_PersistentReference("PRELOAD_SPEECH_SOUND", (audSound**)&m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].ScriptedSpeechSound, &initParams);
if(m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].ScriptedSpeechSound)
{
bool success = m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].ScriptedSpeechSound->InitSpeech(voiceName, context, variationNum);
if (success)
{
m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].PauseTime = 0;
if(conversationLine + 1 < AUD_MAX_SCRIPTED_CONVERSATION_LINES && IsContextAnSFXCommand(m_ScriptedConversation[conversationLine+1].Context))
m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].PauseTime = GetPauseTimeForConversationLine(&m_ScriptedConversation[conversationLine+1].Context[4]);
naConvDisplayf("Preloading line %s %u", context, g_AudioEngine.GetTimeInMilliseconds());
#if !__FINAL && __BANK
safecpy(m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].VoiceName, voiceName);
safecpy(m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].Context, context);
#endif//!__FINAL && __BANK
scriptedSpeechSlot++;
}
else
{
//naAssertf(0, "Incorrect voicename found for conversation line %s. Check that the speaker number in d* is correct.", context);
naDisplayf("InitSpeech Failed - Incorrect voicename found for conversation line %s _%d %s. Check that the speaker number in d* is correct.", context, variationNum, voiceName);
// ditch this sound, as it failed to find a variation
m_ScriptedSpeechSlot[scriptedSpeechSlot][m_CurrentPreloadSet].ScriptedSpeechSound->StopAndForget();
m_ScriptedConversation[conversationLine].SpeechSlotIndex = -1;
m_ScriptedConversation[conversationLine].FailedToLoad = true;
}
}
else
{
#if __BANK
char txt[64];
formatf(txt,"No scripted speech sound in slot: %d preloadset: %d. Bug Default Audio Code for any associated dialogue problems.", scriptedSpeechSlot, m_CurrentPreloadSet);
naConvWarningf("%s", txt);
#endif
AbortScriptedConversation(false BANK_ONLY(,txt));
return AUD_PREPARE_FAILED;
}
}
m_HighestLoadingLine = conversationLine;
conversationLine++;
}
// See how many lines there are remaining, and load that many
audSpeechSoundInfo speechSoundInfo[AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD];
u32 numSpeechSounds = 0;
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
if (m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound)
{
speechSoundInfo[numSpeechSounds].sound = m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].ScriptedSpeechSound;
speechSoundInfo[numSpeechSounds].slot = m_ScriptedSpeechSlot[i][m_CurrentPreloadSet].WaveSlot;
numSpeechSounds++;
}
}
audSpeechSound::BatchPrepare(speechSoundInfo, numSpeechSounds);
if(numSpeechSounds == 0)
m_HighestLoadedLine = m_HighestLoadingLine;
return AUD_PREPARING; // we want to come in at least once again, to clear everything up
}
}
#if !__FINAL
void audScriptAudioEntity::SetTimeTriggeredSpeechPlayed(u32 time)
{
m_TimeTriggeredSpeechPlayed = time;
m_ShouldSetTimeTriggeredSpeechPlayedStat = true;
}
#endif
u32 audScriptAudioEntity::GetPauseTimeForConversationLine(char* conversationContext)
{
// Find the bit before the first underscore
const s32 len = ((s32)strlen(conversationContext));
if (len>6 && strncmp(conversationContext, "PAUSE_", 6) == 0)
{
char number[32] = "";
formatf(number, 31, "%s", &conversationContext[6]);
u32 time = atoi(number);
return time;
}
return 0;
}
s32 audScriptAudioEntity::GetOverlapOffsetTime(char* conversationContext)
{
// Find the bit before the first underscore
const s32 len = ((s32)strlen(conversationContext));
if (len>8 && strncmp(conversationContext, "OVERLAP_", 8) == 0)
{
char number[32] = "";
formatf(number, 31, "%s", &conversationContext[8]);
if (strcmp(number, "PHONE") == 0)
{
return g_PhoneOverlapTime;
}
u32 time = atoi(number);
return time;
}
return -1;
}
bool audScriptAudioEntity::IsContextAnSFXCommand(char* conversationContext)
{
const s32 len = ((s32)strlen(conversationContext));
if (len>4 && strncmp(conversationContext, "SFX_", 4) == 0)
{
return true;
}
return false;
}
bool audScriptAudioEntity::StartMobilePhoneRinging(s32 ringtoneId)
{
if (m_CellphoneRingSound)
{
return false;
}
char soundName[32];
naCErrorf(ringtoneId>=0 && ringtoneId<10, "Invalid ringtoneId");
formatf(soundName, "MOBILE_PHONE_RING_%02d", ringtoneId);
audSoundInitParams initParams;
initParams.WaveSlot = audWaveSlot::FindWaveSlot(ATSTRINGHASH("PLAYER_RINGTONE", 0x0d2645e26));
initParams.PrepareTimeLimit = 3000;
initParams.AllowLoad = true;
audCategory* category = g_AudioEngine.GetCategoryManager().GetCategoryPtr(g_FrontendGameMobileRingingHash);
initParams.Category = category;
CreateAndPlaySound_Persistent(soundName, &m_CellphoneRingSound, &initParams);
if (!m_CellphoneRingSound)
{
naWarningf("Audio: ringtone %d not found", ringtoneId);
audSoundInitParams altInitParams;
altInitParams.Category = category;
CreateAndPlaySound_Persistent("MOBILE_PHONE_RING", &m_CellphoneRingSound, &altInitParams);
}
return true;
}
void audScriptAudioEntity::SetMobileRingType(s32 ringModeId)
{
m_MobileRingType = (u8)ringModeId;
audCategory* category = g_AudioEngine.GetCategoryManager().GetCategoryPtr(g_FrontendGameMobileRingingHash);
if (category)
{
switch (ringModeId)
{
case AUD_CELLPHONE_NORMAL:
category->SetVolume(0.0f);
break;
case AUD_CELLPHONE_QUIET:
category->SetVolume(-12.0f);
break;
case AUD_CELLPHONE_SILENT:
category->SetVolume(g_SilenceVolume);
break;
case AUD_CELLPHONE_VIBE:
category->SetVolume(g_SilenceVolume);
break;
case AUD_CELLPHONE_VIBE_AND_RING:
category->SetVolume(0.0f);
break;
default:
category->SetVolume(0.0f);
break;
}
}
}
void audScriptAudioEntity::SetPedMobileRingType(CPed *ped, const s32 ringType)
{
if(ped == NULL)
ped = FindPlayerPed();
Assert(ped->GetPhoneComponent());
if (ped->GetPhoneComponent())
ped->GetPhoneComponent()->SetMobileRingType(ringType);
}
bool audScriptAudioEntity::StartMobilePhoneCalling()
{
if (m_CellphoneRingSound)
{
return false;
}
CreateAndPlaySound_Persistent("CALLING_MOBILE_PHONE_RING", &m_CellphoneRingSound);
return true;
}
void audScriptAudioEntity::StopMobilePhoneRinging()
{
if (m_CellphoneRingSound)
{
m_CellphoneRingSound->StopAndForget();
}
}
void audScriptAudioEntity::StartPedMobileRinging(CPed *ped, const s32 ringtoneId)
{
if(ped == NULL)
ped = FindPlayerPed();
Assert(ped->GetPhoneComponent());
if (ped->GetPhoneComponent())
ped->GetPhoneComponent()->StartMobileRinging(static_cast<u32>(ringtoneId));
}
void audScriptAudioEntity::StopPedMobileRinging(CPed *ped)
{
// note: calling sound uses this function to stop...
if (m_CellphoneRingSound)
{
m_CellphoneRingSound->StopAndForget();
}
if(ped == NULL)
ped = FindPlayerPed();
Assert(ped->GetPhoneComponent());
if (ped->GetPhoneComponent())
ped->GetPhoneComponent()->StopMobileRinging();
}
void audScriptAudioEntity::StartNewScriptedConversationBuffer()
{
naConvDisplayf("StartNewScriptedConversationBuffer()");
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_LINES; i++)
{
m_ScriptedConversationBuffer[i].Speaker = -1;
m_ScriptedConversationBuffer[i].Listener = -1;
strcpy(m_ScriptedConversationBuffer[i].Context, "");
strcpy(m_ScriptedConversationBuffer[i].Subtitle, "");
m_ScriptedConversationBuffer[i].SpeechSlotIndex = -1;
m_ScriptedConversationBuffer[i].Volume = 0;
m_ScriptedConversationBuffer[i].PreloadSet = 0;
m_ScriptedConversationBuffer[i].UnresolvedLineNum = 0;
m_ScriptedConversationBuffer[i].DoNotRemoveSubtitle = false;
m_ScriptedConversationBuffer[i].IsRandom = false;
}
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_PedBuffer[i])
{
AssertEntityPointerValid( m_PedBuffer[i] );
m_PedBuffer[i] = NULL;
}
strcpy(m_VoiceNameBuffer[i], "");
m_PedIsInvolvedInCurrentConversation[i] = false;
m_ConvPedRagDollTimer[i] = 0;
m_ConvPedNotInRagDollTimer[i] = 0;
m_TimeThrownFromVehicle[i] = 0;
m_NullPedSpeakerLocations[i].Set(ORIGIN);
m_NullPedSpeakerEntities[i] = NULL;
}
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_ExtraConvLines = 0;
m_CodeNamePreLineNeedsResolving = false;
m_TriggeredAirborneInterrupt = false;
m_AbortScriptedConversationRequested = false;
m_ConversationIsSfxOnly = true;
m_ExternalStreamConversationSFXPlaying = false;
m_LastInterruptTimeDiff = 0;
m_PausedAndRepeatingLine = false;
m_SizeOfSubtitleSubStringArray = 0;
m_WaitingForFirstLineToPrepareToDuck = false;
if(!g_DuckForConvAfterPreload && m_ConversationDuckingScene && !m_ScriptSetNoDucking)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 1.f);
}
void audScriptAudioEntity::AddLineToScriptedConversation(u32 lineNumber, s32 speakerNumber, const char* pContext, const char* pSubtitle, s32 listenerNumber, u8 volume, bool isRandom,
bool interruptible, bool ducksRadio, bool ducksScore, audConversationAudibility audibility, bool headset,
bool dontInterruptForSpecialAbility, bool isPadSpeakerRoute)
{
Displayf("AddLineToScripted...line(%d) speaker(%d) Context(%s) headset(%d) isPad(%d)", lineNumber, speakerNumber, pContext, headset, isPadSpeakerRoute);
bool lineHandled = false;
u32 realLineNumber = lineNumber + m_ExtraConvLines;
naConvAssertf(realLineNumber<AUD_MAX_SCRIPTED_CONVERSATION_LINES, "lineNumber is out of bounds");
strcpy(m_ScriptedConversationBuffer[realLineNumber].Context, pContext);
// Check if we have to play the headset beep on.
if( headset && (m_TimeLastBeepOffPlayed + 1000 < audNorthAudioEngine::GetCurrentTimeInMs()))
{
if( m_HeadSetBeepOnLineIndex < 0 )
{
m_HeadSetBeepOnLineIndex = realLineNumber;
}
m_TimeLastBeepOffPlayed = 0;
}
if(IsContextAnSFXCommand(m_ScriptedConversationBuffer[realLineNumber].Context))
{
Assertf(!m_CodeNamePreLineNeedsResolving, "Recieved an SFX line while CODENAME_PRE still needs resolving.");
if(!strcmp(&(m_ScriptedConversationBuffer[realLineNumber].Context[4]), "CODENAME_POST"))
{
Assertf(m_SizeOfSubtitleSubStringArray == 0, "Found two procedural lines in the same conversation. Subtitling will be screwed. Alert Audio.");
Assertf(realLineNumber > 0, "Found CODENAME_POST with no preceeding line. Subtitling will be screwed. Game may crash. Alert Audio.");
Assertf(((int)m_ConversationCodeLetter) -64 > 0, "Bad ConversationCodeLetter value. Subtitling will be screwed. Game may crash. Alert Audio.");
char subString[128];
formatf(subString, 128, "%s-%i (%s)", g_CarCodeUnitTypeStrings[Clamp<int>(((int)m_ConversationCodeLetter) - 65, 0, 25)], m_ConversationCodeNumber, m_ConversationUserName);
m_SizeOfSubtitleSubStringArray = 1;
m_ArrayOfSubtitleSubStrings[0].SetLiteralString(subString, CSubStringWithinMessage::LITERAL_STRING_TYPE_PERSISTENT, false);
char codeContext[64];
formatf(codeContext, "CODENAME_%c", m_ConversationCodeLetter);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Context, codeContext);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Subtitle, m_ScriptedConversationBuffer[realLineNumber-1].Subtitle);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)realLineNumber - 1;
m_ScriptedConversationBuffer[realLineNumber].DoNotRemoveSubtitle = true;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
m_ExtraConvLines++;
realLineNumber = lineNumber + m_ExtraConvLines;
naConvAssertf(realLineNumber<AUD_MAX_SCRIPTED_CONVERSATION_LINES, "lineNumber is out of bounds");
formatf(codeContext, "CODENAME_%u", m_ConversationCodeNumber);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Context, codeContext);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Subtitle, m_ScriptedConversationBuffer[realLineNumber-1].Subtitle);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)realLineNumber - 2;
m_ScriptedConversationBuffer[realLineNumber].DoNotRemoveSubtitle = true;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
lineHandled = true;
}
else if(!strcmp(&(m_ScriptedConversationBuffer[realLineNumber].Context[4]), "CODENAME_PRE"))
{
Assertf(m_SizeOfSubtitleSubStringArray == 0, "Found two procedural lines in the same conversation. Subtitling will be screwed. Alert Audio.");
Assertf(((int)m_ConversationCodeLetter) -64 > 0, "Bad ConversationCodeLetter value. Subtitling will be screwed. Game may crash. Alert Audio.");
char subString[128];
formatf(subString, 128, "%s-%i (%s)", g_CarCodeUnitTypeStrings[Clamp<int>(((int)m_ConversationCodeLetter) - 65, 0, 25)], m_ConversationCodeNumber, m_ConversationUserName);
m_SizeOfSubtitleSubStringArray = 1;
m_ArrayOfSubtitleSubStrings[0].SetLiteralString(subString, CSubStringWithinMessage::LITERAL_STRING_TYPE_PERSISTENT, false);
char codeContext[64];
formatf(codeContext, "CODENAME_%c", m_ConversationCodeLetter);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Context, codeContext);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)realLineNumber + 1;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
m_ExtraConvLines++;
realLineNumber = lineNumber + m_ExtraConvLines;
naConvAssertf(realLineNumber<AUD_MAX_SCRIPTED_CONVERSATION_LINES, "lineNumber is out of bounds");
formatf(codeContext, "CODENAME_%u", m_ConversationCodeNumber);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Context, codeContext);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)realLineNumber;
m_ScriptedConversationBuffer[realLineNumber].DoNotRemoveSubtitle = true;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
m_CodeNamePreLineNeedsResolving = true;
lineHandled = true;
}
else if(!strcmp(&(m_ScriptedConversationBuffer[realLineNumber].Context[4]), "LOCATION"))
{
Assertf(m_SizeOfSubtitleSubStringArray == 0, "Found two procedural lines in the same conversation. Subtitling will be screwed. Alert Audio.");
naAssertf(realLineNumber<AUD_MAX_SCRIPTED_CONVERSATION_LINES, "lineNumber is out of bounds");
naAssertf(!m_ConversationLocation.IsEqual(ORIGIN), "Attempting to resolve conversation position set to ORIGIN.");
char locationContext[255];
char streetName[255];
char zoneName[255];
locationContext[0] = '\0';
streetName[0] = '\0';
zoneName[0] = '\0';
GetStreetAndZoneStringsForPos(m_ConversationLocation, streetName, 255, zoneName, 255);
m_SizeOfSubtitleSubStringArray = 1;
m_ArrayOfSubtitleSubStrings[0].SetLiteralString(zoneName, CSubStringWithinMessage::LITERAL_STRING_TYPE_PERSISTENT, false);
formatf(locationContext, "AREA_%s", zoneName);
safecpy(m_ScriptedConversationBuffer[realLineNumber].Context, locationContext);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)lineNumber;
m_ScriptedConversationBuffer[realLineNumber].DoNotRemoveSubtitle = true;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
lineHandled = true;
}
}
else
{
m_ConversationIsSfxOnly = false;
}
if(!lineHandled)
{
safecpy(m_ScriptedConversationBuffer[realLineNumber].Subtitle, pSubtitle);
m_ScriptedConversationBuffer[realLineNumber].Speaker = speakerNumber;
m_ScriptedConversationBuffer[realLineNumber].Listener = listenerNumber;
m_ScriptedConversationBuffer[realLineNumber].Volume = volume;
m_ScriptedConversationBuffer[realLineNumber].UnresolvedLineNum = (u8)lineNumber;
m_ScriptedConversationBuffer[realLineNumber].IsRandom = isRandom;
m_ScriptedConversationBuffer[realLineNumber].Audibility = audibility;
m_ScriptedConversationBuffer[realLineNumber].IsInterruptible = interruptible;
m_ScriptedConversationBuffer[realLineNumber].DucksScore = ducksScore;
m_ScriptedConversationBuffer[realLineNumber].DucksRadio = ducksRadio;
m_ScriptedConversationBuffer[realLineNumber].Headset = headset;
m_ScriptedConversationBuffer[realLineNumber].DontInterruptForSpecialAbility = dontInterruptForSpecialAbility;
m_ScriptedConversationBuffer[realLineNumber].isPadSpeakerRoute = isPadSpeakerRoute;
if(m_CodeNamePreLineNeedsResolving)
{
naAssertf(realLineNumber > 1, "Attempting to resolve SFX_CODENAME_PRE with bad line number.");
m_ScriptedConversationBuffer[realLineNumber].DoNotRemoveSubtitle = true;
safecpy(m_ScriptedConversationBuffer[realLineNumber-2].Subtitle, m_ScriptedConversationBuffer[realLineNumber].Subtitle);
safecpy(m_ScriptedConversationBuffer[realLineNumber-1].Subtitle, m_ScriptedConversationBuffer[realLineNumber].Subtitle);
m_CodeNamePreLineNeedsResolving = false;
}
}
}
void audScriptAudioEntity::GetStreetAndZoneStringsForPos(const Vector3& pos, char* streetName, u32 streetNameSize, char* zoneName, u32 zoneNameSize)
{
CNodeAddress aNode;
s32 NodesFound = ThePaths.RecordNodesInCircle(pos, 5.0f, 1, &aNode, false, false, false);
if(NodesFound != 0)
{
u32 streetID = ThePaths.FindNodePointer(aNode)->m_streetNameHash;
if(streetID != 0)
{
safecpy( streetName, TheText.Get(streetID, "Streetname"), streetNameSize );
for(u32 i = 0; i < streetNameSize; i++)
{
if(streetName[i] == ' ')
{
streetName[i] = '_';
}
}
}
}
CPopZone *zone = CPopZones::FindSmallestForPosition(&pos, ZONECAT_ANY, ZONETYPE_SP);
naAssertf(zone, "Attempted to find smallest zone for position but a null ptr was returned");
if(zone)
{
safecpy( zoneName, zone->GetTranslatedName(), zoneNameSize );
}
}
void audScriptAudioEntity::SetConversationCodename(char codeLetter, u8 codeNumber, const char* userName)
{
naConvDebugf1("SetConversationCodename() codeLetter: %c codeNumber: %u userName: %s", codeLetter, codeNumber, userName);
m_ConversationCodeLetter = codeLetter;
m_ConversationCodeNumber = codeNumber;
safecpy(m_ConversationUserName, userName);
}
void audScriptAudioEntity::SetConversationLocation(const Vector3& location)
{
naConvDebugf1("SetConversationLocation() location: %f %f %f", location.x, location.y, location.z);
m_ConversationLocation.Set(location);
}
void audScriptAudioEntity::AddConversationSpeaker(u32 speakerConversationIndex, CPed* speaker, char* voiceName)
{
naConvDisplayf("AddConversationSpeaker() - speakerConversationIndex: %u ped: %s voiceName: %s",
speakerConversationIndex, speaker ? speaker->GetModelName() : "NULL", voiceName);
if(speaker && speaker->GetSpeechAudioEntity() && !speaker->GetSpeechAudioEntity()->GetIsOkayTimeToStartConversation())
{
m_AbortScriptedConversationRequested = true;
}
naConvAssertf(speakerConversationIndex<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS, "speakerConversationIndex is out of bounds");
// Assert(speaker);
naConvAssertf(!m_PedBuffer[speakerConversationIndex], "About to write into a ped buffer that's not null");
m_PedBuffer[speakerConversationIndex] = speaker;
if (m_PedBuffer[speakerConversationIndex])
{
AssertEntityPointerValid( ((CEntity*)m_PedBuffer[speakerConversationIndex]) );
}
strcpy(m_VoiceNameBuffer[speakerConversationIndex], voiceName);
// As a debug, make sure we've not added the same ped twice - there's almost certainly no good reason to
#if !__FINAL
for (u32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (i!=speakerConversationIndex && speaker)
{
if (speaker == m_PedBuffer[i])
{
for (s32 j=0; j<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; j++)
{
naConvWarningf("id: %d; ped: %p; voice: %s", j, m_PedBuffer[j].Get(), m_VoiceNameBuffer[j]);
}
naConvWarningf("Just added id: %d", speakerConversationIndex);
naConvErrorf("Multiple speakers from one ped in conversation - please assign bug to level designer");
}
}
}
#endif // !__FINAL
}
void audScriptAudioEntity::AddFrontendConversationSpeaker(u32 speakerConversationIndex, char* voiceName)
{
naConvAssertf(speakerConversationIndex<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS, "speakerConversationIndex out of bounds");
naConvAssertf(!m_PedBuffer[speakerConversationIndex], "m_pedBuffer[%i] should be null and isn't", speakerConversationIndex);
m_PedBuffer[speakerConversationIndex] = NULL;
strcpy(m_VoiceNameBuffer[speakerConversationIndex], voiceName);
}
void audScriptAudioEntity::SetConversationControlledByAnimTriggers(bool enable)
{
Assertf(!m_ScriptedConversationInProgress, "Changing whether conversations should be controlled by anim triggers while conversation is ongoing.");
m_AnimTriggersControllingConversation = enable;
}
void audScriptAudioEntity::HandleConversaionAnimTrigger(CPed* ped)
{
if(!m_AnimTriggersControllingConversation)
{
naConvWarningf("Triggering conversation line via anim trigger when conversation isn't set to handle it. This could seriously screw up conversation logic!!");
return;
}
naConvDebugf1("Setting m_AnimTriggerReported. %u", g_AudioEngine.GetTimeInMilliseconds());
naConvAssertf(!m_AnimTriggerReported, "Triggering conversation line via anim trigger before last trigger has been processed. This could seriously screw up conversation logic!!");
if(!IsPedInCurrentConversation(ped))
{
naConvWarningf("Triggering conversation line via anim trigger from a ped not involved in the conversation. Ignoring the trigger.");
return;
}
m_AnimTriggerReported = true;
}
void audScriptAudioEntity::SetConversationPlaceholder(bool isPlaceholder)
{
Assertf(!m_ScriptedConversationInProgress, "Changing whether conversations is placeholder while conversation is ongoing.");
m_IsConversationPlaceholder = isPlaceholder;
}
bool audScriptAudioEntity::StartConversation(bool mobileCall, bool displaySubtitles, bool addToBriefing, bool cloneConversation, bool interruptible, bool preload)
{
if (m_ScriptedConversationInProgress)
{
return false;
}
naConvDebugf1("StartConversation() mobileCall: %s displaySubtitles: %s addToBriefing: %s, cloneConversation: %s interruptible: %s preload: %s",
mobileCall ? "True" : "False", displaySubtitles ? "True" : "False", addToBriefing ? "True" : "False",
cloneConversation ? "True" : "False", interruptible ? "True" : "False", preload ? "True" : "False");
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_LINES; i++)
{
// Check all the speaking peds are alive, and don't play conversation if not.
if (m_ScriptedConversationBuffer[i].Speaker>=0)
{
// also need to check that this line isn't an SFX_ lines, as they may reference a speaker number that doesn't exist
if (!IsContextAnSFXCommand(m_ScriptedConversationBuffer[i].Context))
{
// flag this ped as being involved in the conv (listening doesn't count) - we'll check this before aborting for
// missing/dead/etc peds during playback.
m_PedIsInvolvedInCurrentConversation[m_ScriptedConversationBuffer[i].Speaker] = true;
naConvAssertf(m_ScriptedConversationBuffer[i].Speaker < AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS, "Speaker index out of bounds");
// it's a valid line, so if it's not frontend, make sure it's a valid ped, and alive (TODO: and not on fire, falling, etc)
if (m_PedBuffer[m_ScriptedConversationBuffer[i].Speaker])
{
if (m_PedBuffer[m_ScriptedConversationBuffer[i].Speaker]->IsDead())
{
naConvWarningf("Didn't start scripted conv due to ped being dead for line %s", m_ScriptedConversationBuffer[i].Context);
return false;
}
else if(m_PedBuffer[m_ScriptedConversationBuffer[i].Speaker]->m_nDEflags.bFrozen)
{
if(m_ScriptedConversationBuffer[i].Volume == AUD_SPEECH_FRONTEND || m_ScriptedConversationBuffer[i].Headset)
m_PedBuffer[m_ScriptedConversationBuffer[i].Speaker] = NULL;
else
{
naConvWarningf("Didn't start scripted conv due to ped being frozen for line %s", m_ScriptedConversationBuffer[i].Context);
naConvWarningf("Speaker: %u VoiceName: %s isInteriorFrozen: %s", m_ScriptedConversationBuffer[i].Speaker, m_VoiceNameBuffer[i],
m_PedBuffer[m_ScriptedConversationBuffer[i].Speaker]->m_nDEflags.bFrozen ? "TRUE" : "FALSE");
return false;
}
}
}
}
}
m_ScriptedConversation[i].Speaker = m_ScriptedConversationBuffer[i].Speaker;
m_ScriptedConversation[i].Listener = m_ScriptedConversationBuffer[i].Listener;
m_ScriptedConversation[i].Volume = m_ScriptedConversationBuffer[i].Volume;
strcpy(m_ScriptedConversation[i].Context, m_ScriptedConversationBuffer[i].Context);
strcpy(m_ScriptedConversation[i].Subtitle, m_ScriptedConversationBuffer[i].Subtitle);
m_ScriptedConversation[i].UnresolvedLineNum = m_ScriptedConversationBuffer[i].UnresolvedLineNum;
m_ScriptedConversation[i].DoNotRemoveSubtitle = m_ScriptedConversationBuffer[i].DoNotRemoveSubtitle;
m_ScriptedConversation[i].IsRandom = m_ScriptedConversationBuffer[i].IsRandom;
m_ScriptedConversation[i].Audibility = m_ScriptedConversationBuffer[i].Audibility;
m_ScriptedConversation[i].IsInterruptible = m_ScriptedConversationBuffer[i].IsInterruptible;
m_ScriptedConversation[i].DucksScore = m_ScriptedConversationBuffer[i].DucksScore;
m_ScriptedConversation[i].DucksRadio = m_ScriptedConversationBuffer[i].DucksRadio;
m_ScriptedConversation[i].Headset = m_ScriptedConversationBuffer[i].Headset;
m_ScriptedConversation[i].DontInterruptForSpecialAbility = m_ScriptedConversationBuffer[i].DontInterruptForSpecialAbility;
m_ScriptedConversation[i].isPadSpeakerRoute = m_ScriptedConversationBuffer[i].isPadSpeakerRoute;
m_ScriptedConversation[i].HasBeenInterruptedForSpecialAbility = false;
m_ScriptedConversation[i].HasRepeatedForRagdoll = false;
m_ScriptedConversation[i].FailedToLoad = false;
m_ScriptedConversation[i].IsPlaceholder = false;
#if !__FINAL
m_ScriptedConversation[i].TimePrepareFirstRequested = 0;
#endif
if(strcmp(m_ScriptedConversation[i].Context, "") != 0)
{
naConvDisplayf("AddLineToScriptedConversation() - Line#: %u Context: %s Subtitle: %s Speaker: %d Listener: %d Volume: %u audibility: %u "
"DoNotRemoveSubtitle: %s IsInterruptible: %s DucksScore: %s DucksRadio: %s Headset: %s, DontInterruptForSpecialAbility: %s",
i,
m_ScriptedConversation[i].Context,
m_ScriptedConversation[i].Subtitle,
m_ScriptedConversation[i].Speaker,
m_ScriptedConversation[i].Listener,
m_ScriptedConversation[i].Volume,
m_ScriptedConversation[i].Audibility,
m_ScriptedConversation[i].DoNotRemoveSubtitle ? "True" : "False",
m_ScriptedConversation[i].IsInterruptible ? "True" : "False",
m_ScriptedConversation[i].DucksScore ? "True" : "False",
m_ScriptedConversation[i].DucksRadio ? "True" : "False",
m_ScriptedConversation[i].Headset ? "True" : "False",
m_ScriptedConversation[i].DontInterruptForSpecialAbility ? "True" : "False");
}
}
// Need to do these things after our last chance to abort and return false
m_CloneConversation = cloneConversation;
m_DisplaySubtitles = displaySubtitles;
m_AddSubtitlesToBriefing = addToBriefing;
m_IsScriptedConversationAMobileCall = mobileCall;
m_ScriptedConversationInProgress = true;
m_PlayingScriptedConversationLine = -1;
m_PauseConversation = preload;
m_PauseCalledFromScript = preload;
m_PausedForRagdoll = false;
m_PauseWaitingOnCurrentLine = false;
m_ScriptedConversationOverlapTriggerTime = 0;
m_TimeTrueOverlapWasSet = 0;
m_OverlapTriggerTimeAdjustedForPredelay = false;
m_TriggeredOverlapSpeech = false;
m_WaitingForPreload = false; // though actually we will be almost instantly
m_CarUpsideDownTimer = 0;
m_ConversationWasInterruptedForWantedLevel = false;
m_ConversationIsInterruptible = interruptible;
m_HasShownSubtitles = true;
m_LastSpeakingPed = NULL;
m_MarkerPredelayApplied = 0;
m_TreatNextLineToPlayAsUrgent = false;
m_InterruptSpeechRequestTime = 0;
m_CheckInterruptPreloading = false;
m_FirstLineHasStarted = false;
m_FramesVehicleIsInWater = 0;
#if !__FINAL
m_TimeLastWarnedAboutNonScriptRestart = 0;
#endif
#if __ASSERT
bool allSpeakersHaveValidVoicename = true;
u32 exampleLine = 0;
#endif
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
m_Ped[i] = m_PedBuffer[i];
if (m_Ped[i])
{
AssertEntityPointerValid( ((CEntity*)m_Ped[i]) );
}
if (m_PedBuffer[i])
{
AssertEntityPointerValid( m_PedBuffer[i] );
m_PedBuffer[i] = NULL;
}
strcpy(m_VoiceName[i], m_VoiceNameBuffer[i]);
m_BlockAnimTime[i] = 0;
// Check that any ped involved in this conversation has a valid voice.
// Can't check this as peds are added, as we don't at that point know if they're just a listener, where no voice is valid,
// or any details of the conversation to point script at the correct info.
if (m_PedIsInvolvedInCurrentConversation[i] && strcmp(m_VoiceName[i], "")==0)
{
ASSERT_ONLY(allSpeakersHaveValidVoicename = false;)
// Find the first line said by that speaker
for (s32 line=0; line<AUD_MAX_SCRIPTED_CONVERSATION_LINES; line++)
{
if (m_ScriptedConversation[line].Speaker == i)
{
naConvWarningf("No voicename for speaker %d, and line %s.",
i, m_ScriptedConversation[line].Context);
ASSERT_ONLY(exampleLine = line;)
break;
}
}
}
}
// Assert just once if any speaker is missing their voice (usually there'll multiple missing voicenames)
//naAssertf(allSpeakersHaveValidVoicename, "No voicename set for speaker in scripted conversation including line %s. Log contains more details. Please bug level design. Assign voicename when calling ADD_PED_FOR_DIALOGUE.",
// m_ScriptedConversation[exampleLine].Context);
SetAllPedsHavingAConversation(true);
if (atStringHash(m_VoiceName[1]) == ATSTRINGHASH("LAZLOW", 0x052c5cc91) || atStringHash(m_VoiceName[8]) == ATSTRINGHASH("LAZLOW", 0x052c5cc91))
{
m_IsScriptedConversationZit = true;
}
else
{
m_IsScriptedConversationZit = false;
}
#if __BANK
if (g_DebugScriptedConvs)
{
DebugScriptedConversation();
}
#endif
return true;
}
void audScriptAudioEntity::SetAllPedsHavingAConversation(bool isHavingAConversation)
{
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i] && m_Ped[i]->GetSpeechAudioEntity())
{
// Control whether they can say anything
m_Ped[i]->GetSpeechAudioEntity()->SetIsHavingAConversation(isHavingAConversation);
}
}
}
bool audScriptAudioEntity::IsScriptedConversationOngoing()
{
return m_ScriptedConversationInProgress;
}
bool audScriptAudioEntity::ShouldDuckForScriptedConversation()
{
return !IsFlagSet(audScriptAudioFlags::ScriptedSpeechDuckingDisabled) && !m_ScriptSetNoDucking && !ShouldNotDuckRadioThisLine() &&
m_ScriptedConversationInProgress && !m_IsScriptedConversationZit && (!m_PauseConversation || m_PauseWaitingOnCurrentLine) &&
m_FirstLineHasStarted;//m_DuckingForScriptedConversation;
}
bool audScriptAudioEntity::ShouldNotDuckRadioThisLine()
{
return !m_ScriptedConversationInProgress || m_PlayingScriptedConversationLine < 0 || m_PlayingScriptedConversationLine > AUD_MAX_SCRIPTED_CONVERSATION_LINES-1 ||
!m_ScriptedConversation[m_PlayingScriptedConversationLine].DucksRadio || m_ScriptSetNoDucking;
}
bool audScriptAudioEntity::ShouldDuckScoreThisLine()
{
return m_ScriptedConversationInProgress && m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].DucksScore && !m_ScriptSetNoDucking;
}
void audScriptAudioEntity::SetNoDuckingForConversationFromScript(bool enable)
{
m_ScriptSetNoDucking = enable;
}
s32 audScriptAudioEntity::GetVariationChosenForScriptedLine(u32 filenameHash)
{
if(!IsScriptedConversationOngoing())
return -1;
for(int i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_LINES; ++i)
{
tScriptedConversationEntry& entry = m_ScriptedConversation[i];
if(filenameHash == atStringHash(entry.Context) && entry.SpeechSlotIndex >= 0)
{
s32 variationNum = m_ScriptedSpeechSlot[entry.SpeechSlotIndex][entry.PreloadSet].VariationNumber;
return (variationNum > 0) ? variationNum : 0;
}
}
return -1;
}
bool audScriptAudioEntity::IsPedInCurrentConversation(const CPed* ped)
{
if(!IsScriptedConversationOngoing() || !ped)
return false;
for(int i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; ++i)
{
if(m_Ped[i] == ped)
{
return m_PedIsInvolvedInCurrentConversation[i];
}
}
return false;
}
void audScriptAudioEntity::SetPositionForNullConvPed(s32 speakerNumber, const Vector3& position)
{
if(Verifyf(!m_Ped[speakerNumber], "Trying to set position for null speaker when speaker isn't actually NULL.") &&
Verifyf(!m_NullPedSpeakerEntities[speakerNumber], "Trying to set position for null speaker when speaker already has an entity set.")
)
{
m_NullPedSpeakerLocations[speakerNumber].Set(position);
}
}
void audScriptAudioEntity::SetEntityForNullConvPed(s32 speakerNumber, CEntity* entity)
{
if(Verifyf(!m_Ped[speakerNumber], "Trying to set position for null speaker when speaker isn't actually NULL.") &&
Verifyf(m_NullPedSpeakerLocations[speakerNumber].IsEqual(ORIGIN), "Trying to set position for null speaker when speaker already has an entity set.")
)
{
m_NullPedSpeakerEntities[speakerNumber] = entity;
}
}
void audScriptAudioEntity::SayAmbientSpeechFromPosition(const char* context, const u32 voiceHash, const Vector3& pos, const char* speechParams)
{
m_AmbientSpeechAudioEntity.Say(context, speechParams, voiceHash, -1, NULL, 0, -1, 1.0f, true, 0, pos);
}
void audScriptAudioEntity::RegisterCarInAir(CVehicle* vehicle)
{
if(!vehicle || m_ColLevel == AUD_INTERRUPT_LEVEL_SCRIPTED || m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO || m_PauseConversation)
return;
CPed* interrupter = PickRandomInterrupterInVehicle(vehicle);
CPed* currentSpeaker = m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() :
NULL;
if(!currentSpeaker || !interrupter || !interrupter->GetSpeechAudioEntity() || !vehicle->ContainsPed(currentSpeaker) || !vehicle->ContainsPed(interrupter))
return;
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers);
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers)
m_SpeechRestartOffset = 0;
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
m_ColLevel = AUD_INTERRUPT_LEVEL_IN_AIR;
m_Interrupter = interrupter;
m_TriggeredAirborneInterrupt = true;
m_TimeToInterrupt = currentTime;
m_TimeLastRegisteredCarInAir = currentTime;
naConvDisplayf("RegisterCarInAir() - time: %u m_SpeechRestartOffset: %u m_TimeToInterrupt: %u, interrupter: %s",
currentTime, m_SpeechRestartOffset, m_TimeToInterrupt, interrupter ? interrupter->GetModelName() : "NULL");
}
CPed* audScriptAudioEntity::PickRandomInterrupterInVehicle(CVehicle* vehicle)
{
if(vehicle)
{
if( CNetwork::IsGameInProgress() && vehicle->GetSeatManager())
{
const s32 iNumSeats = vehicle->GetSeatManager()->GetMaxSeats();
s32 Random = fwRandom::GetRandomNumberInRange(0, iNumSeats);
for (s32 C = 0; C < vehicle->GetSeatManager()->GetMaxSeats(); C++)
{
s32 TestPassenger = (Random + C) % iNumSeats;
if (vehicle->GetSeatManager()->GetPedInSeat(TestPassenger)&& TestPassenger != vehicle->GetDriverSeat())
{
CPed * pedInSeat = vehicle->GetSeatManager()->GetPedInSeat(TestPassenger);
if (!pedInSeat->IsPlayer())
{
return pedInSeat;
}
}
}
}
else
{
return audEngineUtil::ResolveProbability(0.5f) BANK_ONLY(|| g_ForceNonPlayerInterrupt) ? vehicle->PickRandomPassenger() : vehicle->GetDriver();
}
}
return NULL;
}
void audScriptAudioEntity::RegisterSlowMoInterrupt(SlowMoType slowMoType)
{
if(!IsScriptedConversationOngoing() || m_AnimTriggersControllingConversation)
return;
if(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].DontInterruptForSpecialAbility)
return;
//if(m_ScriptedConversation[m_PlayingScriptedConversationLine].HasBeenInterruptedForSpecialAbility)
//{
if(!g_AudioEngine.GetSoundManager().IsPaused(4))
{
naConvDisplayf("Registering conversation slow mo interrupt. %u", g_AudioEngine.GetTimeInMilliseconds());
g_AudioEngine.GetSoundManager().Pause(4);
m_ColLevel = AUD_INTERRUPT_LEVEL_SLO_MO;
m_ScriptedSpeechTimerPaused = true;
m_PauseConversation = true; //make sure we don't get stuck
if( slowMoType == AUD_SLOWMO_SPECIAL)
{
m_InSlowMoForSpecialAbility = true;
}
if(slowMoType == AUD_SLOWMO_PAUSEMENU)
m_InSlowMoForPause = true;
if (m_LastDisplayedTextBlock>0 && (m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES-1 || !m_ScriptedConversation[m_PlayingScriptedConversationLine+1].DoNotRemoveSubtitle))
{
#if __BANK
sm_ScriptedLineDebugInfo[0] = '\0';
#endif
CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
m_LastDisplayedTextBlock = -1; // to avoid trying to cancel the same subtitle repeatedly.
}
}
//return;
//}
/*m_ScriptedConversation[m_PlayingScriptedConversationLine].HasBeenInterruptedForSpecialAbility = true;
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
CPed* currentSpeaker = m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() :
NULL;
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker ? currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers) : 0;
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers)
m_SpeechRestartOffset = 0;
m_ColLevel = AUD_INTERRUPT_LEVEL_SLO_MO;
m_Interrupter = currentSpeaker;
m_TimeToInterrupt = currentTime;*/
}
void audScriptAudioEntity::StopSlowMoInterrupt()
{
naConvDisplayf("Unregistering conversation slow mo interrupt. %u", g_AudioEngine.GetTimeInMilliseconds());
m_ColLevel = AUD_INTERRUPT_LEVEL_COLLISION_NONE;
if(m_TimeToRestartConversation == 0)
m_TimeToRestartConversation = g_AudioEngine.GetTimeInMilliseconds() + 500;
m_InSlowMoForPause = false;
if(g_AudioEngine.GetSoundManager().IsPaused(4))
{
if(!m_PausedAndRepeatingLine && m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
!IsContextAnSFXCommand(m_ScriptedConversation[m_PlayingScriptedConversationLine].Context))
ShowSubtitles();
g_AudioEngine.GetSoundManager().UnPause(4);
m_ScriptedSpeechTimerPaused = false;
}
}
void audScriptAudioEntity::RegisterThrownFromVehicle(CPed* ped)
{
if(!ped)
return;
//if we can catch this early enough, but right now, all the vehicle occupants are showing up null...bummer
/*if(ped->GetMyVehicle() && ped->GetMyVehicle()->GetDriver() == ped && ped->GetMyVehicle()->GetVehicleType() == VEHICLE_TYPE_BIKE)
{
const CSeatManager* seatManager = ped->GetMyVehicle()->GetSeatManager();
if(seatManager)
{
for (s32 i = 0; i < seatManager->GetMaxSeats(); i++)
{
if (seatManager->GetPedInSeat(i) && i != ped->GetMyVehicle()->GetDriverSeat())
{
RegisterThrownFromVehicle(seatManager->GetPedInSeat(i));
}
}
}
}*/
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
if(!m_ScriptedConversationInProgress)
{
audSpeechAudioEntity* speechEnt = ped->GetSpeechAudioEntity();
if(speechEnt)
{
audDamageStats damageStats;
damageStats.Fatal = false;
damageStats.IsFromAnim = true; //stop any pain level randomization
damageStats.RawDamage = g_HealthLostForHighPain + 1.0f;
damageStats.DamageReason = AUD_DAMAGE_REASON_DEFAULT;
damageStats.PedWasAlreadyDead = ped->ShouldBeDead();
speechEnt->InflictPain(damageStats);
speechEnt->SetHasBeenThrownFromVehicle();
}
}
else
{
for(int i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if(m_Ped[i] == ped && m_PedIsInvolvedInCurrentConversation[i])
{
m_TimeThrownFromVehicle[i] = currentTime;
return;
}
}
}
}
void audScriptAudioEntity::RegisterPlayerCarCollision(CVehicle* vehicle, CEntity* inflictor, f32 applyDamage)
{
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
m_TimeOfLastCollision = currentTime;
if(!vehicle || m_ColLevel == AUD_INTERRUPT_LEVEL_SCRIPTED || m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO || m_IsScriptedConversationAMobileCall ||
m_TriggeredAirborneInterrupt)
return;
audCollisionLevel colLevel = AUD_INTERRUPT_LEVEL_COLLISION_LOW;
if(applyDamage >= g_MinHighReactionDamage)
{
colLevel = AUD_INTERRUPT_LEVEL_COLLISION_HIGH;
}
else if(applyDamage >= g_MinMediumReactionDamage)
{
colLevel = AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM;
}
else if(applyDamage < g_MinLowReactionDamage)
{
return;
}
if(colLevel >= AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM && currentTime - m_TimeOfLastCollisionCDI > 30000)
{
g_AudioScannerManager.TriggerCopDispatchInteraction(SCANNER_CDIT_REPORT_SUSPECT_CRASHED_VEHICLE);
m_TimeOfLastCollisionCDI = currentTime;
}
if(currentTime - m_TimeOfLastChopCollisionReaction > 4000)
{
fwEntity* childAttachment = vehicle->GetChildAttachment();
while(childAttachment)
{
if(childAttachment->GetType() == ENTITY_TYPE_PED)
{
CPed* childAttPed = static_cast<CPed*>(childAttachment);
if(childAttPed && childAttPed->GetPedType() == PEDTYPE_ANIMAL && childAttPed->GetSpeechAudioEntity())
{
if(colLevel == AUD_INTERRUPT_LEVEL_COLLISION_HIGH)
{
childAttPed->GetSpeechAudioEntity()->PlayAnimalVocalization(0x5E666895); //painDeath phash
}
else
{
childAttPed->GetSpeechAudioEntity()->PlayAnimalVocalization(g_BarkPHash);
}
m_TimeOfLastChopCollisionReaction = currentTime;
break;
}
}
childAttachment = childAttachment->GetSiblingAttachment();
}
}
//if not a scripted conversation, we still might play something
if (!IsScriptedConversationOngoing())
{
if(vehicle->GetVehicleType() == VEHICLE_TYPE_PLANE || vehicle->GetVehicleType() == VEHICLE_TYPE_HELI)
return;
if(inflictor && inflictor->GetIsTypeVehicle())
{
CVehicle* vehicle = static_cast<CVehicle*>(inflictor);
if(vehicle && vehicle->GetVehicleType() == VEHICLE_TYPE_BICYCLE && vehicle->GetDriver() &&
vehicle->GetDriver()->GetSpeechAudioEntity())
{
vehicle->GetDriver()->GetSpeechAudioEntity()->Say("GENERIC_INSULT", "SPEECH_PARAMS_INTERRUPT");
return;
}
}
s32 numPassengers = vehicle->GetNumberOfPassenger();
CPed* driver = vehicle->GetDriver();
if(numPassengers <= 0 || audEngineUtil::ResolveProbability(0.5f))
{
if(driver)
{
if(applyDamage >= g_MinLowPainBikeReaction && vehicle->GetVehicleType() == VEHICLE_TYPE_BIKE && driver->IsLocalPlayer())
{
audDamageStats damageStats;
damageStats.Fatal = false;
damageStats.IsFromAnim = true; //not really, but this forces the pain level to stay low
damageStats.RawDamage = 1.0f;
damageStats.DamageReason = AUD_DAMAGE_REASON_DEFAULT;
driver->GetSpeechAudioEntity()->InflictPain(damageStats);
}
else if(colLevel == AUD_INTERRUPT_LEVEL_COLLISION_HIGH && driver->GetSpeechAudioEntity())
driver->GetSpeechAudioEntity()->Say("GENERIC_CURSE_MED", "SPEECH_PARAMS_INTERRUPT");
}
}
else if(colLevel >= AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM && vehicle->GetSeatManager())
{
s32 maxSeats = vehicle->GetSeatManager()->GetMaxSeats();
if(maxSeats > 0)
{
int startSeat = audEngineUtil::GetRandomInteger() % maxSeats;
for( s32 iSeat = 0; iSeat < maxSeats; iSeat++ )
{
int index = (startSeat + iSeat) % maxSeats;
CPed* pPed = vehicle->GetSeatManager()->GetPedInSeat(index);
// Special case - allow Franklin and Lamar to comment on crashes when being driven in multiplayer even if the player is controlling them
const bool allowSpeechFromPlayerPed = CNetwork::IsGameInProgress() && audSpeechAudioEntity::IsMPPlayerPedAllowedToSpeak(pPed);
if( pPed && (!pPed->IsLocalPlayer() || allowSpeechFromPlayerPed) && pPed != driver && pPed->GetSpeechAudioEntity())
{
if( pPed->GetSpeechAudioEntity()->Say("CRASH_GENERIC_DRIVEN", "SPEECH_PARAMS_INTERRUPT") )
{
break;
}
}
}
}
}
m_TimeOfLastCollisionReaction = currentTime;
return;
}
if(g_DisableInterrupts || !IsScriptedConversationOngoing() || m_PauseConversation ||
!m_ConversationIsInterruptible ||
(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES && !m_ScriptedConversation[m_PlayingScriptedConversationLine].IsInterruptible))
{
naConvDisplayf("Current script conversation interrupted due to a car collision with the player (audScriptAudioEntity::RegisterPlayerCarCollision).");
return;
}
//Collision triggers tend to come in bunches, which leads to a couple issues.
//We don't want to play interruptions right in a row, but once the min time between has passed, we don't want to start an interruption
// in the middle of a series of collision triggers...we should wait for the next initial trigger.
//randomly pick a person in the car to interrupt in SP
CPed* interrupter = PickRandomInterrupterInVehicle(vehicle);
CPed* currentSpeaker = m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() :
NULL;
if(!currentSpeaker || !interrupter || !interrupter->GetSpeechAudioEntity() || !vehicle->ContainsPed(currentSpeaker) || !vehicle->ContainsPed(interrupter))
return;
if(inflictor && inflictor->GetIsTypePed())
{
//hitting peds should be handled through RegisterCarPedCollision
return;
}
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers);
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers)
return;
if(colLevel > m_ColLevel)
m_ColLevel = colLevel;
else
return;
if( m_ColLevel != AUD_INTERRUPT_LEVEL_COLLISION_HIGH &&
(m_TimeOfLastCollisionReaction + g_MinTimeBetweenCollisionReactions > currentTime ||
m_TimeOfLastCollisionTrigger + g_MinTimeBetweenCollisionTriggers > currentTime))
return;
//naDisplayf("Player car collision %u", currentTime);
m_TimeOfLastCollisionTrigger = currentTime;
m_Interrupter = interrupter;
m_TimeToLowerCollisionLevel = currentTime + 10000;
m_LastInterruptTimeDiff = 0;
s32 timeDiff = playTime - m_SpeechRestartOffset;
naConvDisplayf("Car crash interrupt hit with time diff %i %s %u", timeDiff, m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
//if we've been given a marker in the future, then there's a small enough amount of time until the next marker to let us finish out
// playing then pause
if(timeDiff < 0)
{
m_TimeToInterrupt = currentTime - timeDiff;
}
else if(timeDiff < 2000)
{
m_TimeToInterrupt = currentTime;
//Adjust overlap time for repeated part of line
if(m_ScriptedConversationOverlapTriggerTime != 0)
{
m_ScriptedConversationOverlapTriggerTime += timeDiff;
m_LastInterruptTimeDiff = timeDiff;
naConvDebugf2("Adding %u to make trigger time %u at point 5 %s %u", timeDiff, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
}
else
{
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_InterruptTriggered = false;
}
}
void audScriptAudioEntity::RegisterCarPedCollision(CVehicle* vehicle, f32 applyDamage, CPed* victim)
{
//Collision triggers tend to come in bunches, which leads to a couple issues.
//We don't want to play interruptions right in a row, but once the min time between has passed, we don't want to start an interruption
// in the middle of a series of collision triggers...we should wait for the next initial trigger.
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
m_TimeOfLastCollision = currentTime;
if(victim)
{
u32 timeOfOldestCollision = ~0U;
u32 indexOfOldestCollision = 0;
bool victimFound = false;
//we use non-paused time for this bookkeeping, as it is compared to the time handed in to PreUpdateService
for(int i = 0; i<m_CarPedCollisions.GetMaxCount() && victimFound == false; i++)
{
if(m_CarPedCollisions[i].Victim == victim)
{
m_CarPedCollisions[i].TimeOfCollision = fwTimer::GetTimeInMilliseconds();
victimFound = true;
}
else if(timeOfOldestCollision == ~0U || m_CarPedCollisions[i].TimeOfCollision < timeOfOldestCollision)
{
timeOfOldestCollision = m_CarPedCollisions[i].TimeOfCollision;
indexOfOldestCollision = i;
}
}
if(!victimFound)
{
m_CarPedCollisions[indexOfOldestCollision].Victim = victim;
m_CarPedCollisions[indexOfOldestCollision].TimeOfCollision = fwTimer::GetTimeInMilliseconds();
}
}
m_TimeOfLastCarPedCollision = currentTime;
if(g_DisableInterrupts || !IsScriptedConversationOngoing() || m_PauseConversation || !vehicle || applyDamage < g_MinLowReactionDamage ||
!m_ConversationIsInterruptible || m_TriggeredAirborneInterrupt || m_IsScriptedConversationAMobileCall ||
m_ColLevel == AUD_INTERRUPT_LEVEL_SCRIPTED || m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO ||
(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES && m_ScriptedConversation[m_PlayingScriptedConversationLine].IsInterruptible))
{
naConvDisplayf("Current script conversation interrupted due to a car collision with a ped (audScriptAudioEntity::RegisterCarPedCollision).");
return;
}
//randomly pick a person in the car to interrupt in SP
CPed* interrupter = PickRandomInterrupterInVehicle(vehicle);
CPed* currentSpeaker = m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() :
NULL;
if(!currentSpeaker || !interrupter || !interrupter->GetSpeechAudioEntity() || !vehicle->ContainsPed(currentSpeaker) || !vehicle->ContainsPed(interrupter))
return;
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers);
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers)
return;
if(AUD_INTERRUPT_LEVEL_COLLISION_LOW > m_ColLevel)
m_ColLevel = AUD_INTERRUPT_LEVEL_COLLISION_LOW;
else
return;
if( m_TimeOfLastCollisionReaction + g_MinTimeBetweenCollisionReactions > currentTime ||
m_TimeOfLastCollisionTrigger + g_MinTimeBetweenCollisionTriggers > currentTime)
return;
m_TimeOfLastCollisionTrigger = currentTime;
m_Interrupter = interrupter;
m_TimeToLowerCollisionLevel = currentTime + 10000;
m_LastInterruptTimeDiff = 0;
s32 timeDiff = playTime - m_SpeechRestartOffset;
naConvDebugf1("Ped crash interrupt hit with time diff %i %s %u", timeDiff, m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
//if we've been given a marker in the future, then there's a small enough amount of time until the next marker to let us finish out
// playing then pause
if(timeDiff < 0)
{
m_TimeToInterrupt = currentTime - timeDiff;
}
else if(timeDiff < 2000)
{
m_TimeToInterrupt = currentTime;
//Adjust overlap time for repeated part of line
if(m_ScriptedConversationOverlapTriggerTime != 0)
{
m_ScriptedConversationOverlapTriggerTime += timeDiff;
m_LastInterruptTimeDiff = timeDiff;
naConvDebugf2("Adding %u to make trigger time %u at point 6 %s %u", timeDiff, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
}
else
{
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_InterruptTriggered = false;
}
}
void audScriptAudioEntity::RegisterWantedLevelIncrease(CVehicle* vehicle)
{
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
if(g_DisableInterrupts || !IsScriptedConversationOngoing() || m_PauseConversation || !vehicle || m_IsScriptedConversationAMobileCall ||
(m_ConversationWasInterruptedForWantedLevel BANK_ONLY(&& !g_AllowMuliWantedInterrupts)) ||
!m_ConversationIsInterruptible || m_TriggeredAirborneInterrupt ||
m_ColLevel == AUD_INTERRUPT_LEVEL_SCRIPTED || m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO ||
(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES && !m_ScriptedConversation[m_PlayingScriptedConversationLine].IsInterruptible))
{
naConvDisplayf("Current script conversation interrupted due to wanted leave increase (audScriptAudioEntity::RegisterWantedLevelIncrease).");
return;
}
m_ConversationWasInterruptedForWantedLevel = true;
//Collision triggers tend to come in bunches, which leads to a couple issues.
//We don't want to play interruptions right in a row, but once the min time between has passed, we don't want to start an interruption
// in the middle of a series of collision triggers...we should wait for the next initial trigger.
//randomly pick a person in the car to interrupt in SP
CPed* interrupter = PickRandomInterrupterInVehicle(vehicle);
CPed* currentSpeaker = m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() :
NULL;
if(!currentSpeaker || !interrupter || !interrupter->GetSpeechAudioEntity() || !vehicle->ContainsPed(currentSpeaker) || !vehicle->ContainsPed(interrupter))
return;
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers);
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers)
return;
m_ColLevel = AUD_INTERRUPT_LEVEL_WANTED;
m_Interrupter = interrupter;
m_LastInterruptTimeDiff = 0;
s32 timeDiff = playTime - m_SpeechRestartOffset;
//if we've been given a marker in the future, then there's a small enough amount of time until the next marker to let us finish out
// playing then pause
if(timeDiff < 0)
{
m_TimeToInterrupt = currentTime - timeDiff;
}
else if(timeDiff < 2000)
{
m_TimeToInterrupt = currentTime;
//Adjust overlap time for repeated part of line
if(m_ScriptedConversationOverlapTriggerTime != 0)
{
m_ScriptedConversationOverlapTriggerTime += timeDiff;
m_LastInterruptTimeDiff = timeDiff;
naConvDebugf2("Adding %u to make trigger time %u at point 7 %s %u", timeDiff, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
}
else
{
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_InterruptTriggered = false;
}
}
void audScriptAudioEntity::RegisterScriptRequestedInterrupt(CPed* interrupter, const char* context, const char* voiceName, bool pause BANK_ONLY(, bool force))
{
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
if(m_ColLevel == AUD_INTERRUPT_LEVEL_SLO_MO)
return;
#if !__FINAL
u32 placeholderVoiceNameHash = 0;
char placeholderVoiceName[128] = "";
formatf(placeholderVoiceName, sizeof(placeholderVoiceName), "%s_PLACEHOLDER", voiceName);
placeholderVoiceNameHash = atStringHash(placeholderVoiceName);
#endif
u32 voiceNameHash = atStringHash(voiceName);
if(!IsScriptedConversationOngoing() || m_PauseConversation || m_PlayingScriptedConversationLine < 0 ||
m_PlayingScriptedConversationLine > AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
NOTFINAL_ONLY(bool success = true);
if(interrupter->GetSpeechAudioEntity())
NOTFINAL_ONLY(success = AUD_SPEECH_SAY_FAILED != )interrupter->GetSpeechAudioEntity()->Say(context,
g_ForceScriptedInterrupts ? "SPEECH_PARAMS_FORCE" : "SPEECH_PARAMS_INTERRUPT", voiceNameHash);
#if !__FINAL
if(!success && g_EnablePlaceholderDialogue)
interrupter->GetSpeechAudioEntity()->Say(context,
g_ForceScriptedInterrupts ? "SPEECH_PARAMS_FORCE" : "SPEECH_PARAMS_INTERRUPT", placeholderVoiceNameHash);
#endif
return;
}
CPed* currentSpeaker = m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0 ?
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker].Get() : NULL;
if(g_DisableInterrupts || !currentSpeaker ||
(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES && !m_ScriptedConversation[m_PlayingScriptedConversationLine].IsInterruptible))
{
naConvDisplayf("Current script conversation interrupted from script (audScriptAudioEntity::RegisterScriptReqestedInterrupt)");
}
if(!FindPlayerPed() || !FindPlayerPed()->GetMyVehicle() || (interrupter && (FindPlayerPed()->GetMyVehicle() != interrupter->GetMyVehicle())))
{
BANK_ONLY(if(!force))
return;
}
u32 playTime = ~0U;
bool hasNoMarkers = false;
m_SpeechRestartOffset = currentSpeaker ? currentSpeaker->GetSpeechAudioEntity()->GetConversationInterruptionOffset(playTime, hasNoMarkers) : 0;
m_LineCheckedForRestartOffset = m_PlayingScriptedConversationLine;
if(hasNoMarkers && !g_ForceScriptedInterrupts && !IsFlagSet(audScriptAudioFlags::ForceConversationInterrupt))
return;
if(pause)
{
PauseScriptedConversation(false,true,true);
}
if(m_TimeToInterrupt != 0)
{
//We're overriding another interrupt, so make sure we adjust overlap trigger time
m_ScriptedConversationOverlapTriggerTime -= m_LastInterruptTimeDiff;
naConvDebugf2("Subtracting %u to make trigger time %u at point 9 %s %u", m_LastInterruptTimeDiff, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
m_ColLevel = AUD_INTERRUPT_LEVEL_SCRIPTED;
m_Interrupter = interrupter;
safecpy(m_InterruptingContext, context);
m_InterruptingVoiceNameHash = voiceNameHash;
NOTFINAL_ONLY(m_InterruptingPlaceholderVoiceNameHash = placeholderVoiceNameHash;)
s32 timeDiff = playTime - m_SpeechRestartOffset;
naConvDebugf1("Scripted interrupt hit with time diff %i %s %u", timeDiff, m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
//if we've been given a marker in the future, then there's a small enough amount of time until the next marker to let us finish out
// playing then pause
if(timeDiff < 0)
{
m_TimeToInterrupt = currentTime - timeDiff;
}
else
{
m_TimeToInterrupt = currentTime;
//Adjust overlap time for repeated part of line
if(m_ScriptedConversationOverlapTriggerTime != 0)
{
m_ScriptedConversationOverlapTriggerTime += timeDiff;
naConvDebugf2("Adding %u to make trigger time %u at point 8 %s %u", timeDiff, m_ScriptedConversationOverlapTriggerTime,
m_PlayingScriptedConversationLine >= 0 ? m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "[line -1]", g_AudioEngine.GetTimeInMilliseconds());
}
}
}
void audScriptAudioEntity::InterruptConversation()
{
u32 currentTime = g_AudioEngine.GetTimeInMilliseconds();
m_LastInterruptTimeDiff = 0;
if(m_ColLevel != AUD_INTERRUPT_LEVEL_SLO_MO && m_TimeOfLastCollisionReaction + g_MinTimeBetweenCollisionReactions > currentTime)
return;
naConvDisplayf("Interrupting conversation %s %u", m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
audSpeechAudioEntity* speechAudioEntity = &m_SpeechAudioEntity;
bool success = false;
bool isPlayerInterrupter = m_Interrupter && m_Interrupter->IsPlayer();
m_TimeOfLastCollisionReaction = currentTime;
m_InterruptTriggered = true;
m_TimeToInterrupt = 0;
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
bool playVocal = true;
if(playerVehicle && playerVehicle->InheritsFromHeli())
{
playVocal = false; // we don't want the vocalizations in some cases because they are no radio voice and the scripted speech is
}
switch(m_ColLevel)
{
case AUD_INTERRUPT_LEVEL_COLLISION_HIGH:
if(isPlayerInterrupter || !m_Interrupter)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
else
{
if(playVocal)
{
success = m_Interrupter->NewSayWithParams(
#if __BANK
g_szHighInterruptContext
#else
"CRASH_GENERIC_INTERRUPT"
#endif
, "SPEECH_PARAMS_INTERRUPT");
}
if(!success)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
}
break;
case AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM:
if(isPlayerInterrupter || !m_Interrupter)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
else
{
if(playVocal)
{
success = m_Interrupter->NewSayWithParams(
#if __BANK
g_szMediumInterruptContext
#else
"CRASH_GENERIC_INTERRUPT"
#endif
, "SPEECH_PARAMS_INTERRUPT");
}
if(!success)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
}
break;
case AUD_INTERRUPT_LEVEL_COLLISION_LOW:
//if(isPlayerInterrupter || !m_Interrupter)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
/*else
m_Interrupter->NewSayWithParams(
#if __BANK
g_szLowInterruptContext
#else
"GENERIC_SHOCKED_MED"
#endif
, "SPEECH_PARAMS_INTERRUPT");*/
break;
case AUD_INTERRUPT_LEVEL_WANTED:
success = m_Interrupter->NewSayWithParams("GENERIC_SHOCKED_HIGH", "SPEECH_PARAMS_INTERRUPT");
if(!success)
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
m_ColLevel = m_TimeToLowerCollisionLevel != 0 ? AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM : AUD_INTERRUPT_LEVEL_COLLISION_NONE;
case AUD_INTERRUPT_LEVEL_IN_AIR:
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
m_ColLevel = m_TimeToLowerCollisionLevel != 0 ? AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM : AUD_INTERRUPT_LEVEL_COLLISION_NONE;
break;
case AUD_INTERRUPT_LEVEL_SLO_MO:
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
break;
case AUD_INTERRUPT_LEVEL_SCRIPTED:
if( m_Interrupter && m_Interrupter->GetSpeechAudioEntity())
{
speechAudioEntity = m_Interrupter->GetSpeechAudioEntity();
}
if(speechAudioEntity)
{
success = speechAudioEntity->Say(m_InterruptingContext, "SPEECH_PARAMS_INTERRUPT", m_InterruptingVoiceNameHash,
-1, NULL, 0, -1, 1.0f, true) != AUD_SPEECH_SAY_FAILED;
}
#if !__FINAL
if(!success && speechAudioEntity)
{
success = speechAudioEntity->Say(m_InterruptingContext, "SPEECH_PARAMS_INTERRUPT", m_InterruptingPlaceholderVoiceNameHash,
-1, NULL, 0, -1, 1.0f, true) != AUD_SPEECH_SAY_FAILED;
}
#endif
if(!success)
{
m_TimeToRestartConversation = currentTime + g_TimeToPauseConvOnCollision;
}
m_ColLevel = m_TimeToLowerCollisionLevel != 0 ? AUD_INTERRUPT_LEVEL_COLLISION_MEDIUM : AUD_INTERRUPT_LEVEL_COLLISION_NONE;
m_InterruptingContext[0] = '\0';
m_InterruptingVoiceNameHash = 0;
NOTFINAL_ONLY(m_InterruptingPlaceholderVoiceNameHash = 0;)
break;
default:
m_SpeechRestartOffset = 0;
m_LineCheckedForRestartOffset = -1;
break;
}
if(success)
{
m_InterruptSpeechRequestTime = currentTime;
m_CheckInterruptPreloading = true;
}
//kick off restarted line immediately. We'll cache the predelay applied and use it to adjust the following line's trigger tiem.
m_ScriptedConversationOverlapTriggerTime = m_TimeToRestartConversation;
if(m_PlayingScriptedConversationLine < 0)
{
m_ScriptedConversationOverlapTriggerTime = 0;
m_OverlapTriggerTimeAdjustedForPredelay = false;
}
}
bool audScriptAudioEntity::IsScriptedConversationAMobileCall()
{
return m_IsScriptedConversationAMobileCall;
}
bool audScriptAudioEntity::IsScriptBankLoaded(u32 bankID)
{
for(u32 loop = 0; loop < AUD_MAX_SCRIPT_BANK_SLOTS; loop++)
{
if(m_BankSlots[loop].BankSlot->GetBankLoadingStatus(bankID) == audWaveSlot::LOADED)
{
return true;
}
}
return false;
}
bool audScriptAudioEntity::IsScriptedConversationPaused()
{
return m_PauseConversation;
}
bool audScriptAudioEntity::IsScriptedConversationLoaded()
{
return m_HighestLoadedLine != -1;
}
s32 audScriptAudioEntity::GetCurrentScriptedConversationLine()
{
// If we're not mid-conversation, do nothing
if (!m_ScriptedConversationInProgress)
{
return -1;
}
return m_PlayingScriptedConversationLine;
}
s32 audScriptAudioEntity::GetCurrentUnresolvedScriptedConversationLine()
{
// If we're not mid-conversation, do nothing
if (!m_ScriptedConversationInProgress || m_PlayingScriptedConversationLine < 0 || m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
return -1;
}
return m_ScriptedConversationBuffer[m_PlayingScriptedConversationLine].UnresolvedLineNum;
}
s32 audScriptAudioEntity::AbortScriptedConversation(bool finishCurrentLine BANK_ONLY(,const char* reason))
{
// If we're not mid-conversation, do nothing
if (!m_ScriptedConversationInProgress)
{
return -1;
}
s32 currentLine = m_PlayingScriptedConversationLine;
if (finishCurrentLine)
{
// We're going to abort asynchronously, after the current line.
// On the first Process after we've finished the current line, this'll abort.
m_AbortScriptedConversationRequested = true;
}
else
{
// We need to finish right now, and be available for another conversation request immediately.
if (m_PlayingScriptedConversationLine>=0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0) // Check we've actually started the conversation
{
// See if they're still talking
if (m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->StopSpeech();
}
else
{
m_SpeechAudioEntity.StopSpeech();
}
}
FinishScriptedConversation();
}
#if __BANK
if( g_ConversationDebugSpew )
{
naConvDisplayf("[Conv Spew] Aborting conversation %s Time: %u Reason :%s", m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds(),reason);
}
#endif
return currentLine;
}
bool audScriptAudioEntity::ShouldWeAbortScriptedConversation()
{
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if (playerVehicle)
{
if(playerVehicle->IsOnFire() && !g_AllowVehicleOnFireConversations)
{
naConvWarningf("Aborted conv: vehicle on fire");
return true;
}
if(playerVehicle->m_nVehicleFlags.bIsDrowning && !g_AllowVehicleDrowningConversations)
{
if(playerVehicle->GetVehicleType() != VEHICLE_TYPE_PLANE &&
playerVehicle->GetVehicleType() != VEHICLE_TYPE_HELI &&
playerVehicle->GetVehicleType() != VEHICLE_TYPE_BLIMP)
{
naConvWarningf("Aborted conv: vehicle drowning");
return true;
}
else
{
m_FramesVehicleIsInWater++;
}
if(m_FramesVehicleIsInWater > 15)
{
naConvWarningf("Aborted conv: vehicle drowning");
return true;
}
}
else
{
m_FramesVehicleIsInWater = 0;
}
}
else
{
m_FramesVehicleIsInWater = 0;
}
// Check if all our peds are alive, none of them are on fire, we haven't been busted, and the car's not on fire
naConvCErrorf(m_ScriptedConversationInProgress, "Trying to abort scripted conversion but there isn't one active");
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i] && m_PedIsInvolvedInCurrentConversation[i])
{
// Check they're not dead or on fire, or injured
if ( ((m_Ped[i]->GetIsDeadOrDying()|| m_Ped[i]->IsFatallyInjured() || m_Ped[i]->IsInjured()) && !g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::DisableAbortConversationForDeathAndInjury)) ||
(g_fireMan.IsEntityOnFire(m_Ped[i]) && !g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::DisableAbortConversationForOnFire)) ||
m_Ped[i]->m_nDEflags.bFrozen)
{
if(m_Ped[i]->m_nDEflags.bFrozen)
{
if(m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES &&
i == m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker &&
(m_ScriptedConversation[m_PlayingScriptedConversationLine].Volume != AUD_SPEECH_FRONTEND && !m_ScriptedConversationBuffer[i].Headset))
{
naConvWarningf("Aborted conv. Ped frozen: voice: %s line: %s interiorFrozen: %s ", m_VoiceName[i],
m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES ?
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "UNKNOWN",
m_Ped[i]->m_nDEflags.bFrozenByInterior ? "TRUE" : "FALSE");
return true;
}
}
else
{
naConvWarningf("Aborted conv: voice: %s line: %s. Ped dead, dying, (fatally) injured, on fire, or ragdolling", m_VoiceName[i],
m_PlayingScriptedConversationLine >= 0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES ?
m_ScriptedConversation[m_PlayingScriptedConversationLine].Context : "UNKNOWN");
return true;
}
}
}
}
// Check we're not busted
CPed* player = CGameWorld::FindLocalPlayer();
if (player && player->GetIsArrested())
{
naConvWarningf("Aborted conv: player arrested");
return true;
}
return false;
}
bool audScriptAudioEntity::ShouldWePauseScriptedConversation(u32 timeInMs, bool checkForInAir)
{
naConvCErrorf(m_ScriptedConversationInProgress, "Asking if we can pause scripted conversation but there isn't one active");
if(m_PlayingScriptedConversationLine >= AUD_MAX_SCRIPTED_CONVERSATION_LINES)
{
return false;
}
if(m_PlayingScriptedConversationLine < 0)
{
if(!m_PauseConversation || !m_PausedForRagdoll)
{
return false;
}
}
bool isAnyoneRagdolling = false;
//Not such an aptly named flag, now that we pause isntead of aborting, but you get the idea
if(!g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::DisableAbortConversationForRagdoll) && m_ConversationIsInterruptible)
{
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
if (m_Ped[i])
{
if(m_PedIsInvolvedInCurrentConversation[i])
{
//make sure we don't cancel thrown from vehicle until we're sure it's made it through once and called pause
if(m_TimeThrownFromVehicle[i] != 0 && m_PausedForRagdoll)
m_TimeThrownFromVehicle[i] = 0;
if(m_Ped[i]->GetUsingRagdoll())
{
if(m_ConvPedRagDollTimer[i] == 0)
m_ConvPedRagDollTimer[i] = timeInMs;
//we've been ragdolling for a long time...we better just abort this.
if(timeInMs - m_ConvPedRagDollTimer[i] > 5000)
{
AbortScriptedConversation(false BANK_ONLY(,"Ragdoling for a long time"));
}
m_ConvPedNotInRagDollTimer[i] = 0;
}
else
{
if(m_ConvPedRagDollTimer[i] != 0)
{
m_ConvPedNotInRagDollTimer[i] = timeInMs;
}
m_ConvPedRagDollTimer[i] = 0;
}
}
bool thrownFromVehicle = m_TimeThrownFromVehicle[i] != 0 && m_TimeThrownFromVehicle[i] < timeInMs;
bool hasBeenRagdollingLongEnough = m_ConvPedRagDollTimer[i] != 0 && timeInMs - m_ConvPedRagDollTimer[i] > g_MinRagdollTimeBeforeConvPause;
bool recoveringFromRagdollPause = m_PausedForRagdoll && m_ConvPedNotInRagDollTimer[i] != timeInMs && timeInMs - m_ConvPedNotInRagDollTimer[i] < g_MinNotRagdollTimeBeforeConvRestart;
if( thrownFromVehicle || hasBeenRagdollingLongEnough || recoveringFromRagdollPause)
{
isAnyoneRagdolling = true;
if(m_PlayingScriptedConversationLine >= 0 &&
(thrownFromVehicle || ( hasBeenRagdollingLongEnough && i == m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker ))
)
{
// B* 1976594 - If we hit 2 interrupts in a row, then it's possible for m_PauseConversation to be set true before
// m_PausedForRagdoll is set to true. This causes the m_TimeThrownFromVehicle[i] above to never be reset to 0,
// so the conversation never unpauses.
m_PausedForRagdoll = true;
if(!m_PauseConversation)
{
bool shouldRepeatForRagolling = false;
if(m_Ped[i]->IsLocalPlayer())
{
shouldRepeatForRagolling = !g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::DontRepeatLineForPlayerRagdolling);
}
else
{
shouldRepeatForRagolling = g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::RepeatLineForRagdolling);
}
if(shouldRepeatForRagolling)
{
if(m_ScriptedConversation[m_PlayingScriptedConversationLine].HasRepeatedForRagdoll)
{
shouldRepeatForRagolling = false;
}
else
{
m_ScriptedConversation[m_PlayingScriptedConversationLine].HasRepeatedForRagdoll = true;
}
}
PauseScriptedConversation(false, shouldRepeatForRagolling);
}
}
return true;
}
}
}
}
//If currently playing line is -1 and we've made it this far, we've already checked that we are currently paused for ragdolling
if(m_PlayingScriptedConversationLine < 0 && !isAnyoneRagdolling)
{
RestartScriptedConversation();
m_PausedForRagdoll = false;
return false;
}
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if(playerVehicle)
{
if (playerVehicle->GetVehicleDamage()->GetIsStuck(VEH_STUCK_ON_ROOF|VEH_STUCK_ON_SIDE, 2000))
{
return true;
}
if (!playerVehicle->InheritsFromSubmarine() && !playerVehicle->InheritsFromSubmarineCar() && playerVehicle->GetVehicleAudioEntity() &&
playerVehicle->GetVehicleAudioEntity()->GetDrowningFactor() > 0.0f)
{
return true;
}
//naDisplayf("time in air: %f", playerVehicle->GetTimeInAir());
if(!g_DisableAirborneInterrupts && (checkForInAir || timeInMs - m_TimeOfLastCollision < 2000))
{
u32 timePlayersCarInAir = timeInMs - m_TimePlayersCarWasLastOnTheGround;
u32 timePlayersCarOnGround = timeInMs - m_TimePlayersCarWasLastInTheAir;
if(timePlayersCarInAir > g_MinTimeCarInAirForInterrupt &&
m_TimeOfLastInAirInterrupt < timeInMs - g_MinTimeCarOnGroundForInterrupt &&
playerVehicle->GetTransform().GetC().GetZf() <= g_MinCarRollForInterrupt)
{
if(g_DisableAirborneInterrupts && timeInMs - m_TimeOfLastCollision > 2000)
m_TriggeredAirborneInterrupt = true;
//trigger interrupt once, then just return true;
if(!m_TriggeredAirborneInterrupt)
{
RegisterCarInAir(playerVehicle);
}
return true;
}
else if(m_TriggeredAirborneInterrupt && timePlayersCarInAir == 0)
{
u32 totalTimeAirborne = timeInMs - m_TimeLastRegisteredCarInAir;
//swear and set variables this upon landing
if(m_Interrupter)
{
m_Interrupter->NewSayWithParams(
#if __BANK
g_szMediumInterruptContext
#else
"CRASH_GENERIC_INTERRUPT"
#endif
, "SPEECH_PARAMS_INTERRUPT");
m_InterruptSpeechRequestTime = timeInMs;
}
//If we've been up for a while, just swear and then kill the conversation
if(totalTimeAirborne > g_MinAirBorneTimeToKillConversation)
{
AbortScriptedConversation(true BANK_ONLY(,"totalTimeAirborne > g_MinAirBorneTimeToKillConversation"));
m_TimeOfLastInAirInterrupt = timeInMs;
m_TriggeredAirborneInterrupt = false;
return false;
}
//if we're in the air a medium time, restart last line from the start
else if(totalTimeAirborne >= g_MaxAirBorneTimeToApplyRestartOffset)
{
m_SpeechRestartOffset = 0;
}
m_TimeOfLastInAirInterrupt = timeInMs;
m_TriggeredAirborneInterrupt = false;
}
if(timePlayersCarOnGround < g_MinTimeCarOnGroundToRestart)
{
return true;
}
}
}
if(m_PauseConversation && !m_PauseCalledFromScript && !m_InterruptTriggered)
RestartScriptedConversation();
m_PausedForRagdoll = false;
return false;
}
void audScriptAudioEntity::PauseScriptedConversation(bool finishCurrentLine, bool repeatCurrentLine, bool fromScript)
{
naConvDisplayf("Conversation pause called. FinishLine: %s, repeatLine: %s fromScript: %s inProgress: %s", finishCurrentLine ? "TRUE" : "FALSE",
repeatCurrentLine ? "TRUE" : "FALSE", fromScript ? "TRUE" : "FALSE", m_ScriptedConversationInProgress ? "TRUE" : "FALSE");
// If we're not mid-conversation, do nothing
if (!m_ScriptedConversationInProgress)
{
return;
}
if(!m_PauseCalledFromScript)
m_PauseCalledFromScript = fromScript;
if(m_PauseConversation && finishCurrentLine)
return;
if (finishCurrentLine)
{
// We're going to pause asynchronously, after the current line.
m_PauseConversation = true;
m_PauseWaitingOnCurrentLine = true;
}
else
{
naConvDisplayf("Pausing conversation %s %u", m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
// We need to finish right now.
if (m_PlayingScriptedConversationLine>=0 && m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0) // Check we've actually started the conversation
{
// See if they're still talking
if (m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->StopSpeech();
}
else
{
m_SpeechAudioEntity.StopSpeech();
}
// and cancel the current subtitle
if (m_LastDisplayedTextBlock>0)
{
#if __BANK
sm_ScriptedLineDebugInfo[0] = '\0';
#endif
CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(m_LastDisplayedTextBlock, false);
m_LastDisplayedTextBlock = -1; // to avoid trying to cancel the same subtitle repeatedly.
// Don't think losing our memory of playing it will ever be a problem.
}
// higher level code decides whether or not to repeat the line.
if (repeatCurrentLine && !m_PausedAndRepeatingLine)
{
naConvDebugf2("Decrementing playing conversation line 5. %u", g_AudioEngine.GetTimeInMilliseconds());
m_PlayingScriptedConversationLine--;
}
m_PausedAndRepeatingLine = repeatCurrentLine;
}
if(m_ConversationDuckingScene)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 0.f);
m_PauseConversation = true;
m_PauseWaitingOnCurrentLine = false;
}
SetAllPedsHavingAConversation(false);
return;
}
void audScriptAudioEntity::RestartScriptedConversation(bool startAtMarker, bool fromScript)
{
if(!m_PauseConversation)
{
m_TimeToRestartConversation = 0;
return;
}
if(m_PauseCalledFromScript && !fromScript)
{
#if !__FINAL
if(g_AudioEngine.GetTimeInMilliseconds() - m_TimeLastWarnedAboutNonScriptRestart > 3000)
{
naConvWarningf("Restarting a conversation from code when it was paused from script. Ignoring call to restart.");
m_TimeLastWarnedAboutNonScriptRestart = g_AudioEngine.GetTimeInMilliseconds();
}
#endif
return;
}
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
m_ConvPedNotInRagDollTimer[i] = 0;
}
m_PauseConversation = false;
m_PauseCalledFromScript = false;
#if !__FINAL
m_TimeLastWarnedAboutNonScriptRestart = 0;
#endif
m_ShouldRestartConvAtMarker = startAtMarker;
SetAllPedsHavingAConversation(true);
m_Interrupter = NULL;
m_TimeToInterrupt = 0;
m_TimeToRestartConversation = 0;
m_InterruptTriggered = false;
m_PausedAndRepeatingLine = false;
m_InterruptSpeechRequestTime = 0;
m_CheckInterruptPreloading = false;
//This function seems to be called repeatedly when starting missions, so lets be careful about this.
if(m_ScriptedConversationInProgress && m_ConversationDuckingScene && !m_ScriptSetNoDucking)
m_ConversationDuckingScene->SetVariableValue(ATSTRINGHASH("apply", 0xe865cde8), 1.f);
naConvDisplayf("Restarting conversation %s %u", m_ScriptedConversation[0].Context, g_AudioEngine.GetTimeInMilliseconds());
}
void audScriptAudioEntity::SkipToNextScriptedConversationLine()
{
// If we're not mid-conversation, do nothing
if (!m_ScriptedConversationInProgress)
{
return;
}
// Stop the current line
if (m_PlayingScriptedConversationLine>=0 && m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker >= 0) // Check we've actually started the conversation
{
// See if they're still talking
if (m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker] &&
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity())
{
m_Ped[m_ScriptedConversation[m_PlayingScriptedConversationLine].Speaker]->GetSpeechAudioEntity()->StopSpeech();
}
else
{
m_SpeechAudioEntity.StopSpeech();
}
}
m_ScriptedConversationOverlapTriggerTime = g_AudioEngine.GetTimeInMilliseconds();
// Don't touch m_PlayingScriptedConversationLine - our next update will trigger that.
}
void audScriptAudioEntity::SetAircraftWarningSpeechVoice(CVehicle* vehicle)
{
if(!vehicle || !(vehicle->GetVehicleType() == VEHICLE_TYPE_HELI || vehicle->GetVehicleType() == VEHICLE_TYPE_PLANE) ||
!vehicle->GetVehicleAudioEntity())
return;
m_AircraftWarningSpeechEnt.SetAmbientVoiceName(vehicle->GetVehicleAudioEntity()->AircraftWarningVoiceIsMale() ? g_AircraftWarningMale1Hash : g_AircraftWarningFemale1Hash, true);
}
void audScriptAudioEntity::TriggerAircraftWarningSpeech(audAircraftWarningTypes warningType)
{
if(!g_EnableAircraftWarningSpeech)
return;
if(!FindPlayerPed() || !FindPlayerPed()->GetVehiclePedInside() ||
(FindPlayerPed()->GetVehiclePedInside()->GetVehicleType() != VEHICLE_TYPE_PLANE && FindPlayerPed()->GetVehiclePedInside()->GetVehicleType() != VEHICLE_TYPE_HELI) )
return;
if(naVerifyf(warningType >= 0 && warningType < AUD_AW_NUM_WARNINGS, "Invalid warning type passed to TriggerAircraftWarningSpeech"))
{
m_AircraftWarningInfo[warningType].ShouldPlay = true;
m_AircraftWarningInfo[warningType].LastTimeTriggered = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
m_ShouldUpdateAircraftWarning = true;
}
}
void audScriptAudioEntity::UpdateAircraftWarning()
{
if(m_AircraftWarningSpeechEnt.IsAmbientSpeechPlaying())
return;
#if __BANK
if(PARAM_audiodesigner.Get())
InitAircraftWarningRaveSettings();
#endif
u32 currentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
s8 mostRecent = -1;
for(s8 i=0; i<AUD_AW_NUM_WARNINGS; i++)
{
audAircraftWarningStruct& info = m_AircraftWarningInfo[i];
if(info.ShouldPlay)
{
if(currentTime - info.LastTimeTriggered > info.MaxTimeBetweenTriggerAndPlay || currentTime - info.LastTimePlayed < info.MinTimeBetweenPlay)
info.ShouldPlay = false;
else if(mostRecent == -1 || info.LastTimeTriggered < m_AircraftWarningInfo[mostRecent].LastTimeTriggered)
mostRecent = i;
}
}
if(mostRecent == -1)
m_ShouldUpdateAircraftWarning = false;
else
{
m_AircraftWarningSpeechEnt.SayWhenSafe(m_AircraftWarningInfo[mostRecent].Context, "SPEECH_PARAMS_FORCE_FRONTEND");
m_AircraftWarningInfo[mostRecent].LastTimePlayed = currentTime;
m_AircraftWarningInfo[mostRecent].ShouldPlay = false;
}
}
s32 audScriptAudioEntity::GetIndexFromScriptThreadId(scrThreadId scriptThreadId)
{
// Find our index, from the thread id
for (s32 i=0; i<AUD_MAX_SCRIPTS; i++)
{
if (scriptThreadId == m_Scripts[i].ScriptThreadId)
{
return (s32)i;
}
}
// We didn't find it!
return -1;
}
s32 audScriptAudioEntity::AddScript()
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naCErrorf(scriptThreadId, "Failed to get thread id");
BANK_ONLY(m_CurrentScriptName = const_cast<char*>(CTheScripts::GetCurrentScriptName()));
s32 index = GetIndexFromScriptThreadId(scriptThreadId);
// DEBUG
// Make sure we're not already in the list of scripts
if (index >= 0)
{
naAssertf(!m_Scripts[index].m_ScriptisShuttingDown, "Trying to add a script that is shutting down, probably something is calling AddScript that doesn't want to");
return index;
}
// Look through for a free slot
s32 i=0;
for (i=0; i<AUD_MAX_SCRIPTS; i++)
{
if (m_Scripts[i].ScriptThreadId == THREAD_INVALID)
{
// Free slot, so we take it
// Invoke the constructor
rage_placement_new(&m_Scripts[i]) audScript();
m_Scripts[i].ScriptThreadId = scriptThreadId;
index = i;
break;
}
}
// Makes sure we actually found a slot
naAssertf(i<AUD_MAX_SCRIPTS, "Couldn't find a free slot to add script to! Please contact audio team.");
return index;
}
void audScriptAudioEntity::SetScriptCleanupTime(u32 delay)
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naCErrorf(scriptThreadId, "Failed to get thread id");
s32 index = GetIndexFromScriptThreadId(scriptThreadId);
if (index < 0)
{
return;
}
m_Scripts[index].m_ScriptShutDownDelay = delay;
}
void audScriptAudioEntity::RemoveScript(scrThreadId scriptThreadId)
{
naCErrorf(scriptThreadId, "Trying to remove script with invalid thread id");
s32 scriptIndex = GetIndexFromScriptThreadId(scriptThreadId);
if (scriptIndex < 0)
{
return;
}
BANK_ONLY(m_CurrentScriptName = "");
if(m_Scripts[scriptIndex].m_ScriptShutDownDelay > 0 && !audNorthAudioEngine::IsScreenFadedOut())
{
m_Scripts[scriptIndex].m_ScriptisShuttingDown = true;
return;
}
// Tidy up.
// If we're using any banks, dereference them, and stop any sounds that we're currently playing (we may want to reconsider that)
bool hasNetworkBanks = false;
for(int i=0; i< AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
hasNetworkBanks |= ((m_Scripts[scriptIndex].BankOverNetwork & BIT(i)) != 0);
if(m_Scripts[scriptIndex].BankSlotIndex & BIT(i))
{
if(m_BankSlots[i].HintLoaded)
{
naAssertf(m_BankSlots[i].ReferenceCount == 0, "Hinted bankslot with a ref count, looks like something leaked.");
m_BankSlots[i].HintLoaded = false;
}
else
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 - looks like something leaked");
m_BankSlots[i].ReferenceCount--;
}
if(hasNetworkBanks)
{
for (s32 j=0; j<AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
if(m_BankSlots[i].NetworkScripts[j].IsValid() && CTheScripts::GetCurrentGtaScriptHandler() && m_BankSlots[i].NetworkScripts[j] == static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()))
{
m_BankSlots[i].NetworkScripts[j].Invalidate();
Warningf("Invalidating network script id due to script getting removed, this will hopefully fix B* 2355623");
}
}
}
m_Scripts[scriptIndex].BankSlotIndex &= ~BIT(i);
m_Scripts[scriptIndex].BankOverNetwork &= ~BIT(i);
BANK_ONLY(LogScriptBankRequest("RemoveScript",scriptIndex,m_BankSlots[i].BankId,i));
}
}
for (s32 i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
if (m_ScriptSounds[i].ScriptIndex == scriptIndex)
{
// This sound belongs to this script, so stop it (if it still exists).
if (m_ScriptSounds[i].Sound)
{
// Stop the sound over the network.
if(m_ScriptSounds[i].OverNetwork && NetworkInterface::IsGameInProgress())
{
CStopSoundEvent::Trigger(static_cast<u8>(i));
}
m_ScriptSounds[i].Sound->StopAndForget();
}
ResetScriptSound(i);
}
}
// If we're in charge of audio, give that up, and reset anything
if (m_ScriptInChargeOfAudio>-1 && m_ScriptInChargeOfAudio==scriptIndex)
{
ClearUpScriptSetVariables();
m_ScriptInChargeOfAudio = -1;
}
m_Scripts[scriptIndex].Shutdown();
}
void audScriptAudioEntity::ClearUpScriptSetVariables()
{
DontAbortCarConversationsWithScriptId(false, false, false, m_ScriptInChargeOfAudio);
}
bool audScriptAudioEntity::RequestScriptBank(const char* bankName, bool bOverNetwork, unsigned playerBits, bool hintRequest)
{
if(naVerifyf(bankName, "NULL string passed to RequestScriptbank from script %s",const_cast<char*>(CTheScripts::GetCurrentScriptName())))
{
char validatedBankName[256];
formatf(validatedBankName, "%s", bankName);
// Make sure we have the right type of slash
for (u32 i=0; i<strlen(bankName); i++)
{
if (validatedBankName[i] == '/')
{
validatedBankName[i] = '\\';
}
}
u32 bankId = SOUNDFACTORY.GetBankIndexFromName(validatedBankName);
//Displayf("%s %x", validatedBankName, atStringHash(validatedBankName) );
if (bankId>=AUD_INVALID_BANK_ID)
{
char validatedBankNameWithScript[256];
formatf(validatedBankNameWithScript, "SCRIPT\\%s", validatedBankName);
bankId = SOUNDFACTORY.GetBankIndexFromName(validatedBankNameWithScript);
//Displayf("%s %x", validatedBankNameWithScript, atStringHash(validatedBankNameWithScript) );
}
if(naVerifyf(bankId < AUD_INVALID_BANK_ID, "Audio: Script bank not found: %s: %s from script %s", bankName, validatedBankName,const_cast<char*>(CTheScripts::GetCurrentScriptName())))
{
if(!hintRequest)
{
return RequestScriptBank(bankId, bOverNetwork, playerBits);
}
else
{
HintScriptBank(bankId, bOverNetwork, playerBits);
}
}
}
return false;
}
void audScriptAudioEntity::HintScriptBank(const char* bankName, bool bOverNetwork, unsigned playerBits)
{
RequestScriptBank(bankName, bOverNetwork, playerBits, true);
}
void audScriptAudioEntity::HintScriptBank(u32 bankId, bool bOverNetwork, unsigned playerBits)
{
// Abort early if audio disabled
if ( Unlikely(!g_AudioEngine.IsAudioEnabled()) )
return;
s32 scriptIndex = AddScript();
s32 freeBankSlot = -1;
//First of all look if the bank is loaded or already hinted. We will get a freeBankSlot if there is one that isn't loaded or hinted.
for(int i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
if (m_Scripts[scriptIndex].BankSlotIndex & BIT(i) && m_BankSlots[i].BankId == bankId)
{
if( m_BankSlots[i].ReferenceCount != 0 || m_BankSlots[i].HintLoaded)
{
return;
}
else
{
// as a first pass only get the freeBankSlot if the bank isn't hinted.
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0 && !m_BankSlots[i].HintLoaded)
{
freeBankSlot = i;
}
}
}
}
else if (m_BankSlots[i].BankId == bankId )
{
// already loaded or hinted.
if((m_BankSlots[i].ReferenceCount != 0 || m_BankSlots[i].HintLoaded))
{
return;
}
else
{
// bank is free but still referencing to bankId, use it.
freeBankSlot = i;
break;
}
}
else if (m_BankSlots[i].ReferenceCount == 0)
{
// as a first pass only get the freeBankSlot if the bank isn't hinted.
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0 && !m_BankSlots[i].HintLoaded)
{
freeBankSlot = i;
}
}
}
}
if (freeBankSlot == -1)
{
// If all the banks are loaded or hinted, look if we can kick off any hinted one
for(int i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
if (m_BankSlots[i].ReferenceCount == 0)
{
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0)
{
ScriptBankNoLongerNeeded(m_BankSlots[i].BankId);
freeBankSlot = i;
break;
}
}
}
}
if (freeBankSlot == -1)
{
audWarningf("No free bank slots available");
BANK_ONLY(LogScriptBanks());
return;
}
else
{
audWarningf("No free bank slots avaliable, altough some are hinted.");
BANK_ONLY(LogScriptBanks());
return;
}
}
// We have a free slot - load into it, up its ref count, and point our script at it.
// We're free to load the bank into this slot.
// Don't bother instantly seeing if it's loaded - it won't be.
m_BankSlots[freeBankSlot].BankSlot->LoadBank(bankId);
m_BankSlots[freeBankSlot].BankNameHash = atStringHash(audWaveSlot::GetBankName(bankId));
Assign(m_BankSlots[freeBankSlot].BankId, bankId);
m_BankSlots[freeBankSlot].HintLoaded = true;
m_BankSlots[freeBankSlot].State = AUD_SCRIPT_BANK_LOADING;
BANK_ONLY(LogScriptBankRequest("HINT",scriptIndex,bankId,freeBankSlot));
if(bOverNetwork && NetworkInterface::IsGameInProgress())
{
CAudioBankRequestEvent::Trigger(atStringHash(audWaveSlot::GetBankName(bankId)), static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), true, playerBits);
}
// don't record hinted banks to replay
}
audWaveSlot *audScriptAudioEntity::AllocateScriptBank()
{
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
// See if this slot's empty, and keep a note of it, in case we don't find the one we want already loaded
if (m_BankSlots[i].ReferenceCount == 0)
{
Assign(m_BankSlots[i].BankId, AUD_INVALID_BANK_ID);
m_BankSlots[i].BankNameHash = g_NullSoundHash;
m_BankSlots[i].ReferenceCount = 1;
m_BankSlots[i].State = AUD_SCRIPT_BANK_QUEUED;
return m_BankSlots[i].BankSlot;
}
}
return NULL;
}
bool audScriptAudioEntity::FreeUpScriptBank(audWaveSlot *slot)
{
for(s32 i=0; i< AUD_MAX_SCRIPT_BANK_SLOTS; i++)
{
if(slot == m_BankSlots[i].BankSlot)
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 on slot %d - looks like something leaked", i);
m_BankSlots[i].ReferenceCount--;
return true;
}
}
return false;
}
bool audScriptAudioEntity::RequestScriptBank(u32 bankId, bool bOverNetwork, unsigned playerBits)
{
// Abort early if audio disabled
if ( Unlikely(!g_AudioEngine.IsAudioEnabled()) )
return true;
s32 scriptIndex = AddScript();
s32 freeBankSlot = -1;
// We request this over and over again, until it returns true, so only want to up the ref count on the slot the first time.
// After the first time, we have the slot index stored in our script array, so use that to determine if we need to up the count.
for(int i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
if (m_Scripts[scriptIndex].BankSlotIndex & BIT(i) && m_BankSlots[i].BankId == bankId)
{
// We've got a bank slot, so see if it's loaded
if (m_BankSlots[i].State == AUD_SCRIPT_BANK_LOADED)
{
naAssertf(audWaveSlot::FindLoadedBankWaveSlot(m_BankSlots[i].BankId), "Loaded script bank: Id %u name %s points to a slot not found in the loaded bank waveslot table",bankId,m_BankSlots[i].BankSlot ? m_BankSlots[i].BankSlot->GetLoadedBankName() : "NULL BANK SLOT");
BANK_ONLY(m_TimeLoadingScriptBank = 0);
if(m_BankSlots[i].HintLoaded)
{
m_BankSlots[i].HintLoaded = false;
m_BankSlots[i].ReferenceCount = 1;
}
//BANK_ONLY(LogScriptBankRequest("REQUEST WITH BANK SLOT LOADED",scriptIndex,bankId,i));
return true;
}
else
{
// It's loading - see if it's done already
audWaveSlot::audWaveSlotLoadStatus loadStatus = m_BankSlots[i].BankSlot->GetBankLoadingStatus(bankId);
if (loadStatus == audWaveSlot::LOADED)
{
naAssertf(audWaveSlot::FindLoadedBankWaveSlot(bankId),
"Loaded wave slot not found in the loaded bank waveslot table: Id: %u name %s",bankId,m_BankSlots[i].BankSlot ? m_BankSlots[i].BankSlot->GetLoadedBankName() : "NULL BANK SLOT");
// It's loaded
m_BankSlots[i].State = AUD_SCRIPT_BANK_LOADED;
BANK_ONLY(m_TimeLoadingScriptBank = 0);
if(m_BankSlots[i].HintLoaded)
{
m_BankSlots[i].HintLoaded = false;
m_BankSlots[i].ReferenceCount = 1;
}
BANK_ONLY(LogScriptBankRequest("REQUEST WITH BANKSLOT LOADING",scriptIndex,bankId,i));
return true;
}
else
{
// We should defo be loading, or we're in a state muddle
naCErrorf(loadStatus == audWaveSlot::LOADING, "We should be loading, or we're in state middle, loadstatus: %d", loadStatus);
#if __BANK
m_TimeLoadingScriptBank += fwTimer::GetTimeStepInMilliseconds();
if(m_TimeLoadingScriptBank > 16000)
{
LogScriptBanks();
}
#endif
return false;
}
}
}
}
// We don't already have a bank slot allocated.
// Cycle through all our script slots
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
if (m_BankSlots[i].BankId == bankId)
{
// Something's already using it, up the ref count, point at it, and return depending on whether it's already loaded.
m_BankSlots[i].ReferenceCount++;
m_Scripts[scriptIndex].BankSlotIndex |= BIT(i);
if(bOverNetwork && NetworkInterface::IsGameInProgress())
{
BANK_ONLY(LogScriptBankRequest("REQUEST OVER NETWORK:",scriptIndex,bankId,i));
m_Scripts[scriptIndex].BankOverNetwork |= BIT(i);
m_Scripts[scriptIndex].PlayerBits |= playerBits;
CAudioBankRequestEvent::Trigger(atStringHash(audWaveSlot::GetBankName(bankId)), static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), true, playerBits);
}
if (m_BankSlots[i].State == AUD_SCRIPT_BANK_LOADED)
{
naAssertf(audWaveSlot::FindLoadedBankWaveSlot(m_BankSlots[i].BankId), "Loaded script bank: Id %u name %s points to a slot not found in the loaded bank waveslot table",bankId,m_BankSlots[i].BankSlot ? m_BankSlots[i].BankSlot->GetLoadedBankName() : "NULL BANK SLOT");
BANK_ONLY(m_TimeLoadingScriptBank = 0);
m_BankSlots[i].HintLoaded = false;
BANK_ONLY(LogScriptBankRequest("REQUEST IN USE LOADED",scriptIndex,bankId,i));
return true;
}
else
{
// It's loading - see if it's done already
audWaveSlot::audWaveSlotLoadStatus loadStatus = m_BankSlots[i].BankSlot->GetBankLoadingStatus(bankId);
if (loadStatus == audWaveSlot::LOADED)
{
naAssertf(audWaveSlot::FindLoadedBankWaveSlot(bankId),
"Loaded wave slot not found in the loaded bank waveslot table: Id: %u name %s",bankId,m_BankSlots[i].BankSlot ? m_BankSlots[i].BankSlot->GetLoadedBankName() : "NULL BANK SLOT");
// It's loaded
m_BankSlots[i].State = AUD_SCRIPT_BANK_LOADED;
BANK_ONLY(m_TimeLoadingScriptBank = 0);
m_BankSlots[i].HintLoaded = false;
BANK_ONLY(LogScriptBankRequest("REQUEST IN USE LOADING",scriptIndex,bankId,i));
return true;
}
else
{
// We should defo be loading, or we're in a state muddle
naCErrorf(loadStatus == audWaveSlot::LOADING, "We should be loading, or we're in state middle, loadstatus: %d", loadStatus);
#if __BANK
m_TimeLoadingScriptBank += fwTimer::GetTimeStepInMilliseconds();
if(m_TimeLoadingScriptBank > 16000)
{
LogScriptBanks();
}
#endif
return false;
}
}
}
// See if this slot's empty, and keep a note of it, in case we don't find the one we want already loaded
if (m_BankSlots[i].ReferenceCount == 0)
{
// Make sure nothing's still playing out of it - might take a few frames to free up, or even longer for a sound to release.
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0 && !m_BankSlots[i].HintLoaded)
{
freeBankSlot = i;
}
}
else
{
// We're probably running -noaudiothread - pretend we're loaded already
Assign(m_BankSlots[i].BankId, bankId);
m_BankSlots[i].BankNameHash = atStringHash(audWaveSlot::GetBankName(bankId));
m_BankSlots[i].ReferenceCount = 1;
m_BankSlots[i].HintLoaded = false;
m_BankSlots[i].State = AUD_SCRIPT_BANK_LOADED;
m_Scripts[scriptIndex].BankSlotIndex |= BIT(i);
if(bOverNetwork && NetworkInterface::IsGameInProgress())
{
BANK_ONLY(LogScriptBankRequest("REQUEST OVER NETWORK: NOAUDIO",scriptIndex,bankId,i));
m_Scripts[scriptIndex].BankOverNetwork |= BIT(i);
m_Scripts[scriptIndex].PlayerBits |= playerBits;
CAudioBankRequestEvent::Trigger(atStringHash(audWaveSlot::GetBankName(bankId)), static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), true, playerBits);
}
BANK_ONLY(LogScriptBankRequest("REQUEST NOAUDIO",scriptIndex,bankId,i));
BANK_ONLY(m_TimeLoadingScriptBank = 0);
return true;
}
}
}
// We don't have the bank already loaded (or loading), so see if we found a free one while searching.
if (freeBankSlot == -1)
{
// Check if we have any slot loaded with a hint request, if so, use it.
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
// See if this slot's empty, and keep a note of it, in case we don't find the one we want already loaded
if (m_BankSlots[i].HintLoaded)
{
// Make sure nothing's still playing out of it - might take a few frames to free up, or even longer for a sound to release.
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0)
{
ScriptBankNoLongerNeeded(m_BankSlots[i].BankId);
freeBankSlot = i;
}
}
}
}
if (freeBankSlot == -1)
{
naAssertf(false,"No free bank slots available");
#if 0 // __BANK
for (int i=0; i<(AUD_MAX_AMBIENT_BANK_SLOTS); i++)
{
Displayf("Ref Count for bank %d = %d", i, m_BankSlots[i].ReferenceCount);
if (m_BankSlots[i].BankSlot)
{
Displayf("Sound Ref count %d", m_BankSlots[i].BankSlot->GetReferenceCount());
}
}
#endif
BANK_ONLY(LogScriptBanks());
return false;
}
}
// We have a free slot - load into it, up its ref count, and point our script at it.
// We're free to load the bank into this slot.
// Don't bother instantly seeing if it's loaded - it won't be.
m_BankSlots[freeBankSlot].BankSlot->LoadBank(bankId);
Assign(m_BankSlots[freeBankSlot].BankId, bankId);
m_BankSlots[freeBankSlot].BankNameHash = atStringHash(audWaveSlot::GetBankName(bankId));
m_BankSlots[freeBankSlot].ReferenceCount = 1;
m_BankSlots[freeBankSlot].State = AUD_SCRIPT_BANK_LOADING;
m_Scripts[scriptIndex].BankSlotIndex |= BIT(freeBankSlot);
BANK_ONLY(LogScriptBankRequest("REQUEST NEW",scriptIndex,bankId,freeBankSlot));
if(bOverNetwork && NetworkInterface::IsGameInProgress())
{
BANK_ONLY(LogScriptBankRequest("REQUEST OVER NETWORK: NEW",scriptIndex,bankId,freeBankSlot));
m_Scripts[scriptIndex].BankOverNetwork |= BIT(freeBankSlot);
m_Scripts[scriptIndex].PlayerBits |= playerBits;
CAudioBankRequestEvent::Trigger(atStringHash(audWaveSlot::GetBankName(bankId)), static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), true, playerBits);
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CPacketSndLoadScriptWaveBank>(CPacketSndLoadScriptWaveBank(freeBankSlot, m_BankSlots[freeBankSlot].BankNameHash));
}
#endif
return false; // as its not actually loaded yet.
}
#if GTA_REPLAY
bool audScriptAudioEntity::ReplayLoadScriptBank(u32 bankNameHash, u32 bankSlot)
{
u32 bankId = SOUNDFACTORY.GetBankIndexFromName(bankNameHash);
if(bankId == ~0U)
{
Displayf("Attempt to load script wave bank that does not exist %u(name hash)", bankNameHash);
// we return true which stops the preload attempts
return true;
}
if(m_BankSlots[bankSlot].ReferenceCount == 0)
{
// Make sure nothing's still playing out of it - might take a few frames to free up, or even longer for a sound to release.
if (m_BankSlots[bankSlot].BankSlot)
{
if (m_BankSlots[bankSlot].BankSlot->GetReferenceCount() != 0)
{
Displayf("Stalling due to sounds playing from an existing script bank %u(name hash) ref count(%d)", bankNameHash, m_BankSlots[bankSlot].BankSlot->GetReferenceCount());
return false;
}
}
m_BankSlots[bankSlot].BankSlot->LoadBank(bankId);
Assign(m_BankSlots[bankSlot].BankId, bankId);
m_BankSlots[bankSlot].BankNameHash = atStringHash(audWaveSlot::GetBankName(bankId));
m_BankSlots[bankSlot].ReferenceCount = 1;
m_BankSlots[bankSlot].State = AUD_SCRIPT_BANK_LOADING;
m_BankSlots[bankSlot].HintLoaded = false;
// we won't have loaded yet
return false;
}
else if(m_BankSlots[bankSlot].BankNameHash == bankNameHash)
{
audWaveSlot::audWaveSlotLoadStatus loadStatus = m_BankSlots[bankSlot].BankSlot->GetBankLoadingStatus(bankId);
if(loadStatus == audWaveSlot::LOADED)
{
m_BankSlots[bankSlot].State = AUD_SCRIPT_BANK_LOADED;
return true;
}
else if(loadStatus == audWaveSlot::FAILED)
{
return true;
}
}
else if(m_BankSlots[bankSlot].ReferenceCount > 0 && m_BankSlots[bankSlot].BankNameHash != bankNameHash)
{
if (m_BankSlots[bankSlot].BankSlot)
{
if (m_BankSlots[bankSlot].BankSlot->GetReferenceCount() != 0)
{
// sound still ref bank so we can't load yet, return true for preload
return false;
}
}
// setup new bank load - only do this if the bank isn't already loaded, to prevent spamming load requests (which may block other legitimate loads)
if(m_BankSlots[bankSlot].BankSlot->GetLoadedBankId() != bankId)
{
m_BankSlots[bankSlot].BankSlot->LoadBank(bankId);
}
Assign(m_BankSlots[bankSlot].BankId, bankId);
m_BankSlots[bankSlot].BankNameHash = atStringHash(audWaveSlot::GetBankName(bankId));
m_BankSlots[bankSlot].ReferenceCount = 1;
m_BankSlots[bankSlot].State = AUD_SCRIPT_BANK_LOADING;
m_BankSlots[bankSlot].HintLoaded = false;
// we won't have loaded yet
return false;
}
return false;
}
void audScriptAudioEntity::ClearRecentPedCollisions()
{
m_TimeOfLastCarPedCollision = 0;
m_TimeLastTriggeredPedCollisionScreams = 0;
for(int i=0; i<m_CarPedCollisions.GetMaxCount(); i++)
{
m_CarPedCollisions[i].Victim = NULL;
m_CarPedCollisions[i].TimeOfCollision = 0;
}
}
void audScriptAudioEntity::ReplayFreeScriptBank(u32 bankSlot)
{
if(!m_BankSlots[bankSlot].HintLoaded)
{
Assign(m_BankSlots[bankSlot].BankId, AUD_INVALID_BANK_ID);
m_BankSlots[bankSlot].BankNameHash = g_NullSoundHash;
m_BankSlots[bankSlot].ReferenceCount = 0;
m_BankSlots[bankSlot].State = AUD_SCRIPT_BANK_LOADING;
m_BankSlots[bankSlot].HintLoaded = false;
}
}
void audScriptAudioEntity::CleanUpScriptAudioEntityForReplay()
{
for(int i=0; i<AUD_MAX_SCRIPTS; i++)
{
m_Scripts[i].m_FlagResetState.Reset(); // clear out the flags here because Shutdown may assert
m_Scripts[i].Shutdown();
}
}
void audScriptAudioEntity::CleanUpScriptBankSlotsAfterReplay()
{
for (int i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
ReplayFreeScriptBank(i);
}
// reset some time variables incase the time was messed up during the replay
m_LastMobileInterferenceTime = 0;
m_NextTimeScriptedConversationCanStart = 0;
m_TimeOfLastCollisionReaction = 0;
m_TimeOfLastCollisionTrigger = 0;
m_TimeToLowerCollisionLevel = 0;
m_TimePlayersCarWasLastOnTheGround = 0;
m_TimePlayersCarWasLastInTheAir = 0;
m_TimeOfLastInAirInterrupt = 0;
m_TimeLastRegisteredCarInAir = 0;
m_TimeOfLastCarPedCollision = 0;
m_TimeLastTriggeredPedCollisionScreams = 0;
m_TimeOfLastChopCollisionReaction = 0;
}
#endif
void audScriptAudioEntity::ScriptBankNoLongerNeeded(const char * bankName)
{
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
{
return;
}
#endif
// Abort early if audio disabled
if (Unlikely(!g_AudioEngine.IsAudioEnabled()))
{
return;
}
char validatedBankName[256];
formatf(validatedBankName, "%s", bankName);
// Make sure we have the right type of slash
for (u32 i=0; i<strlen(bankName); i++)
{
if (validatedBankName[i] == '/')
{
validatedBankName[i] = '\\';
}
}
u32 bankId = SOUNDFACTORY.GetBankIndexFromName(validatedBankName);
if (bankId>=AUD_INVALID_BANK_ID)
{
char validatedBankNameWithScript[256];
formatf(validatedBankNameWithScript, "SCRIPT\\%s", validatedBankName);
bankId = SOUNDFACTORY.GetBankIndexFromName(validatedBankNameWithScript);
}
ScriptBankNoLongerNeeded(bankId);
}
void audScriptAudioEntity::ScriptBankNoLongerNeeded(u32 bankId)
{
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
{
return;
}
#endif
s32 scriptIndex = AddScript();
if (naVerifyf(scriptIndex >= 0 && scriptIndex<AUD_MAX_SCRIPTS, "No valid scriptIndex in ScriptBankNoLongerNeeded: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
for(s32 i=0; i< AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
if(bankId == m_BankSlots[i].BankId)
{
if (m_BankSlots[i].HintLoaded)
{
naAssertf(m_BankSlots[i].ReferenceCount == 0,"Trying to free up a hint loaded bank with some references");
m_BankSlots[i].HintLoaded = false;
}
else if ( m_Scripts[scriptIndex].BankSlotIndex & BIT(i) )
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 on slot %d - looks like something leaked", i);
if(!m_BankSlots[i].ReferenceCount)
{
continue;
}
m_BankSlots[i].ReferenceCount--;
m_Scripts[scriptIndex].BankSlotIndex &= ~BIT(i);
if((m_Scripts[scriptIndex].BankOverNetwork & BIT(i)) && NetworkInterface::IsGameInProgress())
{
m_Scripts[scriptIndex].BankOverNetwork &= ~BIT(i);
CAudioBankRequestEvent::Trigger(atStringHash(audWaveSlot::GetBankName(bankId)), static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), false, m_Scripts[scriptIndex].PlayerBits);
}
BANK_ONLY(LogScriptBankRequest("FREE BANK ID",scriptIndex,m_BankSlots[i].BankId,i));
}
}
}
}
}
void audScriptAudioEntity::ScriptBankNoLongerNeeded()
{
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
{
return;
}
#endif
// Abort early if audio disabled
if ( Unlikely(!g_AudioEngine.IsAudioEnabled()) )
return;
s32 scriptIndex = AddScript();
bool hasNetworkBanks = false;
u32 playerBits = AUD_NET_ALL_PLAYERS;
if (naVerifyf(scriptIndex >= 0 && scriptIndex<AUD_MAX_SCRIPTS, "No valid scriptIndex in ScriptBankNoLongerNeeded: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
playerBits = m_Scripts[scriptIndex].PlayerBits;
for(int i=0; i< AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
hasNetworkBanks |= ((m_Scripts[scriptIndex].BankOverNetwork & BIT(i)) != 0);
if(m_BankSlots[i].HintLoaded )
{
naAssertf(m_BankSlots[i].ReferenceCount == 0,"Trying to free up a hint loaded bank with some references");
m_BankSlots[i].HintLoaded = false;
}
else if(m_Scripts[scriptIndex].BankSlotIndex & BIT(i))
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 on bank %d - looks like something leaked", i);
if(!m_BankSlots[i].ReferenceCount)
{
continue;
}
m_BankSlots[i].ReferenceCount--;
m_Scripts[scriptIndex].BankSlotIndex &= ~BIT(i);
m_Scripts[scriptIndex].BankOverNetwork &= ~BIT(i);
BANK_ONLY(LogScriptBankRequest("FREE",scriptIndex,m_BankSlots[i].BankId,i));
}
}
naAssertf(m_Scripts[scriptIndex].BankSlotIndex == 0, "Have released all bank slots on script (%s , index: %d), but Bankslotindex is not 0", CTheScripts::GetCurrentScriptName(), scriptIndex);
naAssertf(m_Scripts[scriptIndex].BankOverNetwork == 0, "Have released all bank slots on script (%s , index: %d), but BankOverNetwork is not 0", CTheScripts::GetCurrentScriptName(), scriptIndex);
}
if(hasNetworkBanks && NetworkInterface::IsGameInProgress())
{
CAudioBankRequestEvent::Trigger(0, static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId()), false, playerBits);
}
}
bool audScriptAudioEntity::RequestScriptBankFromNetwork(u32 bankHash, CGameScriptId& scriptId)
{
// Abort early if audio disabled
if ( Unlikely(!g_AudioEngine.IsAudioEnabled()) )
return true;
s32 bankSlot = -1;
bool hadBank = false;
u32 bankId = g_AudioEngine.GetSoundManager().GetFactory().GetBankNameIndexFromHash(bankHash);
// Cycle through all our bank slots
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
if (m_BankSlots[i].BankId == bankId)
{
bankSlot = i;
naDebugf3("RequestBank: Bank ID in use. BankSlot:%d, BankID:%d", i, bankId);
BANK_ONLY(LogNetworkScriptBankRequest("REQUEST OVER NETWORK: IN USE",scriptId.GetScriptName(),bankId,i));
hadBank = true;
break;
}
}
// no bank slot, so find one
if(bankSlot == -1)
{
// See if this slot's empty, and keep a note of it, in case we don't find the one we want already loaded
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
if (m_BankSlots[i].ReferenceCount == 0)
{
// Make sure nothing's still playing out of it - might take a few frames to free up, or even longer for a sound to release.
if (m_BankSlots[i].BankSlot)
{
if (m_BankSlots[i].BankSlot->GetReferenceCount() == 0)
{
bankSlot = i;
break;
}
}
else
{
// We're probably running -noaudiothread - pretend we're loaded already
Assign(m_BankSlots[i].BankId, bankId);
m_BankSlots[i].BankNameHash = bankHash;
m_BankSlots[i].ReferenceCount = 1;
m_BankSlots[i].State = AUD_SCRIPT_BANK_LOADED;
bankSlot = i;
BANK_ONLY(LogNetworkScriptBankRequest("REQUEST NOAUDIO OVER NETWORk",scriptId.GetScriptName(),bankId,i));
break;
}
}
}
}
naDebugf3("RequestBank: Using Slot - BankSlot:%d, BankID:%d", bankSlot, bankId);
BANK_ONLY(LogNetworkScriptBankRequest("REQUEST OVER NETWORK: USING SLOT",scriptId.GetScriptName(),bankId,bankSlot));
// check that we have a valid bank slot
if(!(bankSlot >= 0))
{
Warningf("Could not allocate a bank slot!");
return false;
}
// if we didn't already have this bank, load it
if(!hadBank)
{
m_BankSlots[bankSlot].BankSlot->LoadBank(bankId);
Assign(m_BankSlots[bankSlot].BankId, bankId);
m_BankSlots[bankSlot].BankNameHash = bankHash;
}
// check if we have a reference from this script - if not, add one
bool bFound = false;
for (int i=0; i<(AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS);i++)
{
if (m_BankSlots[bankSlot].NetworkScripts[i] == scriptId)
{
bFound = true;
naDisplayf("RequestBank: Found Script Reference - NetSlot:%d", i);
BANK_ONLY(LogNetworkScriptBankRequest("RequestBank: Found Script Reference",scriptId.GetScriptName(),bankId,bankSlot));
break;
}
}
if(!bFound)
{
// find an empty slot
for (int i=0; i<(AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS);i++)
{
if (!m_BankSlots[bankSlot].NetworkScripts[i].IsValid())
{
m_BankSlots[bankSlot].ReferenceCount++;
m_BankSlots[bankSlot].NetworkScripts[i] = scriptId;
bFound = true;
naDisplayf("RequestBank: Adding Script Reference - NetSlot:%d, Ref:%d", i, m_BankSlots[bankSlot].ReferenceCount);
BANK_ONLY(LogNetworkScriptBankRequest("RequestBank: Adding Script Reference",scriptId.GetScriptName(),bankId,bankSlot));
break;
}
}
}
// check that we have a valid network script slot
if(!bFound)
{
BANK_ONLY(LogScriptBanks(););
audAssertf(false, "Could not allocate a network script slot for bank slot %d! Check preceeding output for Info", bankSlot);
return false;
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CPacketSndLoadScriptWaveBank>(CPacketSndLoadScriptWaveBank(bankSlot, bankHash));
}
#endif
// success
return true;
}
void audScriptAudioEntity::ReleaseScriptBankFromNetwork(u32 bankHash, CGameScriptId& scriptId)
{
u32 bankId = g_AudioEngine.GetSoundManager().GetFactory().GetBankNameIndexFromHash(bankHash);
// Cycle through all our bank slots
for (int i=0; i<(AUD_USABLE_SCRIPT_BANK_SLOTS); i++)
{
// If we are wiping out all bank slots for this script or a particular bank slot
if ((bankId == audScriptBankSlot::INVALID_BANK_ID) || (m_BankSlots[i].BankId == bankId))
{
for (s32 j=0; j<AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
if(m_BankSlots[i].NetworkScripts[j].IsValid() && m_BankSlots[i].NetworkScripts[j] == scriptId)
{
naAssertf(m_BankSlots[i].ReferenceCount, "About to decrement a ref count of 0 - looks like something leaked");
m_BankSlots[i].ReferenceCount--;
m_BankSlots[i].NetworkScripts[j].Invalidate();
naDisplayf("ReleaseBank: BankSlot:%d, NetSlot:%d, BankID:%d, Ref:%d", i, j, m_BankSlots[i].BankId, m_BankSlots[i].ReferenceCount);
BANK_ONLY(LogNetworkScriptBankRequest("ReleaseBank FROM NETWORK:",scriptId.GetScriptName(),bankId,i));
}
}
}
}
}
s32 audScriptAudioEntity::GetSoundId()
{
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return -1;
}
s32 freeSoundId = FindFreeSoundId();
if (freeSoundId == -1)
{
naDisplayf("Script: %s index: %d requesting sound id (none free)",CTheScripts::GetCurrentScriptName(), scriptIndex);
return -1;
}
else
{
naDisplayf("Script: %s index: %d requesting sound id (returned %d)",CTheScripts::GetCurrentScriptName(), scriptIndex, freeSoundId);
}
BANK_ONLY(formatf(m_ScriptSounds[freeSoundId].ScriptName, CTheScripts::GetCurrentScriptName());)
BANK_ONLY(m_ScriptSounds[freeSoundId].TimeAllocated = fwTimer::GetTimeInMilliseconds());
BANK_ONLY(m_ScriptSounds[freeSoundId].FrameAllocated = fwTimer::GetFrameCount());
m_ScriptSounds[freeSoundId].ScriptIndex = scriptIndex;
m_ScriptSounds[freeSoundId].ScriptHasReference = true;
return freeSoundId;
}
s32 audScriptAudioEntity::FindFreeSoundId()
{
// Trawl through for a free sound
s32 i=0;
for (i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
// See if this one's free
if (m_ScriptSounds[i].ScriptIndex == -1 && m_ScriptSounds[i].NetworkId == audScriptSound::INVALID_NETWORK_ID)
{
return i;
}
}
// We didn't find a free one
naAssertf(false,"No free sound id");
return -1;
}
void audScriptAudioEntity::ReleaseSoundId(s32 soundId)
{
if(soundId < 0)
{
return;
}
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
naDisplayf("Script: %s index: %d releasing sound id %d", CTheScripts::GetCurrentScriptName(), scriptIndex, soundId);
// Make sure it's our sound to free, and that we had a reference to it
naAssertf(m_ScriptSounds[soundId].ScriptHasReference, "%s:Level design: You appear to be calling RELEASE_SOUND_ID() on a sound id that has already been released", CTheScripts::GetCurrentScriptNameAndProgramCounter());
naAssertf(!m_ScriptSounds[soundId].ScriptHasReference || m_ScriptSounds[soundId].ScriptIndex == scriptIndex, "%s:Level design: Check you're calling RELEASE_SOUND_ID() on the correct SoundID. This script is %s (%u) but sound ID %d is owned by %s (%u)", CTheScripts::GetCurrentScriptNameAndProgramCounter(), GetScriptName(scriptIndex), scriptIndex, soundId, GetScriptName(m_ScriptSounds[soundId].ScriptIndex), m_ScriptSounds[soundId].ScriptIndex);
m_ScriptSounds[soundId].ScriptHasReference = false;
}
audSound* audScriptAudioEntity::PlaySoundAndPopulateScriptSoundSlot(s32 soundId, const char* soundSetName, const char* soundName, audSoundInitParams* initParams,
bool bOverNetwork, int nNetworkRange, naEnvironmentGroup* environmentGroup, const CEntity* pEntity, const bool ownsEnvGroup, bool enableOnReplay)
{
return PlaySoundAndPopulateScriptSoundSlot(soundId, soundSetName ? atStringHash(soundSetName) : 0, soundName ? atStringHash(soundName) : g_NullSoundHash, initParams, bOverNetwork, nNetworkRange, environmentGroup, pEntity, ownsEnvGroup, enableOnReplay OUTPUT_ONLY(, soundSetName, soundName));
}
audSound* audScriptAudioEntity::PlaySoundAndPopulateScriptSoundSlot(s32 soundId, const u32 soundSetNameHash, const u32 soundNameHash, audSoundInitParams* initParams,
bool bOverNetwork, int nNetworkRange, naEnvironmentGroup* environmentGroup, const CEntity* pEntity, const bool ownsEnvGroup ,bool enableOnReplay OUTPUT_ONLY(, const char* soundSetName, const char* soundName))
{
audSoundSet soundSet;
audMetadataRef soundRef = g_NullSoundRef;
if(soundSetNameHash)
{
if(soundSet.Init(soundSetNameHash))
{
#if __BANK
if(!soundSetName)
{
soundSetName = SOUNDFACTORY.GetMetadataManager().GetObjectName(soundSetNameHash);
}
#endif
soundRef = soundSet.Find(soundNameHash);
if(soundRef == g_NullSoundRef)
{
Displayf("[SFX] Script triggered sound request failed to find valid sound in soundset (Field %s(%u) - SoundSet %s(%u))", soundName, soundNameHash, soundSetName, soundSetNameHash);
return NULL;
}
}
else
{
Displayf("[SFX] Script triggered sound request failed to find soundset (Field %s(%u) - SoundSet %s(%u))", soundName, soundNameHash, soundSetName, soundSetNameHash);
return NULL;
}
}
else
{
soundRef = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataRefFromHash(soundNameHash);
if(soundRef == g_NullSoundRef)
{
Displayf("[SFX] Script triggered sound request failed to find valid sound (Sound %s(%u))", soundName, soundNameHash);
return NULL;
}
else
{
#if __BANK
if(!soundName)
{
soundName = SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash);
}
#endif
}
}
BANK_ONLY(audDisplayf("PlaySoundAndPopulateScriptSoundSlot soundId[%d] soundSetName[%s] soundSetNameHash[%u] soundName[%s] soundNameHash[%u] bOverNetwork[%d] nNetworkRange[%d]",soundId, soundSetName, soundSetNameHash, soundName, soundNameHash, bOverNetwork, nNetworkRange);)
naAssertf(initParams, "initParams must be a valid pointer");
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return NULL;
}
// If we're not passed in a soundId, we need to rustle one up
if (soundId == -1)
{
s32 freeSoundId = FindFreeSoundId();
if (freeSoundId == -1)
{
BANK_ONLY(naErrorf("Tried to play sound but no free sound id (Sound %s(%u) - SoundSet %s(%u))", soundName, soundNameHash, soundSetName, soundSetNameHash);)
return NULL;
}
// Set up the sound slot
m_ScriptSounds[freeSoundId].ScriptIndex = scriptIndex;
m_ScriptSounds[freeSoundId].ScriptHasReference = false;
// From here on, we do the same as if we're passing a soundId in, so set it to our free one.
soundId = freeSoundId;
}
// To avoid cache misses, we're tracking which environmentGroups to update via a bitset
if(ownsEnvGroup)
{
m_ScriptOwnedEnvGroup.Set(soundId);
}
else
{
m_ScriptOwnedEnvGroup.Clear(soundId);
}
m_ScriptSounds[soundId].EnvironmentGroup = environmentGroup;
m_ScriptSounds[soundId].Entity = const_cast<CEntity*>(pEntity);
// If we're currently playing something out of this slot, stop it.
if (m_ScriptSounds[soundId].Sound)
{
// Stop the sound over the network.
if(m_ScriptSounds[soundId].OverNetwork && NetworkInterface::IsGameInProgress())
{
CStopSoundEvent::Trigger(static_cast<u8>(soundId));
}
m_ScriptSounds[soundId].Sound->StopAndForget();
#if GTA_REPLAY
m_ScriptSounds[soundId].SoundNameHash = g_NullSoundHash;
m_ScriptSounds[soundId].SoundSetHash = g_NullSoundHash;
m_ScriptSounds[soundId].ShouldRecord = enableOnReplay;
m_ScriptSounds[soundId].ClearReplayVariableNameHashs();
#endif
}
m_ScriptSounds[soundId].InitParams = *initParams;
m_ScriptSounds[soundId].SoundRef = soundRef;
m_ScriptSounds[soundId].OverNetwork = bOverNetwork;
#if __BANK
if(soundSetName)
{
formatf(m_ScriptSounds[soundId].SoundSetTriggeredName, soundSetName);
}
else
{
formatf(m_ScriptSounds[soundId].SoundSetTriggeredName, "%u", soundSetNameHash);
}
if(soundName)
{
formatf(m_ScriptSounds[soundId].SoundTriggeredName, soundName);
}
else
{
formatf(m_ScriptSounds[soundId].SoundTriggeredName, "%u", soundNameHash);
}
#endif
#if GTA_REPLAY
m_ScriptSounds[soundId].SoundNameHash = soundNameHash;
m_ScriptSounds[soundId].SoundSetHash = soundSetNameHash;
m_ScriptSounds[soundId].ShouldRecord = enableOnReplay;
#endif
// Play the sound over the network.
if(m_ScriptSounds[soundId].OverNetwork && NetworkInterface::IsGameInProgress())
{
m_ScriptSounds[soundId].NetworkId = (NetworkInterface::GetLocalPhysicalPlayerIndex() << 16) | soundId;
CGameScriptId& scriptId = static_cast<CGameScriptId&>(CTheScripts::GetCurrentGtaScriptHandler()->GetScriptId());
if(pEntity != NULL)
{
CPlaySoundEvent::Trigger(pEntity, soundSetNameHash,soundNameHash, static_cast<u8>(soundId), scriptId, nNetworkRange);
}
else
{
CPlaySoundEvent::Trigger(initParams->Position, soundSetNameHash, soundNameHash, static_cast<u8>(soundId), scriptId, nNetworkRange);
}
}
else
{
m_ScriptSounds[soundId].NetworkId = audScriptSound::INVALID_NETWORK_ID;
}
if(pEntity && pEntity->GetAudioEntity())
{
if(pEntity->GetIsTypeVehicle())
{
// Allocate a variable block in-case this sound wants to control fake revs/throttle via parameters
((audVehicleAudioEntity*)pEntity->GetAudioEntity())->AllocateVehicleVariableBlock();
}
pEntity->GetAudioEntity()->CreateAndPlaySound_Persistent(m_ScriptSounds[soundId].SoundRef, &m_ScriptSounds[soundId].Sound, initParams);
}
else
{
CreateAndPlaySound_Persistent(m_ScriptSounds[soundId].SoundRef, &m_ScriptSounds[soundId].Sound, initParams);
}
#if __BANK
if (g_DebugPlayingScriptSounds)
{
if(m_ScriptSounds[soundId].Sound)
{
Displayf("Successfully played script sound %s", SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash));
}
else
{
Displayf("Failed to play script sound %s", SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash));
}
}
#endif
return m_ScriptSounds[soundId].Sound;
}
bool audScriptAudioEntity::PlaySoundAndPopulateScriptSoundSlotFromNetwork(u32 setNameHash, u32 soundNameHash, audSoundInitParams* initParams, u32 nNetworkId,
CGameScriptId& scriptId, naEnvironmentGroup* environmentGroup, CEntity* entity, const bool ownsEnvGroup)
{
networkAudioDebugf3("PlaySoundAndPopulateScriptSoundSlotFromNetwork nNetworkId[%u]",nNetworkId);
audSoundSet soundSet;
audMetadataRef soundRef = g_NullSoundRef;
OUTPUT_ONLY(const char* soundSetName = "";)
OUTPUT_ONLY(const char* soundName = "";)
if(setNameHash)
{
if(soundSet.Init(setNameHash))
{
#if __BANK
if(!soundSetName)
{
soundSetName = SOUNDFACTORY.GetMetadataManager().GetObjectName(setNameHash);
}
#endif
soundRef = soundSet.Find(soundNameHash);
if(soundRef == g_NullSoundRef)
{
Displayf("[SFX] Script triggered sound request failed to find valid sound in soundset (Field: %u - Sound Set: %s (%u))", soundNameHash, soundSetName, setNameHash);
return false;
}
}
else
{
Displayf("[SFX] Script triggered sound request failed to find soundset (Field: %u - Sound Set: %u)", soundNameHash, setNameHash);
return false;
}
}
else
{
soundRef = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataRefFromHash(soundNameHash);
if(soundRef == g_NullSoundRef)
{
Displayf("[SFX] Script triggered sound request failed to find valid sound (Sound: %u)", soundNameHash);
return false;
}
else
{
#if __BANK
if(!soundName)
{
soundName = SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash);
}
#endif
}
}
// check if we have any pending bank loads on this script
for (s32 i=0; i<AUD_USABLE_SCRIPT_BANK_SLOTS; i++)
{
for (s32 j=0; j<AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
if(m_BankSlots[i].NetworkScripts[j].IsValid() && m_BankSlots[i].NetworkScripts[j] == scriptId)
{
if (m_BankSlots[i].State != AUD_SCRIPT_BANK_LOADED)
{
// bank load still pending - hold off trying this sound until the bank has loaded
naDebugf3("RequestSound: Bank Loading - BankSlot:%d, NetSlot:%d, Set:%u, Sound:%u, BankID:%d", i, j, setNameHash, soundNameHash, m_BankSlots[i].BankId);
networkAudioDebugf3("RequestSound: Bank Loading - BankSlot:%d, NetSlot:%d, Set:%u, Sound:%u, BankID:%d", i, j, setNameHash, soundNameHash, m_BankSlots[i].BankId);
return false;
}
}
}
}
s32 soundId = FindFreeSoundId();
if (soundId == -1)
{
naErrorf("Tried to play sound but no free sound id (Sound: %s (%u) - Sound Set: %s (%u)", soundName, soundNameHash, soundSetName, setNameHash);
return false;
}
if (m_ScriptSounds[soundId].Sound)
{
naErrorf("Tried to play sound but 'free' sound id %d already has a sound playing from it! (Sound: %s (%u) - Sound Set: %s (%u)", soundId, soundName, soundNameHash, soundSetName, setNameHash);
return false;
}
// Set up the sound slot
m_ScriptSounds[soundId].ScriptIndex = -1;
m_ScriptSounds[soundId].ScriptHasReference = false;
m_ScriptSounds[soundId].SoundRef = soundRef;
m_ScriptSounds[soundId].OverNetwork = false;
m_ScriptSounds[soundId].NetworkId = nNetworkId;
m_ScriptSounds[soundId].ScriptId = scriptId;
m_ScriptSounds[soundId].EnvironmentGroup = environmentGroup;
m_ScriptSounds[soundId].Entity = entity;
m_ScriptSounds[soundId].ShouldRecord = true;
#if __BANK
if(soundSetName)
{
formatf(m_ScriptSounds[soundId].SoundSetTriggeredName, soundSetName);
}
else
{
formatf(m_ScriptSounds[soundId].SoundSetTriggeredName, "%u", setNameHash);
}
if(soundName)
{
formatf(m_ScriptSounds[soundId].SoundTriggeredName, soundName);
}
else
{
formatf(m_ScriptSounds[soundId].SoundTriggeredName, "%u", soundNameHash);
}
#endif
#if GTA_REPLAY
m_ScriptSounds[soundId].InitParams = *initParams;
m_ScriptSounds[soundId].SoundNameHash = soundNameHash;
m_ScriptSounds[soundId].SoundSetHash = setNameHash;
Displayf("Recording sound hash %u and set %u over network", soundNameHash, setNameHash);
#endif
// To avoid cache misses, we track which environmentGroups need to be updated via a bitset
if(ownsEnvGroup)
{
m_ScriptOwnedEnvGroup.Set(soundId);
}
else
{
m_ScriptOwnedEnvGroup.Clear(soundId);
}
if(entity && entity->GetAudioEntity())
{
if (entity->GetIsTypeVehicle())
{
// Allocate a variable block in-case this sound wants to control fake revs/throttle via parameters
((audVehicleAudioEntity*)entity->GetAudioEntity())->AllocateVehicleVariableBlock();
}
entity->GetAudioEntity()->CreateAndPlaySound_Persistent(m_ScriptSounds[soundId].SoundRef, &m_ScriptSounds[soundId].Sound, initParams);
}
else
{
CreateAndPlaySound_Persistent(m_ScriptSounds[soundId].SoundRef, &m_ScriptSounds[soundId].Sound, initParams);
}
if (g_DebugPlayingScriptSounds)
{
if(m_ScriptSounds[soundId].Sound)
{
Displayf("Successfully played script sound from network (Sound: %s (%u) - Sound Set: %s (%u)", soundName, soundNameHash, soundSetName, setNameHash);
}
else
{
Displayf("Failed to play script sound from network (Sound: %s (%u) - Sound Set: %s (%u)", soundName, soundNameHash, soundSetName, setNameHash);
}
}
// doesn't matter if the sound played or not, that we tried is enough
naDebugf3("RequestSound: Played - SoundID:%d, NetID:%d, Set:%u Sound:%u", soundId, nNetworkId, setNameHash, soundNameHash);
networkAudioDebugf3("RequestSound: Played - SoundID:%d, NetID:%d, Set:%u Sound:%u", soundId, nNetworkId, setNameHash, soundNameHash);
return true;
}
void audScriptAudioEntity::SetAmbientZoneNonPersistentState(u32 ambientZone, bool enabled, bool forceUpdate)
{
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
s32 currentIndex = m_Scripts[scriptIndex].m_ScriptAmbientZoneChanges.Find(ambientZone);
if(currentIndex >= 0)
{
TristateValue currentSetting = g_AmbientAudioEntity.GetZoneNonPersistentStatus(ambientZone);
if((currentSetting == AUD_TRISTATE_TRUE && enabled) ||
(currentSetting == AUD_TRISTATE_FALSE && !enabled))
{
// We've already set this zone to the desired value, nothing to do
return;
}
// We can only set the status on zones with no status set, so we must clear the flag before resetting it
g_AmbientAudioEntity.ClearZoneNonPersistentStatus(ambientZone, forceUpdate);
m_Scripts[scriptIndex].m_ScriptAmbientZoneChanges.Delete(currentIndex);
}
if(g_AmbientAudioEntity.SetZoneNonPersistentStatus(ambientZone, enabled, forceUpdate))
{
m_Scripts[scriptIndex].m_ScriptAmbientZoneChanges.PushAndGrow(ambientZone);
}
}
void audScriptAudioEntity::ClearAmbientZoneNonPersistentState(u32 ambientZone, bool forceUpdate)
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naCErrorf(scriptThreadId, "Failed to get thread id");
s32 scriptIndex = GetIndexFromScriptThreadId(scriptThreadId);
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
s32 ambientZoneIndex = m_Scripts[scriptIndex].m_ScriptAmbientZoneChanges.Find(ambientZone);
if(ambientZoneIndex >= 0)
{
if(g_AmbientAudioEntity.ClearZoneNonPersistentStatus(ambientZone, forceUpdate))
{
m_Scripts[scriptIndex].m_ScriptAmbientZoneChanges.Delete(ambientZoneIndex);
}
}
}
void audScriptAudioEntity::SetVehicleScriptPriority(audVehicleAudioEntity* vehicleEntity, audVehicleScriptPriority priorityLevel, scrThreadId scriptThreadId)
{
s32 scriptIndex = GetIndexFromScriptThreadId(scriptThreadId);
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
if(vehicleEntity)
{
if(vehicleEntity->SetScriptPriority(priorityLevel, scriptThreadId))
{
s32 currentIndex = m_Scripts[scriptIndex].m_PriorityVehicles.Find(vehicleEntity);
if(currentIndex >= 0)
{
if(priorityLevel == AUD_VEHICLE_SCRIPT_PRIORITY_NONE)
{
m_Scripts[scriptIndex].m_PriorityVehicles.Delete(currentIndex);
}
}
else if(priorityLevel > AUD_VEHICLE_SCRIPT_PRIORITY_NONE)
{
m_Scripts[scriptIndex].m_PriorityVehicles.PushAndGrow(vehicleEntity);
}
}
}
}
void audScriptAudioEntity::SetVehicleScriptPriorityForCurrentScript(audVehicleAudioEntity* vehicleEntity, audVehicleScriptPriority priorityLevel)
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naCErrorf(scriptThreadId, "Failed to get thread id");
SetVehicleScriptPriority(vehicleEntity, priorityLevel, scriptThreadId);
}
void audScriptAudioEntity::SetPlayerAngry(audSpeechAudioEntity* speechEntity, bool isAngry)
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naCErrorf(scriptThreadId, "Failed to get thread id");
s32 scriptIndex = GetIndexFromScriptThreadId(scriptThreadId);
if (!naVerifyf(scriptIndex>=0 || !isAngry, "No valid scriptIndex: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
if(speechEntity)
{
speechEntity->SetPedIsAngry(isAngry);
if(scriptIndex >= 0)
{
int index = m_Scripts[scriptIndex].m_PedsSetToAngry.Find(speechEntity);
if(isAngry)
{
if(index == -1)
{
m_Scripts[scriptIndex].m_PedsSetToAngry.PushAndGrow(speechEntity);
}
}
else
{
if(index != -1)
{
m_Scripts[scriptIndex].m_PedsSetToAngry.Delete(index);
}
}
}
else if(!isAngry)
{
for(int i=0; i<AUD_MAX_SCRIPTS; i++)
{
int index = m_Scripts[i].m_PedsSetToAngry.Find(speechEntity);
if(index != -1)
{
m_Scripts[i].m_PedsSetToAngry.Delete(index);
}
}
}
}
}
void audScriptAudioEntity::SetPlayerAngryShortTime(audSpeechAudioEntity* speechEntity, u32 timeInMs)
{
if(speechEntity)
speechEntity->SetPedIsAngryShortTime(timeInMs);
}
void audScriptAudioEntity::PlaySoundFromEntity(s32 soundId, const char* soundName, const CEntity* entity, const char * setName, bool bOverNetwork, int nNetworkRange)
{
PlaySoundFromEntity(soundId, soundName ? atStringHash(soundName) : g_NullSoundHash, entity, setName ? atStringHash(setName) : 0, bOverNetwork, nNetworkRange OUTPUT_ONLY(, setName, soundName));
}
void audScriptAudioEntity::PlaySoundFromEntity(s32 soundId, const u32 soundNameHash, const CEntity* entity, const u32 setNameHash, bool bOverNetwork, int nNetworkRange OUTPUT_ONLY(, const char* soundSetName, const char* soundName))
{
// Grab an existing environmentGroup if we already have one (ped or vehicle) otherwise create a new one.
bool ownsEnvGroup = false;
naEnvironmentGroup* environmentGroup = (naEnvironmentGroup*)entity->GetAudioEnvironmentGroup();
if(!environmentGroup && entity)
{
environmentGroup = CreateScriptEnvironmentGroup(entity, entity->GetTransform().GetPosition());
ownsEnvGroup = true;
}
PlaySoundFromEntity(soundId, soundNameHash, entity, environmentGroup, setNameHash, bOverNetwork, nNetworkRange, ownsEnvGroup OUTPUT_ONLY(, soundSetName, soundName));
}
void audScriptAudioEntity::PlaySoundFromEntity(s32 soundId, const u32 soundNameHash, const CEntity* entity, naEnvironmentGroup* environmentGroup,
const u32 setNameHash, bool bOverNetwork, int nNetworkRange, const bool ownsEnvGroup OUTPUT_ONLY(, const char* soundSetName, const char* soundName))
{
BANK_ONLY(networkAudioDebugf3("PlaySoundFromEntity soundId[%d] soundName[%s] entity[%p][%s] setName[%s] bOverNetwork[%d] nNetworkRange[%d] ownsEnvGroup[%d]",soundId,SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash),entity,entity ? entity->GetModelName() : "",SOUNDFACTORY.GetMetadataManager().GetObjectName(setNameHash),bOverNetwork,nNetworkRange,ownsEnvGroup);)
naAssertf(entity, "entity must be a valid pointer");
//if( entity->GetIsTypeObject() )
//{
// if(!((CObject*)entity)->GetObjectAudioEntity()->HasRegisteredWithController())
// {
// ((CObject*)entity)->GetObjectAudioEntity()->Init((CObject*)entity);
// }
//}
//naAssertf(((CObject*)entity)->GetObjectAudioEntity()->HasRegisteredWithController(), "trying to play a sound from an object that hasn't got a valid audio entity");
audSound* sound = NULL;
audSoundInitParams initParams;
Vec3V position = entity->GetTransform().GetPosition();
initParams.Tracker = entity->GetPlaceableTracker();
initParams.EnvironmentGroup = environmentGroup;
initParams.Position = VEC3V_TO_VECTOR3(position);
sound = PlaySoundAndPopulateScriptSoundSlot(soundId, setNameHash, soundNameHash, &initParams, bOverNetwork, nNetworkRange, environmentGroup, entity, ownsEnvGroup, true OUTPUT_ONLY(, soundSetName, soundName));
#if GTA_REPLAY
if(sound)
{
RecordScriptSlotSound(setNameHash, soundNameHash, &initParams, entity, sound);
}
#endif
#if __BANK
if(sound)
{
Displayf("[SFX] PLAY_SOUND_FROM_ENTITY - Sound: %s(%u) - Set:%s(%u): Played successfully (%s)", soundName, soundNameHash, soundSetName, setNameHash, CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
else
{
Displayf("[SFX] PLAY_SOUND_FROM_ENTITY - Sound: %s(%u) - Set:%s(%u): Failed to play (%s)", soundName, soundNameHash, soundSetName, setNameHash, CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
#endif
}
void audScriptAudioEntity::PlaySoundFromPosition(s32 soundId, const char* soundName, const Vector3* position, const char * setName, bool bOverNetwork, int nNetworkRange, bool isExteriorLoc)
{
networkAudioDebugf3("PlaySoundFromPosition soundId[%d] soundName[%s] setName[%s] bOverNetwork[%d] nNetworkRange[%d] isExteriorLoc[%d]",soundId,soundName,setName,bOverNetwork,nNetworkRange,isExteriorLoc);
// If it's an exterior location, then we can use an environmentGroup since we have accurate interior information
naEnvironmentGroup* environmentGroup = NULL;
bool ownsEnvGroup = false;
if(isExteriorLoc)
{
environmentGroup = CreateScriptEnvironmentGroup(NULL, VECTOR3_TO_VEC3V(*position));
ownsEnvGroup = true;
}
audSound* sound = NULL;
audSoundInitParams initParams;
initParams.Position = *position;
initParams.EnvironmentGroup = environmentGroup;
sound = PlaySoundAndPopulateScriptSoundSlot(soundId, setName, soundName, &initParams, bOverNetwork, nNetworkRange, environmentGroup, NULL, ownsEnvGroup, true);
#if GTA_REPLAY
if(sound && CReplayMgr::IsRecording())
{
if(soundId != -1)
{
if(setName)
{
m_ScriptSounds[soundId].SoundSetHash = atStringHash(setName);
}
else
{
m_ScriptSounds[soundId].SoundSetHash = g_NullSoundHash;
}
m_ScriptSounds[soundId].SoundNameHash = atStringHash(soundName);
m_ScriptSounds[soundId].InitParams = initParams;
}
RecordScriptSlotSound(setName, soundName, &initParams, NULL, sound);
}
#endif
#if __BANK
if(sound)
{
Displayf("[SFX] PLAY_SOUND_FROM_COORD - Sound:%s - Set:%s: Played successfully (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
else
{
Displayf("[SFX] PLAY_SOUND_FROM_COORD - Sound:%s - Set:%s: Failed to play (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
#endif
}
void audScriptAudioEntity::UpdateSoundPosition(s32 soundId, const Vector3* position)
{
if (soundId<0)
{
return;
}
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
// Make sure it's our sound ref to stop
naCErrorf(m_ScriptSounds[soundId].ScriptIndex == scriptIndex, "%s:Level design: Check you're calling UPDATE_SOUND_POSITION(soundId, vector) on the correct SoundID", CTheScripts::GetCurrentScriptName());
if (m_ScriptSounds[soundId].Sound && m_ScriptSounds[soundId].ScriptIndex == scriptIndex)
{
m_ScriptSounds[soundId].Sound->SetRequestedPosition(VECTOR3_TO_VEC3V(*position));
}
}
void audScriptAudioEntity::PlayFireSoundFromPosition(s32 soundId, const Vector3* position)
{
audSoundInitParams initParams;
initParams.Position = *position;
initParams.StartOffset = g_NextFireStartOffset;
initParams.IsStartOffsetPercentage = true;
g_NextFireStartOffset = (g_NextFireStartOffset+70)%100;
audSound* sound = PlaySoundAndPopulateScriptSoundSlot(soundId, "", "SCRIPT_FIRE", &initParams, false, 0, NULL, NULL, false, true);
if(sound)
{
REPLAY_ONLY(RecordScriptSlotSound(NULL, "SCRIPT_FIRE", &initParams, NULL, sound);)
}
}
void audScriptAudioEntity::PlaySound(s32 soundId, const char* soundName, const char * setName, bool bOverNetwork, int nNetworkRange,bool REPLAY_ONLY(enableOnReplay))
{
audSound* sound = NULL;
audSoundInitParams initParams;
sound = PlaySoundAndPopulateScriptSoundSlot(soundId, setName, soundName, &initParams, bOverNetwork, nNetworkRange, NULL, NULL, false, enableOnReplay);
#if GTA_REPLAY
if(sound && enableOnReplay)
{
if(soundId != -1)
{
if(setName)
{
m_ScriptSounds[soundId].SoundSetHash = atStringHash(setName);
}
else
{
m_ScriptSounds[soundId].SoundSetHash = g_NullSoundHash;
}
m_ScriptSounds[soundId].SoundNameHash = atStringHash(soundName);
m_ScriptSounds[soundId].InitParams = initParams;
}
RecordScriptSlotSound(setName, soundName, &initParams, NULL, sound);
}
#endif
#if __BANK
if(sound)
{
Displayf("[SFX] PLAY_SOUND - Sound:%s - Set:%s: Played successfully (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
else
{
Displayf("[SFX] PLAY_SOUND - Sound:%s - Set:%s: Failed to play (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
#endif
}
void audScriptAudioEntity::PlaySoundFrontend(s32 soundId, const char* soundName, const char * setName, bool REPLAY_ONLY(enableOnReplay))
{
audSound* sound = NULL;
audSoundInitParams initParams;
initParams.Pan = 0;
if(setName)
{
const u32 soundSetNameHash = atStringHash(setName);
if( soundSetNameHash == ATSTRINGHASH("MissionFailedSounds", 0x5A23F3D5) ||
soundSetNameHash == ATSTRINGHASH("BustedSounds", 0xDF84A53C) ||
soundSetNameHash == ATSTRINGHASH("WastedSounds", 0xFD4C28))
{
initParams.TimerId = 1;
// don't record sounds played from these sets.
REPLAY_ONLY(enableOnReplay = false;)
}
if( soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_CUSTOM_SOUNDSET", 0x832CAA0F) ||
soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_DEFAULT_SOUNDSET", 0x17BD10F1) ||
soundSetNameHash == ATSTRINGHASH("MP_LOBBY_SOUNDS", 0xFA4A5AA0) ||
soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_MP_COLLECTABLE_SOUNDS", 0x2B8F97E3) ||
soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_STANDARD_PICKUPS_NPC_SOUNDSET", 0x1D46A6A2) ||
soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_VEHICLE_PICKUPS_NPC_SOUNDSET", 0xF5E3A26A) ||
soundSetNameHash == ATSTRINGHASH("HUD_FRONTEND_WEAPONS_PICKUPS_NPC_SOUNDSET", 0xF35C567B) ||
soundSetNameHash == ATSTRINGHASH("RESPAWN_ONLINE_SOUNDSET", 0x71F56AB4) ||
soundSetNameHash == ATSTRINGHASH("MP_MISSION_COUNTDOWN_SOUNDSET", 0xC55C68A0) ||
soundSetNameHash == ATSTRINGHASH("HUD_FREEMODE_SOUNDSET", 0x54C522AD) ||
soundSetNameHash == ATSTRINGHASH("HUD_MINI_GAME_SOUNDSET", 0xD382DF7C) ||
soundSetNameHash == ATSTRINGHASH("HUD_AWARDS", 0x2A508F9C) ||
soundSetNameHash == ATSTRINGHASH("CELEBRATION_SOUNDSET", 0xE8F24AFD) ||
soundSetNameHash == ATSTRINGHASH("LONG_PLAYER_SWITCH_SOUNDS", 0x8DDBFC96) ||
soundSetNameHash == ATSTRINGHASH("PHONE_SOUNDSET_DEFAULT", 0x28C8633) ||
soundSetNameHash == ATSTRINGHASH("PHONE_SOUNDSET_PROLOGUE", 0x596B8EBB) ||
soundSetNameHash == ATSTRINGHASH("Phone_SoundSet_Franklin", 0x8A73028A) ||
soundSetNameHash == ATSTRINGHASH("Phone_SoundSet_Michael", 0x578FE4D7) ||
soundSetNameHash == ATSTRINGHASH("Phone_SoundSet_Trevor", 0xE52306DE) ||
soundSetNameHash == ATSTRINGHASH("Phone_SoundSet_Glasses_Cam", 0x10109BEB))
{
// don't record sounds played from these sets.
REPLAY_ONLY(enableOnReplay = false;)
}
}
sound = PlaySoundAndPopulateScriptSoundSlot(soundId, setName, soundName, &initParams, false, 0, NULL, NULL, false, enableOnReplay);
#if GTA_REPLAY
if(sound && enableOnReplay)
{
if(soundId != -1)
{
if(setName)
{
m_ScriptSounds[soundId].SoundSetHash = atStringHash(setName);
}
else
{
m_ScriptSounds[soundId].SoundSetHash = g_NullSoundHash;
}
m_ScriptSounds[soundId].SoundNameHash = atStringHash(soundName);
m_ScriptSounds[soundId].InitParams = initParams;
}
RecordScriptSlotSound(setName, soundName, &initParams, NULL, sound);
}
#endif
#if __BANK
if(sound)
{
Displayf("[SFX] PLAY_SOUND_FRONTEND - Sound:%s - Set:%s: Played successfully (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
else
{
Displayf("[SFX] PLAY_SOUND_FRONTEND - Sound:%s - Set:%s: Failed to play (%s)", soundName, setName? setName : "null", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
#endif
}
void audScriptAudioEntity::StopSound(s32 soundId)
{
//Assertf(soundId >=0, "Level design: you're calling StopSound with soundId -1" );
if (soundId<0)
{
return;
}
// Stop sounds started via the network from network function
if (m_ScriptSounds[soundId].NetworkId != audScriptSound::INVALID_NETWORK_ID && !m_ScriptSounds[soundId].OverNetwork)
{
StopSoundFromNetwork(m_ScriptSounds[soundId].NetworkId);
return;
}
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
// Make sure it's our sound ref to stop
naCErrorf(m_ScriptSounds[soundId].ScriptIndex == scriptIndex, "%s:Level design: Check you're calling STOP_SOUND() on the correct SoundID", CTheScripts::GetCurrentScriptName());
if (m_ScriptSounds[soundId].Sound && m_ScriptSounds[soundId].ScriptIndex == scriptIndex)
{
Displayf("[SFX] STOP_SOUND - Sound:%s - Id %u ScriptId: %u OverNetwork: %s", m_ScriptSounds[soundId].Sound->GetName(), soundId, m_ScriptSounds[soundId].ScriptIndex,m_ScriptSounds[soundId].OverNetwork ? "Yes" : "No");
// Stop the sound over the network.
if(m_ScriptSounds[soundId].OverNetwork && NetworkInterface::IsGameInProgress())
{
CStopSoundEvent::Trigger(static_cast<u8>(soundId));
}
m_ScriptSounds[soundId].Sound->StopAndForget();
m_ScriptSounds[soundId].EnvironmentGroup = NULL;
m_ScriptSounds[soundId].Entity = NULL;
m_ScriptOwnedEnvGroup.Clear(soundId);
#if GTA_REPLAY
m_ScriptSounds[soundId].SoundNameHash = g_NullSoundHash;
m_ScriptSounds[soundId].SoundSetHash = g_NullSoundHash;
m_ScriptSounds[soundId].ClearReplayVariableNameHashs();
#endif
}
}
bool audScriptAudioEntity::PlaySoundFromNetwork(u32 nNetworkId, CGameScriptId& scriptId, CEntity* entity, u32 setNameHash, u32 soundNameHash)
{
networkAudioDisplayf("PlaySoundFromNetwork nNetworkId[%u] entity[%p][%s] sound %u (set %u)",nNetworkId,entity,entity ? entity->GetModelName() : "", soundNameHash, setNameHash);
if(naVerifyf(entity, "CEntity* must be a valid"))
{
audSoundInitParams initParams;
initParams.Tracker = entity->GetPlaceableTracker();
bool ownsEnvGroup = false;
naEnvironmentGroup* environmentGroup = (naEnvironmentGroup*)entity->GetAudioEnvironmentGroup();
if(!environmentGroup)
{
environmentGroup = CreateScriptEnvironmentGroup(entity, entity->GetTransform().GetPosition());
ownsEnvGroup = true;
}
initParams.Position = VEC3V_TO_VECTOR3(entity->GetTransform().GetPosition());
return PlaySoundAndPopulateScriptSoundSlotFromNetwork(setNameHash, soundNameHash, &initParams, nNetworkId, scriptId, environmentGroup, entity, ownsEnvGroup);
}
return false;
}
bool audScriptAudioEntity::PlaySoundFromNetwork(u32 nNetworkId, CGameScriptId& scriptId, const Vector3* position, u32 setNameHash, u32 soundNameHash)
{
networkAudioDisplayf("PlaySoundFromNetwork nNetworkId[%u] sound %u (set %u)",nNetworkId, setNameHash, soundNameHash);
audSoundInitParams initParams;
initParams.Position = *position;
return PlaySoundAndPopulateScriptSoundSlotFromNetwork(setNameHash, soundNameHash, &initParams, nNetworkId, scriptId, NULL, NULL, false);
}
void audScriptAudioEntity::StopSoundFromNetwork(u32 nNetworkId)
{
networkAudioDisplayf("StopSoundFromNetwork nNetworkId[%u]",nNetworkId);
if (nNetworkId == audScriptSound::INVALID_NETWORK_ID)
return;
for (s32 i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
if (m_ScriptSounds[i].NetworkId == nNetworkId)
{
networkAudioDisplayf("StopSound: SoundID:%d, NetID:%d sound %u (set %u)", i, nNetworkId, m_ScriptSounds[i].SoundNameHash, m_ScriptSounds[i].SoundSetHash);
if (m_ScriptSounds[i].Sound)
{
m_ScriptSounds[i].Sound->StopAndForget();
}
ResetScriptSound(i);
}
}
}
u32 audScriptAudioEntity::GetNetworkIdFromSoundId(s32 soundId)
{
// bounds check
if(!naVerifyf(soundId >= 0 && soundId < AUD_MAX_SCRIPT_SOUNDS, "GetNetworkIdFromSoundId - Invalid soundId: %d", soundId))
return audScriptSound::INVALID_NETWORK_ID;
return m_ScriptSounds[soundId].NetworkId;
}
s32 audScriptAudioEntity::GetSoundIdFromNetworkId(u32 nNetworkId)
{
if (nNetworkId == audScriptSound::INVALID_NETWORK_ID)
{
return -1;
}
for (s32 i=0; i<AUD_MAX_SCRIPT_SOUNDS; i++)
{
if (m_ScriptSounds[i].NetworkId == nNetworkId)
{
return i;
}
}
return -1;
}
void audScriptAudioEntity::SetVariableOnSound(s32 soundId, const char* variableName, f32 variableValue)
{
// Assertf(soundId >=0, "Level design: you're calling StopSound with soundId -1" );
if (soundId<0)
{
return;
}
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return;
}
// Make sure it's our sound ref to do stuff to
naCErrorf(m_ScriptSounds[soundId].ScriptIndex == scriptIndex, "Level design: Check you're calling SET_VARIABLE on the correct SoundID");
if (m_ScriptSounds[soundId].Sound && m_ScriptSounds[soundId].ScriptIndex == scriptIndex)
{
m_ScriptSounds[soundId].Sound->SetVariableValueDownHierarchyFromName(variableName, variableValue);
#if GTA_REPLAY
u32 variableNameHash = atStringHash(variableName);
m_ScriptSounds[soundId].StoreSoundVariable(variableNameHash, variableValue);
#endif
}
}
void audScriptAudioEntity::SetVariableOnStream(const char * variableName, f32 variableValue)
{
if(m_StreamSound)
{
m_StreamSound->SetVariableValueDownHierarchyFromName(variableName, variableValue);
#if GTA_REPLAY
u32 variableNameHash = atStringHash(variableName);
StoreSoundVariable(variableNameHash, variableValue);
#endif
}
}
#if GTA_REPLAY
void audScriptAudioEntity::StoreSoundVariable(u32 variableNameHash, f32 value)
{
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
if(m_StreamVariableNameHash[i] == variableNameHash)
{
m_StreamVariableValue[i] = value;
return;
}
}
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
if(m_StreamVariableNameHash[i] == g_NullSoundHash)
{
m_StreamVariableNameHash[i] = variableNameHash;
m_StreamVariableValue[i] = value;
return;
}
}
}
#endif
bool audScriptAudioEntity::HasSoundFinished(s32 soundId)
{
// Assertf(soundId >=0, "Level design: you're calling StopSound with soundId -1" );
if (soundId<0)
{
return true;
}
s32 scriptIndex = AddScript();
if (!naVerifyf(scriptIndex>=0, "No valid scriptIndex, even with automated registering: %s; %d", CTheScripts::GetCurrentScriptName(), scriptIndex))
{
return true;
}
bool bCanStop = false;
// can stop local sounds with valid script index
bCanStop |= (m_ScriptSounds[soundId].ScriptIndex == scriptIndex);
// can stop network sounds not started by the local player
bCanStop |= (m_ScriptSounds[soundId].NetworkId != audScriptSound::INVALID_NETWORK_ID && !m_ScriptSounds[soundId].OverNetwork);
if (!naVerifyf(bCanStop, "Level design: Check you're calling HAS_SOUND_FINISHED() on the correct SoundID. This script is %s (%d) but SoundID %d is owned by script %s (%d). Network ID = %d, OverNetwork = %s", GetScriptName(scriptIndex), scriptIndex, soundId, GetScriptName(m_ScriptSounds[soundId].ScriptIndex), m_ScriptSounds[soundId].ScriptIndex, m_ScriptSounds[soundId].NetworkId, m_ScriptSounds[soundId].OverNetwork ? "true" : "false"))
{
return true;
}
// Return that it's finished if we no longer have a ptr to it, or it's in the waiting to be deleted state
if (!m_ScriptSounds[soundId].Sound)
{
return true;
}
if (m_ScriptSounds[soundId].Sound->GetPlayState() == AUD_SOUND_WAITING_TO_BE_DELETED)
{
return true;
}
// Else, we have a sound, and it's not done yet
return false;
}
void audScriptAudioEntity::ResetScriptSound(const s32 soundId)
{
m_ScriptSounds[soundId].Reset();
m_ScriptOwnedEnvGroup.Clear(soundId);
}
audScript * audScriptAudioEntity::GetScript(s32 index)
{
if(index >= 0)
{
if(audVerifyf(index < m_Scripts.GetMaxCount(), "Invalid script index passed to audScriptAudioEntity::GetScript! (%d >= %d)", index, m_Scripts.GetMaxCount()))
{
return &m_Scripts[index];
}
}
return NULL;
}
bool audScriptAudioEntity::StartScene(u32 sceneHash)
{
s32 index = AddScript();
if (!naVerifyf(index >= 0, "No valid script, even with automated registering: %s", CTheScripts::GetCurrentScriptName()))
{
return false;
}
audScript * script = GetScript(index);
return script->StartScene(sceneHash);
}
void audScriptAudioEntity::StopScene(u32 sceneHash)
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naAssertf(scriptThreadId, "Failed to get thread id");
s32 index = GetIndexFromScriptThreadId(scriptThreadId);
if(index < 0)
{
return;
}
audScript* script = GetScript(index);
if(script)
{
script->StopScene(sceneHash);
}
}
void audScriptAudioEntity::StopScenes()
{
scrThreadId scriptThreadId = CTheScripts::GetCurrentGtaScriptThread()->GetThreadId();
naAssertf(scriptThreadId, "Failed to get thread id");
s32 index = GetIndexFromScriptThreadId(scriptThreadId);
if(index < 0)
{
return;
}
audScript* script = GetScript(index);
if(script)
{
script->StopScenes();
}
}
void audScriptAudioEntity::SetSceneVariable(u32 sceneHash, u32 variableHash, f32 value)
{
audScript* script = GetScript(AddScript());
if(script)
{
script->SetSceneVariable(sceneHash, variableHash, value);
}
}
bool audScriptAudioEntity::SceneHasFinishedFading(u32 sceneHash)
{
audScript* script = GetScript(AddScript());
if(script)
{
return script->SceneHasFinishedFade(sceneHash);
}
return true;
}
void audScriptAudioEntity::SetSceneVariable(const char * sceneName, const char * variableName, f32 value)
{
SetSceneVariable(atStringHash(sceneName), atStringHash(variableName), value);
}
void audScriptAudioEntity::SetScriptVariable(const char * variableName, f32 value)
{
audScript* script = GetScript(AddScript());
if(script)
{
script->SetVariableValue(atStringHash(variableName), value);
}
}
bool audScriptAudioEntity::IsSceneActive(const u32 sceneHash)
{
audScript *script = GetScript(AddScript());
if(script)
{
if((script->GetScene(sceneHash)) != NULL)
{
return true;
}
}
return DYNAMICMIXER.GetActiveSceneFromHash(sceneHash) != NULL;
}
#if NA_POLICESCANNER_ENABLED
void audScriptAudioEntity::SetPoliceScannerAudioScene(const char* sceneName)
{
if(!Verifyf(m_PoliceScannerSceneHash == 0 && m_PoliceScannerSceneScriptIndex == -1, "Trying to set police scanner audio scene when on has already been set."))
return;
u32 sceneHash = atStringHash(sceneName);
if(StartScene(sceneHash))
{
s32 index = AddScript();
if (!naVerifyf(index >= 0, "No valid script, even with automated registering: %s", CTheScripts::GetCurrentScriptName()))
{
StopScene(sceneHash);
return;
}
audScript* script = GetScript(index);
if(Verifyf(script, "Failed to grab script pointer in spite of a valid index."))
{
audScene* scene = script->GetScene(sceneHash);
if(Verifyf(scene, "Failed to grab audio scene despite successful creation. Something is very wrong."))
{
scene->SetVariableValue("apply", 0.0f);
m_PoliceScannerSceneHash = sceneHash;
m_PoliceScannerSceneScriptIndex = index;
}
else
{
StopScene(sceneHash);
}
}
else
{
StopScene(sceneHash);
}
}
}
void audScriptAudioEntity::StopAndRemovePoliceScannerAudioScene()
{
StopScene(m_PoliceScannerSceneHash);
ResetPoliceScannerVariables();
}
void audScriptAudioEntity::ResetPoliceScannerVariables()
{
m_PoliceScannerSceneHash = 0;
m_PoliceScannerSceneScriptIndex = -1;
}
void audScriptAudioEntity::ApplyPoliceScannerAudioScene()
{
if(m_PoliceScannerSceneHash == 0 || m_PoliceScannerSceneScriptIndex == -1 || m_PoliceScannerIsApplied)
return;
audScript* script = GetScript(m_PoliceScannerSceneScriptIndex);
if(Verifyf(script, "Failed to grab script pointer in spite of a valid index."))
{
audScene* scene = script->GetScene(m_PoliceScannerSceneHash);
if(Verifyf(scene, "Failed to grab audio scene despite successful creation. Something is very wrong."))
{
scene->SetVariableValue("apply", g_AudioScannerManager.GetPoliceScannerApplyValue());
m_PoliceScannerIsApplied = true;
}
}
}
void audScriptAudioEntity::DeactivatePoliceScannerAudioScene()
{
if(m_PoliceScannerSceneHash == 0 || m_PoliceScannerSceneScriptIndex == -1)
return;
audScript* script = GetScript(m_PoliceScannerSceneScriptIndex);
if(Verifyf(script, "Failed to grab script pointer in spite of a valid index."))
{
audScene* scene = script->GetScene(m_PoliceScannerSceneHash);
if(Verifyf(scene, "Failed to grab audio scene despite successful creation. Something is very wrong."))
{
scene->SetVariableValue("apply", SMALL_FLOAT);
m_PoliceScannerIsApplied = false;
}
}
}
#endif
// Custom code handlers
void audScriptAudioEntity::GetSpeechForEmergencyServiceCall(char* RetString)
{
CPopZone *pZone;
Vector3 playerCoors = CGameWorld::FindLocalPlayerCoors();
pZone = CPopZones::FindSmallestForPosition(&playerCoors, ZONECAT_ANY, ZONETYPE_SP);
if(pZone)
{
strncpy(RetString, pZone->m_associatedTextId.TryGetCStr(), 8);
}
else
{
strcpy(RetString, "");
}
}
void audScriptAudioEntity::EnableChaseAudio(bool enable)
{
audScript *script = GetScript();
if(naVerifyf(script, "Failed to register script for audio"))
{
script->SetAudioFlag(audScriptAudioFlags::BoostPlayerCarVolume, enable);
script->SetAudioFlag(audScriptAudioFlags::AggressiveHorns, enable);
script->SetAudioFlag(audScriptAudioFlags::CarsStartFirstTime, enable);;
}
}
void audScriptAudioEntity::EnableAggressiveHorns(bool enable)
{
audScript *script = GetScript();
if(naVerifyf(script, "Failed to register script for audio"))
{
script->SetAudioFlag(audScriptAudioFlags::AggressiveHorns, enable);
}
}
void audScriptAudioEntity::PlayPreviewRingtone(const u32 ringtoneId)
{
StopPreviewRingtone();
char soundName[64];
formatf(soundName, sizeof(soundName)-1, "MOBILE_PHONE_RING_%02d", ringtoneId);
audSoundInitParams initParams;
initParams.WaveSlot = audWaveSlot::FindWaveSlot(ATSTRINGHASH("PLAYER_RINGTONE", 0x0d2645e26));
initParams.Pan = 0;
initParams.AllowLoad = true;
initParams.EffectRoute = EFFECT_ROUTE_FRONT_END;
initParams.PrepareTimeLimit = 3500;
CreateAndPlaySound_Persistent(soundName, &m_PreviewRingtoneSound, &initParams);
}
void audScriptAudioEntity::StopPreviewRingtone()
{
if(m_PreviewRingtoneSound)
{
m_PreviewRingtoneSound->StopAndForget();
}
}
void audScriptAudioEntity::DontAbortCarConversations(bool allowUpsideDown, bool allowOnFire, bool allowDrowning)
{
s32 scriptIndex = AddScript();
DontAbortCarConversationsWithScriptId(allowUpsideDown, allowOnFire, allowDrowning, scriptIndex);
}
void audScriptAudioEntity::DontAbortCarConversationsWithScriptId(bool allowUpsideDown, bool allowOnFire, bool allowDrowning, s32 scriptIndex)
{
naConvDisplayf("DontAbortCarConversations() allowUpsideDown: %s allowOnFire: %s allowDrowning: %s", allowUpsideDown ? "True" : "False", allowOnFire ? "True" : "False", allowDrowning? "True" : "False");
if(allowOnFire || allowUpsideDown || allowDrowning)
{
if (scriptIndex < 0 || m_ScriptInChargeOfAudio != -1)
{
naErrorf("Couldn't set persistent car conversations...has another script already called this or have we failed to get an audScript? (scriptIndex: %d, scriptInchargeOfAudio: %d", scriptIndex, m_ScriptInChargeOfAudio);
return;
}
m_ScriptInChargeOfAudio = scriptIndex;
g_AllowUpsideDownConversations = allowUpsideDown;
g_AllowVehicleOnFireConversations = allowOnFire;
g_AllowVehicleDrowningConversations = allowDrowning;
}
else
{
m_ScriptInChargeOfAudio = -1;
BANK_ONLY(m_CurrentScriptName = "");
g_AllowUpsideDownConversations = allowUpsideDown;
g_AllowVehicleOnFireConversations = allowOnFire;
g_AllowVehicleDrowningConversations = allowDrowning;
}
}
void audScriptAudioEntity::MuteGameWorldAndPositionedRadioForTV(bool mute)
{
audNorthAudioEngine::MuteGameWorldAndPositionedRadioForTv(mute);
}
bool audScriptAudioEntity::RequestScriptWeaponAudio(const char * itemName)
{
s32 scriptIndex = AddScript();
audScript * script = GetScript(scriptIndex);
if(!script)
{
return false;
}
return script->RequestScriptWeaponAudio(itemName);
}
void audScriptAudioEntity::ReleaseScriptWeaponAudio()
{
s32 scriptIndex = AddScript();
audScript * script = GetScript(scriptIndex);
if(!script)
{
return;
}
script->ReleaseScriptWeaponAudio();
}
bool audScriptAudioEntity::RequestScriptVehicleAudio(u32 vehicleModelName)
{
s32 scriptIndex = AddScript();
audScript * script = GetScript(scriptIndex);
if(!script)
{
return false;
}
return script->RequestScriptVehicleAudio(vehicleModelName);
}
#if !__FINAL
void audScriptAudioEntity::DebugScriptStream(s32 scriptIndex)
{
GtaThread* currentThread = static_cast<GtaThread*>(scrThread::GetThread(m_Scripts[scriptIndex].ScriptThreadId));
const char * currentScriptName = currentThread ? currentThread->GetScriptName() : "none";
GtaThread* streamThread = static_cast<GtaThread*>(scrThread::GetThread(m_Scripts[sm_ScriptInChargeOfTheStream].ScriptThreadId));
const char *streamScriptName = streamThread ? streamThread->GetScriptName() : "none";
naWarningf("Attempting to preload as stream from script %s but another script (%s) is in charge of script stream", currentScriptName, streamScriptName);
}
#endif
bool audScriptAudioEntity::PreloadStream(const char *streamName, const u32 startOffset, const char * setName)
{
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Preload stream call, state: %s, frame : %u",audScriptAudioEntity::GetStreamStateName(m_ScriptStreamState) , fwTimer::GetFrameCount());
}
#endif
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
safecpy(sm_CurrentStreamName, streamName);
safecpy(sm_CurrentSetName, setName);
sm_CurrentStartOffset = startOffset;
}
#endif
s32 scriptIndex = AddScript();
if((sm_ScriptInChargeOfTheStream != -1 && sm_ScriptInChargeOfTheStream != scriptIndex))
{
#if !__FINAL
DebugScriptStream(scriptIndex);
#endif
return true;
}
audScript *script = GetScript(scriptIndex);
if(!script)
return false;
audScriptAudioEntity::sm_ScriptInChargeOfTheStream = scriptIndex;
return PreloadStreamInternal(streamName, startOffset, setName);
}
bool audScriptAudioEntity::PreloadStreamInternal(const char *streamName, const u32 startOffsetMs, const char *setName)
{
naCErrorf(m_ScriptStreamState != AUD_SCRIPT_STREAM_PLAYING, "Shouldn't try to preload while script stream state is set to playing");
audMetadataRef streamDataRef;
if(setName)
{
audSoundSet soundSet;
soundSet.Init(setName);
#if GTA_REPLAY
if(CReplayMgr::IsPlaying())
{
char buf[64] = {0};
formatf(buf, "%s_REPLAY", streamName);
streamDataRef = soundSet.Find(buf);
}
#endif
if(!streamDataRef.IsValid())
{
streamDataRef = soundSet.Find(streamName);
}
}
else
{
#if GTA_REPLAY
if(CReplayMgr::IsPlaying())
{
char buf[64] = {0};
formatf(buf, "%s_REPLAY", streamName);
streamDataRef = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataRefFromHash(atStringHash(buf));
}
#endif
if(!streamDataRef.IsValid())
{
streamDataRef = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataRefFromHash(atStringHash(streamName));
}
}
if(m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARED && streamDataRef == m_LoadedStreamRef)
{
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Stream %s successfully prepared",SOUNDFACTORY.GetMetadataManager().GetObjectName(streamDataRef));
}
#endif
return true;
}
if(m_ScriptStreamState == AUD_SCRIPT_STREAM_IDLE)
{
// grab a slot
if(m_StreamSlot == NULL)
{
audStreamClientSettings settings;
settings.priority = audStreamSlot::STREAM_PRIORITY_SCRIPT;
settings.stopCallback = &OnStopCallback;
settings.hasStoppedCallback = &HasStoppedCallback;
settings.userData = 0;
m_StreamSlot = audStreamSlot::AllocateSlot(&settings);
}
if(m_StreamSlot && (m_StreamSlot->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE))
{
audSoundInitParams initParams;
initParams.StartOffset = startOffsetMs;
initParams.IsStartOffsetPercentage = false;
Assign(initParams.BucketId, audNorthAudioEngine::GetBucketIdForStreamingSounds());
CreateSound_PersistentReference(streamDataRef, &m_StreamSound, &initParams);
if(!m_StreamSound)
{
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Failed to create the sound: %s",SOUNDFACTORY.GetMetadataManager().GetObjectName(streamDataRef));
}
#endif
return false;
}
else
{
m_ScriptStreamState = AUD_SCRIPT_STREAM_PREPARING;
}
}
}
if(m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARING)
{
naAssertf(m_StreamSlot, "About to access a null ptr....");
naAssertf(m_StreamSound, "About to access a null ptr....");
if(m_StreamSound)
{
audPrepareState ret = m_StreamSound->Prepare(m_StreamSlot->GetWaveSlot(), true);
if(ret == AUD_PREPARED)
{
naDisplayf("Scripted stream %s prepared", streamName);
m_LoadedStreamRef = streamDataRef;
m_ScriptStreamState = AUD_SCRIPT_STREAM_PREPARED;
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Stream %s successfully prepared",SOUNDFACTORY.GetMetadataManager().GetObjectName(streamDataRef));
}
#endif
return true;
}
}
else
{
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Preparing a stream with no sound: %s going into IDLE",SOUNDFACTORY.GetMetadataManager().GetObjectName(streamDataRef));
}
#endif
m_ScriptStreamState = AUD_SCRIPT_STREAM_IDLE;
}
}
#if GTA_REPLAY
//During replay playback when scrubbing around the timeline its possible for the streamstate to get stuck
//in the prepared state, if we're in this state and trying to preload a different stream then stop the current one.
if(CReplayMgr::IsEditModeActive())
{
if( m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARED && streamDataRef != m_LoadedStreamRef )
{
StopStream();
return false;
}
}
#endif //GTA_REPLAY
return false;
}
void audScriptAudioEntity::PlayStreamFromEntity(CEntity *entity)
{
s32 scriptIndex = AddScript();
if((sm_ScriptInChargeOfTheStream != -1 && sm_ScriptInChargeOfTheStream != scriptIndex))
{
#if !__FINAL
DebugScriptStream(scriptIndex);
#endif
return;
}
audScript *script = GetScript(scriptIndex);
if(!script)
return;
PlayStreamFromEntityInternal(entity);
}
void audScriptAudioEntity::PlayStreamFrontend()
{
s32 scriptIndex = AddScript();
if((sm_ScriptInChargeOfTheStream != -1 && sm_ScriptInChargeOfTheStream != scriptIndex))
{
#if !__FINAL
DebugScriptStream(scriptIndex);
#endif
return;
}
audScript *script = GetScript(scriptIndex);
if(!script)
return;
PlayStreamFrontendInternal();
}
void audScriptAudioEntity::PlayStreamFromEntityInternal(CEntity *entity)
{
if(naVerifyf(entity, "CEntity* must be valid, can't play stream"))
{
if(naVerifyf((m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARED), "Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState))
{
naAssertf(m_StreamSound, "About to access a null ptr...");
naAssertf(m_StreamSlot, "About to access a null ptr...");
if(!m_StreamSlot && g_DebugPlayingScriptSounds)
{
naWarningf("Must have a stream slot to play stream");
}
m_StreamTrackerEntity = entity;
m_StreamEnvironmentGroup = (naEnvironmentGroup*)entity->GetAudioEnvironmentGroup();
if(!m_StreamEnvironmentGroup)
{
m_StreamEnvironmentGroup = CreateScriptEnvironmentGroup(entity, entity->GetTransform().GetPosition());
}
if(m_StreamSound)
{
m_StreamSound->GetRequestedSettings()->SetEnvironmentGroup(m_StreamEnvironmentGroup);
m_StreamSound->SetRequestedPosition(m_StreamTrackerEntity->GetTransform().GetPosition());
m_StreamSound->Play();
}
m_StreamType = AUD_PLAYSTREAM_ENTITY;
m_ScriptStreamState = AUD_SCRIPT_STREAM_PLAYING;
m_LoadedStreamRef.SetInvalid();
#if GTA_REPLAY
m_DisableReplayScriptStreamRecording = IsFlagSet(audScriptAudioFlags::DisableReplayScriptStreamRecording);
if(!m_DisableReplayScriptStreamRecording && m_StreamSound)
{
REPLAY_ONLY(CReplayMgr::ReplayRecordPlayStreamFromEntity(entity, m_StreamSound, sm_CurrentStreamName, sm_CurrentStartOffset, sm_CurrentSetName);)
}
#endif
#if __USEDEBUGAUDIO
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Playing stream: %s from entity",SOUNDFACTORY.GetMetadataManager().GetObjectNameFromNameTableOffset(m_StreamSound->GetBaseMetadata()->NameTableOffset));
}
#endif
}
else if(g_DebugPlayingScriptSounds)
{
naWarningf("Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState);
}
}
}
void audScriptAudioEntity::PlayStreamFrontendInternal()
{
if(naVerifyf((m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARED), "Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState))
{
naAssertf(m_StreamSound, "Must have a stream sound to play: about to access a null ptr....");
if(!m_StreamSound && g_DebugPlayingScriptSounds)
{
Displayf("Must have a stream sound to play: about to access a null ptr....");
}
naAssertf(m_StreamSlot, "Must have a stream slot");
if(!m_StreamSlot && g_DebugPlayingScriptSounds)
{
naDisplayf("Must have a stream slot to play stream");
}
if(m_StreamTrackerEntity)
{
m_StreamTrackerEntity = NULL;
}
m_StreamEnvironmentGroup = NULL;
if(m_StreamSound)
{
m_StreamSound->Play();
}
m_StreamType = AUD_PLAYSTREAM_FRONTEND;
m_ScriptStreamState = AUD_SCRIPT_STREAM_PLAYING;
m_LoadedStreamRef.SetInvalid();
#if GTA_REPLAY
m_DisableReplayScriptStreamRecording = IsFlagSet(audScriptAudioFlags::DisableReplayScriptStreamRecording) || IsFlagSet(audScriptAudioFlags::DisableReplayScriptFrontendStreamRecording);
if(!m_DisableReplayScriptStreamRecording && m_StreamSound)
{
REPLAY_ONLY(CReplayMgr::ReplayRecordPlayStreamFrontend(m_StreamSound, sm_CurrentStreamName, sm_CurrentStartOffset, sm_CurrentSetName);)
}
#endif
#if __USEDEBUGAUDIO
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Playing stream: %s frontend",SOUNDFACTORY.GetMetadataManager().GetObjectNameFromNameTableOffset(m_StreamSound->GetBaseMetadata()->NameTableOffset));
}
#endif
}
else if(g_DebugPlayingScriptSounds)
{
Displayf("Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState);
}
}
const u32 audScriptAudioEntity::GetStreamPlayTime() const
{
if(m_StreamSound && m_ScriptStreamState == AUD_SCRIPT_STREAM_PLAYING)
{
#if __USEDEBUGAUDIO
if(naVerifyf(m_StreamSound->GetSoundTypeID() == StreamingSound::TYPE_ID,"Script stream %s, with a parent sound that it's not a stream sound. Please bug default audio.",g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_StreamSound->GetBaseMetadata()->NameTableOffset)))
#endif
{
return ((audStreamingSound*)m_StreamSound)->GetCurrentPlayTimeOfWave();
}
}
return 0;
}
bool audScriptAudioEntity::IsPlayingStream()
{
bool isPlayingStream = false;
if(m_StreamSound && m_ScriptStreamState == AUD_SCRIPT_STREAM_PLAYING)
{
isPlayingStream = true;
}
return isPlayingStream;
}
void audScriptAudioEntity::PlayStreamFromPosition(const Vector3 &pos)
{
s32 scriptIndex = AddScript();
if((sm_ScriptInChargeOfTheStream != -1 && sm_ScriptInChargeOfTheStream != scriptIndex))
{
#if !__FINAL
DebugScriptStream(scriptIndex);
#endif
return;
}
audScript *script = GetScript(scriptIndex);
if(!script)
return;
PlayStreamFromPositionInternal(pos);
}
void audScriptAudioEntity::PlayStreamFromPositionInternal(const Vector3 &pos)
{
if(naVerifyf((m_ScriptStreamState == AUD_SCRIPT_STREAM_PREPARED), "Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState))
{
naAssertf(m_StreamSound, "Must have a stream sound to play: about to access a null ptr....");
if(!m_StreamSound && g_DebugPlayingScriptSounds)
{
Displayf("Must have a stream sound to play: about to access a null ptr....");
}
naAssertf(m_StreamSlot, "Must have a stream slot");
if(!m_StreamSlot && g_DebugPlayingScriptSounds)
{
naDisplayf("Must have a stream slot to play stream");
}
if(m_StreamTrackerEntity)
{
m_StreamTrackerEntity = NULL;
}
m_StreamEnvironmentGroup = NULL;
m_StreamPosition.Set(pos);
if(m_StreamSound)
{
m_StreamSound->SetRequestedPosition(VECTOR3_TO_VEC3V(pos));
m_StreamSound->Play();
}
m_StreamType = AUD_PLAYSTREAM_POSITION;
m_ScriptStreamState = AUD_SCRIPT_STREAM_PLAYING;
m_LoadedStreamRef.SetInvalid();
#if GTA_REPLAY
m_DisableReplayScriptStreamRecording = IsFlagSet(audScriptAudioFlags::DisableReplayScriptStreamRecording);
if(!m_DisableReplayScriptStreamRecording && m_StreamSound)
{
REPLAY_ONLY(CReplayMgr::ReplayRecordPlayStreamFromPosition(pos, m_StreamSound, sm_CurrentStreamName, sm_CurrentStartOffset, sm_CurrentSetName);)
}
#endif
#if __USEDEBUGAUDIO
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] Playing stream: %s from position",SOUNDFACTORY.GetMetadataManager().GetObjectNameFromNameTableOffset(m_StreamSound->GetBaseMetadata()->NameTableOffset));
}
#endif
}
else if(g_DebugPlayingScriptSounds)
{
Displayf("Can't play a stream that hasn't been prepared, m_ScriptStreamState: %d", m_ScriptStreamState);
}
}
void audScriptAudioEntity::StopStream()
{
if(m_StreamSound)
{
m_StreamSound->StopAndForget();
}
if(m_StreamSlot)
{
m_StreamSlot->Free();
m_StreamSlot = NULL;
}
if(m_StreamTrackerEntity)
{
m_StreamTrackerEntity = NULL;
}
m_StreamType = AUD_PLAYSTREAM_UNKNOWN;
m_StreamEnvironmentGroup = NULL;
m_ScriptStreamState = AUD_SCRIPT_STREAM_IDLE;
m_LoadedStreamRef.SetInvalid();
audScriptAudioEntity::sm_ScriptInChargeOfTheStream = -1;
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] StopStream called frame %u",fwTimer::GetFrameCount());
}
#endif
#if GTA_REPLAY
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
m_StreamVariableNameHash[i] = g_NullSoundHash;
m_StreamVariableValue[i] = 0.0f;
}
CReplayMgr::RecordStopStream();
#endif
}
bool audScriptAudioEntity::OnStopCallback(u32 UNUSED_PARAM(userData))
{
//We are losing our stream slot, so stop the scripted stream.
if(g_ScriptAudioEntity.m_StreamSound)
{
g_ScriptAudioEntity.m_StreamSound->StopAndForget();
}
g_ScriptAudioEntity.m_StreamType = AUD_PLAYSTREAM_UNKNOWN;
#if GTA_REPLAY
for(int i=0; i<AUD_MAX_REPLAY_SCRIPT_SOUND_VARIABLES; i++)
{
g_ScriptAudioEntity.m_StreamVariableValue[i] = 0.0f;
g_ScriptAudioEntity.m_StreamVariableNameHash[i] = g_NullSoundHash;
}
#endif
#if __BANK
if(g_ScriptedStreamSpew)
{
audDebugf1("[audScriptedStream] OnStopCallback which mean we lost our stream slot, stop the sound and go into idle ");
}
#endif
g_ScriptAudioEntity.m_ScriptStreamState = AUD_SCRIPT_STREAM_IDLE;
g_ScriptAudioEntity.m_LoadedStreamRef.SetInvalid();
g_ScriptAudioEntity.m_StreamEnvironmentGroup = NULL;
return true;
}
bool audScriptAudioEntity::HasStoppedCallback(u32 UNUSED_PARAM(userData))
{
bool hasStopped = true;
audStreamSlot *streamSlot = g_ScriptAudioEntity.m_StreamSlot;
if(streamSlot)
{
//Check if we are still loading or playing from our allocated wave slot
audWaveSlot *waveSlot = streamSlot->GetWaveSlot();
if(waveSlot && (waveSlot->GetIsLoading() || (waveSlot->GetReferenceCount() > 0)))
{
hasStopped = false;
}
}
return hasStopped;
}
void audScriptAudioEntity::StopPedSpeaking(CPed *ped, bool shouldDisable )
{
// for now, just flag them to be completely silent
if (ped && ped->GetSpeechAudioEntity())
{
ped->GetSpeechAudioEntity()->DisableSpeaking(shouldDisable );
}
}
void audScriptAudioEntity::BlockAllSpeechFromPed(CPed *ped, bool shouldDisable, bool shouldSuppressOutgoingNetworkSpeech)
{
if (ped && ped->GetSpeechAudioEntity())
{
ped->GetSpeechAudioEntity()->BlockAllSpeechFromPed(shouldDisable, shouldSuppressOutgoingNetworkSpeech);
}
}
void audScriptAudioEntity::StopPedSpeakingSynced(CPed *ped, bool shouldDisable)
{
// for now, just flag them to be completely silent
if (ped && ped->GetSpeechAudioEntity())
{
ped->GetSpeechAudioEntity()->DisableSpeakingSynced(shouldDisable );
}
}
void audScriptAudioEntity::StopPedPainAudio(CPed *ped, bool shouldDisable )
{
if (ped && ped->GetSpeechAudioEntity())
{
ped->GetSpeechAudioEntity()->DisablePainAudio(shouldDisable );
}
}
void audScriptAudioEntity::MobilePreRingHandler(u32 UNUSED_PARAM(hash), void *UNUSED_PARAM(context))
{
g_ScriptAudioEntity.PlayMobilePreRing(g_MobilePreRingHash, 0.3f);
}
void audScriptAudioEntity::PlayMobileGetSignal(f32 probability)
{
g_ScriptAudioEntity.PlayMobilePreRing(g_MobileGetSignalHash, probability);
}
void audScriptAudioEntity::PlayMobilePreRing(u32 NA_RADIO_ENABLED_ONLY(mobileInterferenceHash), f32 NA_RADIO_ENABLED_ONLY(probability))
{
#if NA_RADIO_ENABLED
u32 timeNow = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
// Purposefully leave a fair bit of a delay between triggers - don't want it constantly happening when popping in and out of tunnels
if(timeNow - m_LastMobileInterferenceTime > 10000 BANK_ONLY(|| g_ForceMobileInterference))
{
// If the radio's playing frontend, and we're not already playing a prering, play one now
if(g_RadioAudioEntity.IsVehicleRadioOn() && !m_MobileInterferenceSound && (audEngineUtil::ResolveProbability(probability) BANK_ONLY(|| g_ForceMobileInterference)))
{
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if(playerVehicle && playerVehicle->GetVehicleAudioEntity()->GetAudioVehicleType() == AUD_VEHICLE_CAR)
{
CarAudioSettings* settings = ((audCarAudioEntity*)playerVehicle->GetVehicleAudioEntity())->GetCarAudioSettings();
if(settings && AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_CARAUDIOSETTINGS_MOBILECAUSESRADIOINTERFERENCE) == AUD_TRISTATE_TRUE BANK_ONLY(|| g_ForceMobileInterference))
{
CreateAndPlaySound_Persistent(mobileInterferenceHash, &m_MobileInterferenceSound);
}
}
}
m_LastMobileInterferenceTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
}
#endif
}
naEnvironmentGroup* audScriptAudioEntity::CreateScriptEnvironmentGroup(const CEntity* entity, const Vec3V& pos) const
{
naEnvironmentGroup* environmentGroup = naEnvironmentGroup::Allocate("Script");
if(environmentGroup) // Need to check for NULL because we could run out of space in the naEnvironmentGroup pool
{
// NULL entity; will be deleted when the sound is deleted
environmentGroup->Init(NULL, 20);
// Force a source environment update giving it our entity and interior information, since we don't normally have one
if(entity)
{
environmentGroup->ForceSourceEnvironmentUpdate(entity);
}
// We only create script environmentGroups without an entity if we know they're outside, so set that info on the group
else
{
environmentGroup->SetInteriorInfoWithInteriorInstance(NULL, INTLOC_ROOMINDEX_LIMBO);
}
environmentGroup->SetSource(pos);
environmentGroup->SetUsePortalOcclusion(true);
environmentGroup->SetMaxPathDepth(audNorthAudioEngine::GetOcclusionManager()->GetMaxOcclusionPathDepth());
}
return environmentGroup;
}
#if GTA_REPLAY
bool audScriptAudioEntity::HasAlreadyPlayed(u32 voiceNameHash, u32 contextNameHash, s32 variationNumber)
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
if( sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash == contextNameHash &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber == variationNumber &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash == voiceNameHash)
{
return true;
}
}
return false;
}
void audScriptAudioEntity::AddToPlayedList(u32 voiceNameHash, u32 contextNameHash, s32 variationNumber)
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
if(sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber == -1)
{
//Displayf("AddToPlayedList %u, %u, %u", CReplayMgr::GetCurrentTimeRelativeMs(), contextNameHash, voiceNameHash);
sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash = contextNameHash;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber = variationNumber;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash = voiceNameHash;
sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1);
return;
}
}
}
void audScriptAudioEntity::UpdateTimeStamp(u32 voiceNameHash, u32 contextNameHash, s32 variationNumber)
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
if(sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash == voiceNameHash &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash == contextNameHash &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber == variationNumber)
{
sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1);
return;
}
}
}
void audScriptAudioEntity::RemoveFromAreadyPlayedList(u32 voiceNameHash, u32 contextNameHash, s32 variationNumber)
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
if( sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash == contextNameHash &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber == variationNumber &&
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash == voiceNameHash)
{
//Displayf("RemoveFromAreadyPlayedList %u, %u, %u", CReplayMgr::GetCurrentTimeRelativeMs(), contextNameHash, voiceNameHash);
sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber = -1;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp = 0;
}
}
}
void audScriptAudioEntity::ClearPlayedList()
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber = -1;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp = 0;
}
StopAllReplayScriptedSpeech();
}
void audScriptAudioEntity::StopAllReplayScriptedSpeech()
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_SLOTS; i++)
{
if(sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound)
{
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound->StopAndForget();
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound = NULL;
}
}
g_SpeechManager.FreeAllScriptedSpeechSlots();
}
ePreloadResult audScriptAudioEntity::PreloadReplayScriptedSpeech(u32 voiceNameHash, u32 contextHash, u32 variationNumber, s32 UNUSED_PARAM(waveSlotIndex ))
{
//Displayf("PreloadReplayScriptedSpeech time:%d - %u - %d - %d", CReplayMgr::GetCurrentTimeRelativeMs(), m_uContextNameHash, m_sWaveSlotIndex, m_sPreDelay);
//Displayf("PreloadReplayScriptedSpeech time:%u - %u - %d - evenet:%u, %u, %u game:%u", CReplayMgr::GetCurrentTimeRelativeMs(), m_uContextNameHash, m_sWaveSlotIndex,
// eventInfo.GetReplayTime(), eventInfo.GetPreloadOffsetTime(), eventInfo.GetIsFirstFrame(),
// GetGameTime());
if(CReplayMgr::IsCursorFastForwarding() || CReplayMgr::IsCursorRewinding() || CReplayMgr::IsScrubbing())
{
return PRELOAD_SUCCESSFUL;
}
bool hasAlreadyPlayed = g_ScriptAudioEntity.HasAlreadyPlayed(voiceNameHash, contextHash, variationNumber);
if(hasAlreadyPlayed)
{
return PRELOAD_SUCCESSFUL;
}
s32 waveSlot = g_ScriptAudioEntity.LookupReplayPreloadScriptedSpeechSlot(voiceNameHash, contextHash, (s32)variationNumber);
if( waveSlot == -1)
{
waveSlot = g_SpeechManager.GetScriptedSpeechSlot(NULL);
}
if(waveSlot == -1)
{
return PRELOAD_SUCCESSFUL;
}
s32 sSpeechSoundIndex = g_ScriptAudioEntity.GetFreeReplayScriptedSpeechSlot(voiceNameHash, contextHash, (s32)variationNumber, waveSlot);
if(sSpeechSoundIndex < 0)
{
return PRELOAD_FAIL;
}
s32 sBaseWaveSlotIndex = audWaveSlot::GetWaveSlotIndex(audWaveSlot::FindWaveSlot("SCRIPT_SPEECH_0"));
waveSlot += sBaseWaveSlotIndex;
if(g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound == NULL && !hasAlreadyPlayed)
{
audSoundInitParams initParams;
g_ScriptAudioEntity.CreateSound_PersistentReference( "PRELOAD_SPEECH_SOUND", (audSound**)&g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound, &initParams );
if( g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound )
{
bool bSuccess = g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound->InitSpeech( voiceNameHash, contextHash, variationNumber );
if( !bSuccess )
{
// ditch this sound, as it failed to find a variation
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound->StopAndForget();
return PRELOAD_SUCCESSFUL;
}
audWaveSlot* pWaveSlot = audWaveSlot::GetWaveSlotFromIndex(waveSlot);
if( pWaveSlot )
{
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].WaveSlot = pWaveSlot;
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].WaveSlotIndex = waveSlot;
}
}
}
if( g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound )
{
audWaveSlot* pWaveSlot = audWaveSlot::GetWaveSlotFromIndex(waveSlot);
if(pWaveSlot)
{
audPrepareState prepareState = g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound->Prepare(pWaveSlot, true, naWaveLoadPriority::Speech);
if( prepareState == AUD_PREPARE_FAILED )
{
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ScriptedSpeechSound->StopAndForget();
}
else
{
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].VoiceNameHash = voiceNameHash;
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].ContextNameHash = contextHash;
g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[sSpeechSoundIndex].VariationNumber = (s32)variationNumber;
if(prepareState == AUD_PREPARING)
{
return PRELOAD_FAIL;
}
}
}
}
return PRELOAD_SUCCESSFUL;
}
int audScriptAudioEntity::LookupReplayPreloadScriptedSpeechSlot( u32 uVoiceNameHash, u32 uContextNameHash, s32 sVariationNumber)
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_SLOTS; i++)
{
if( sm_ReplayScriptedSpeechSlot[i].VariationNumber == sVariationNumber &&
sm_ReplayScriptedSpeechSlot[i].ContextNameHash == uContextNameHash &&
sm_ReplayScriptedSpeechSlot[i].VoiceNameHash == uVoiceNameHash )
{
return i;
}
}
return -1;
}
int audScriptAudioEntity::GetFreeReplayScriptedSpeechSlot( u32 uVoiceNameHash, u32 uContextNameHash, s32 sVariationNumber, s32 speechSlotIndex)
{
s32 sBaseWaveSlotIndex = audWaveSlot::GetWaveSlotIndex(audWaveSlot::FindWaveSlot("SCRIPT_SPEECH_0"));
s32 i = speechSlotIndex;
// check and see if we are already preloading
if( sm_ReplayScriptedSpeechSlot[i].VariationNumber == sVariationNumber &&
sm_ReplayScriptedSpeechSlot[i].ContextNameHash == uContextNameHash &&
sm_ReplayScriptedSpeechSlot[i].VoiceNameHash == uVoiceNameHash &&
sm_ReplayScriptedSpeechSlot[i].WaveSlot == audWaveSlot::GetWaveSlotFromIndex(sBaseWaveSlotIndex + speechSlotIndex) )
{
return i;
}
else
{
if(g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound)
{
return -1;
}
}
return i;
}
void audScriptAudioEntity::UpdateReplayScriptedSpeechSlots()
{
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_SLOTS; i++)
{
if(sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound)
{
if(sm_ReplayScriptedSpeechSlot[i].WaveSlot)
{
audPrepareState state = sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound->Prepare(sm_ReplayScriptedSpeechSlot[i].WaveSlot, true, naWaveLoadPriority::Speech);
if(state == AUD_PREPARE_FAILED || (state == AUD_PREPARED && g_ScriptAudioEntity.HasAlreadyPlayed(sm_ReplayScriptedSpeechSlot[i].VoiceNameHash, sm_ReplayScriptedSpeechSlot[i].ContextNameHash, sm_ReplayScriptedSpeechSlot[i].VariationNumber)))
{
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound->StopAndForget();
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound = NULL;
}
}
}
}
u32 currentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1);
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_PLAYED_SLOTS; i++)
{
if(sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp != 0 && currentTime > sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp + 10000)
{
//Displayf("RemoveFromAreadyPlayedList auto %u, %u, %u", CReplayMgr::GetCurrentTimeRelativeMs(), sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash, sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash);
sm_ReplayScriptedSpeechAlreadyPlayed[i].ContextNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VariationNumber = -1;
sm_ReplayScriptedSpeechAlreadyPlayed[i].VoiceNameHash = 0;
sm_ReplayScriptedSpeechAlreadyPlayed[i].TimeStamp = 0;
}
}
}
void audScriptAudioEntity::CleanUpReplayScriptedSpeechWaveSlot(s32 waveSlotIndex)
{
s32 sBaseWaveSlotIndex = audWaveSlot::GetWaveSlotIndex(audWaveSlot::FindWaveSlot("SCRIPT_SPEECH_0"));
replayAssert(waveSlotIndex >= sBaseWaveSlotIndex);
s32 i = waveSlotIndex - sBaseWaveSlotIndex;
CleanUpReplayScriptedSpeechSlot(i, true);
}
void audScriptAudioEntity::CleanUpReplayScriptedSpeechSlot(s32 slotIndex, bool force)
{
if(sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound)
{
if(force)
{
sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound->StopAndForget();
sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound = NULL;
}
else if(sm_ReplayScriptedSpeechSlot[slotIndex].WaveSlot)
{
audWaveSlot* pWaveSlot = sm_ReplayScriptedSpeechSlot[slotIndex].WaveSlot;
audPrepareState prepareState = g_ScriptAudioEntity.sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound->Prepare(pWaveSlot, true, naWaveLoadPriority::Speech);
if( prepareState != AUD_PREPARING )
{
sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound->StopAndForget();
sm_ReplayScriptedSpeechSlot[slotIndex].ScriptedSpeechSound = NULL;
}
}
}
g_SpeechManager.FreeScriptedSpeechSlot(slotIndex);
}
naEnvironmentGroup* audScriptAudioEntity::CreateReplayScriptedEnvironmentGroup(const CEntity* entity, const Vec3V& pos) const
{
return CreateScriptEnvironmentGroup(entity, pos);
}
bool audScriptAudioEntity::IsPlayerConversationActive(const CPed* pPlayer, s16& currentLine)
{
//Check if the player is in the conversation
if(IsPedInCurrentConversation(pPlayer))
{
// Check we've actually started the conversation
if ( m_PlayingScriptedConversationLine>=0 && m_PlayingScriptedConversationLine < AUD_MAX_SCRIPTED_CONVERSATION_LINES
&& (currentLine == -1 || currentLine == m_PlayingScriptedConversationLine) )
{
currentLine = m_PlayingScriptedConversationLine;
return true;
}
}
currentLine = -1;
return false;
}
void audScriptAudioEntity::RecordScriptSlotSound(const char* setName, const char* soundName, audSoundInitParams* initParams, const CEntity* pEntity, audSound* sound)
{
RecordScriptSlotSound(setName ? atStringHash(setName) : 0, soundName ? atStringHash(soundName) : g_NullSoundHash,initParams,pEntity,sound);
}
void audScriptAudioEntity::RecordScriptSlotSound(const int setNameHash, const int soundNameHash, audSoundInitParams* initParams, const CEntity* pEntity, audSound* sound)
{
if(pEntity && pEntity->GetAudioEntity())
{
if(setNameHash != 0)
{
CReplayMgr::ReplayRecordSoundPersistant(setNameHash,soundNameHash, initParams, sound, pEntity, eNoGlobalSoundEntity, ReplaySound::CONTEXT_INVALID);
}
else
{
CReplayMgr::ReplayRecordSoundPersistant(soundNameHash, initParams, sound, pEntity, eNoGlobalSoundEntity, ReplaySound::CONTEXT_INVALID);
}
}
else
{
if(setNameHash != 0)
{
CReplayMgr::ReplayRecordSoundPersistant(setNameHash,soundNameHash, initParams, sound, pEntity, eScriptSoundEntity, ReplaySound::CONTEXT_INVALID);
}
else
{
CReplayMgr::ReplayRecordSoundPersistant(soundNameHash, initParams, sound, pEntity, eScriptSoundEntity, ReplaySound::CONTEXT_INVALID);
}
}
}
void audScriptAudioEntity::RecordUpdateScriptSlotSound(const u32 setNameHash, const u32 soundNameHash, audSoundInitParams* initParams, const CEntity* pEntity, audSound* sound)
{
if(pEntity && pEntity->GetAudioEntity())
{
if(setNameHash != g_NullSoundHash)
{
CReplayMgr::ReplayRecordSoundPersistant(setNameHash, soundNameHash, initParams, sound, pEntity, eNoGlobalSoundEntity, ReplaySound::CONTEXT_INVALID, true);
}
else
{
CReplayMgr::ReplayRecordSoundPersistant(soundNameHash, initParams, sound, pEntity, eNoGlobalSoundEntity, ReplaySound::CONTEXT_INVALID, true);
}
}
else
{
if(setNameHash != g_NullSoundHash)
{
CReplayMgr::ReplayRecordSoundPersistant(setNameHash, soundNameHash, initParams, sound, pEntity, eScriptSoundEntity, ReplaySound::CONTEXT_INVALID, true);
}
else
{
CReplayMgr::ReplayRecordSoundPersistant(soundNameHash, initParams, sound, pEntity, eScriptSoundEntity, ReplaySound::CONTEXT_INVALID, true);
}
}
}
#endif
#if __BANK
const char* audScriptAudioEntity::GetStreamStateName(audScriptStreamState state)
{
switch(state)
{
case AUD_SCRIPT_STREAM_IDLE:
return "AUD_SCRIPT_STREAM_IDLE";
case AUD_SCRIPT_STREAM_PREPARING:
return "AUD_SCRIPT_STREAM_PREPARING";
case AUD_SCRIPT_STREAM_PREPARED:
return "AUD_SCRIPT_STREAM_PREPARED";
case AUD_SCRIPT_STREAM_PLAYING:
return "AUD_SCRIPT_STREAM_PLAYING";
default:
return "UNKNOWN";
}
}
void audScriptAudioEntity::DebugScriptedConversation()
{
naConvWarningf("Debugging scripted conversation:");
if (m_ScriptedConversationInProgress)
{
naConvWarningf("Scripted conversation in progress");
}
if (m_IsScriptedConversationAMobileCall)
{
naConvWarningf("Scripted conversation is a mobile call");
}
if (m_WaitingForPreload)
{
naConvWarningf("WaitingForPreload");
}
if (m_WaitingForPreload)
{
naConvWarningf("WaitingForPreload");
}
if (m_PauseConversation)
{
naConvWarningf("PauseConversation");
}
naConvWarningf("PSCL:%d; HighestLoadedLine:%d; HLoadingL:%d; PLSL:%d", m_PlayingScriptedConversationLine, m_HighestLoadedLine, m_HighestLoadingLine, m_PreLoadingSpeechLine);
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_SPEAKERS; i++)
{
naConvWarningf("Voice %d: %s", i, m_VoiceName[i]);
}
for (s32 i=0; i<AUD_MAX_SCRIPTED_CONVERSATION_LINES; i++)
{
naConvWarningf("%d; sp:%d; ctx:%s", i, m_ScriptedConversation[i].Speaker, m_ScriptedConversation[i].Context);
}
}
void audScriptAudioEntity::DebugScriptedConversationStatic()
{
g_ScriptAudioEntity.DebugScriptedConversation();
}
//void audScriptAudioEntity::AddCallToLog(const char *scriptCommand)
//{
//
//}
void audScriptAudioEntity::LogScriptBanks()
{
static const char* stateNames[3] = { "Queued", "Loading", "Loaded" };
naDisplayf("Logging script bank info @ Time %u (frame %u)", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
naDisplayf("Currently active script: %s", m_CurrentScriptName);
for(u32 i = 0; i < AUD_MAX_SCRIPT_BANK_SLOTS; i++)
{
naDisplayf(" Slot %u", i);
naDisplayf(" Script Slot Bank: %s (%u)", audWaveSlot::GetBankName(m_BankSlots[i].BankId), m_BankSlots[i].BankId);
naDisplayf(" Waveslot Bank: %s (%u)", m_BankSlots[i].BankSlot->GetLoadedBankName(), m_BankSlots[i].BankSlot->GetLoadedBankId());
naDisplayf(" State: %s", stateNames[m_BankSlots[i].State]);
naDisplayf(" Ref Count: %u", m_BankSlots[i].ReferenceCount);
for(u32 j = 0; j < AUD_MAX_SCRIPT_NETWORK_BANK_SLOTS; j++)
{
if(m_BankSlots[i].NetworkScripts[j].IsValid())
{
naDisplayf(" Network Script %d: %s (%u)", j, m_BankSlots[i].NetworkScripts[j].GetScriptName(), (u32)m_BankSlots[i].NetworkScripts[j].GetScriptNameHash());
}
else
{
naDisplayf(" Network Script %d: none", j);
}
}
}
}
const char* audScriptAudioEntity::GetScriptName(s32 scriptIndex)
{
audScript * script = GetScript(scriptIndex);
if (script)
{
GtaThread* pThread = static_cast<GtaThread*>(scrThread::GetThread(script->ScriptThreadId));
if (pThread)
{
return pThread->GetScriptName();
}
}
return "none";
}
void audScriptAudioEntity::LogScriptBankRequest(const char *request,const u32 scriptIndex,const u32 bankId, const u32 bankIndex)
{
const char * scriptName = "none";
audScript * script = GetScript(scriptIndex);
if(script)
{
GtaThread* pThread = static_cast<GtaThread*>(scrThread::GetThread(script->ScriptThreadId));
scriptName = pThread ? pThread->GetScriptName() : "none";
}
if(audVerifyf(bankIndex < m_BankSlots.GetMaxCount(), "Bank index %u exceeds slot count %u, Request %s, Script %s", bankIndex, m_BankSlots.GetMaxCount(), request, scriptName))
{
naDisplayf("%s [Script %s] [ScriptIdx %u] [Bank Idx %u] [Bank Name %s] [BankSlotIdx %u] [RefCount %d]"
,request,scriptName,scriptIndex,bankId,audWaveSlot::GetBankName(bankId),m_Scripts[scriptIndex].BankSlotIndex,m_BankSlots[bankIndex].ReferenceCount);
if (m_BankSlots[bankIndex].BankSlot)
{
naDisplayf("Script bankSlot: [Id %u] [Bankslot: %s] [WaveSlot: %s]",m_BankSlots[bankIndex].BankId,m_BankSlots[bankIndex].BankSlot->GetLoadedBankName(),m_BankSlots[bankIndex].BankSlot->GetSlotName());
}
else
{
naDisplayf("Script bankSlot: Null");
}
const audWaveSlot* waveSlot = audWaveSlot::FindLoadedBankWaveSlot(m_BankSlots[bankIndex].BankId);
if(waveSlot)
{
naDisplayf("WaveSlot: [Id %u] [Bankslot: %s] [WaveSlot: %s]",waveSlot->GetLoadedBankId(),waveSlot->GetCachedLoadedBankName(),waveSlot->GetSlotName());
}
else
{
naDisplayf("WaveSlot: Null");
}
}
}
void audScriptAudioEntity::LogNetworkScriptBankRequest(const char *request,const char* scriptName,const u32 bankId, const u32 bankIndex)
{
if(audVerifyf(bankIndex < m_BankSlots.GetMaxCount(), "Bank index %u exceeds slot count %u, Request %s, Script %s", bankIndex, m_BankSlots.GetMaxCount(), request, scriptName))
{
naDisplayf("%s [Script %s] [Bank Idx %u] [Bank Name %s]" ,request,scriptName,bankId,audWaveSlot::GetBankName(bankId));
if (m_BankSlots[bankIndex].BankSlot)
{
naDisplayf("Script bankSlot: [Id %u] [Bankslot: %s] [WaveSlot: %s]",m_BankSlots[bankIndex].BankId,m_BankSlots[bankIndex].BankSlot->GetLoadedBankName(),m_BankSlots[bankIndex].BankSlot->GetSlotName());
}
else
{
naDisplayf("Script bankSlot: Null");
}
const audWaveSlot* waveSlot = audWaveSlot::FindLoadedBankWaveSlot(m_BankSlots[bankIndex].BankId);
if(waveSlot)
{
naDisplayf("WaveSlot: [Id %u] [Bankslot: %s] [WaveSlot: %s]",waveSlot->GetLoadedBankId(),waveSlot->GetCachedLoadedBankName(),waveSlot->GetSlotName());
}
else
{
naDisplayf("WaveSlot: Null");
}
}
}
void audScriptAudioEntity::DrawDebug()
{
if(sm_DrawReplayScriptSpeechSlots)
{
char tempString[64];
static bank_float lineInc = 0.015f;
f32 lineBase = 0.1f;
#if GTA_REPLAY
for(int i=0; i<AUD_REPLAY_SCRIPTED_SPEECH_SLOTS; i++)
{
s32 index = -1;
if(sm_ReplayScriptedSpeechSlot[i].WaveSlot)
{
index = audWaveSlot::GetWaveSlotIndex(sm_ReplayScriptedSpeechSlot[i].WaveSlot);
}
audPrepareState state = AUD_PREPARING;
if(sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound)
{
state = sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound->Prepare(sm_ReplayScriptedSpeechSlot[i].WaveSlot, true, naWaveLoadPriority::Speech);
}
bool played = false;
played = g_ScriptAudioEntity.HasAlreadyPlayed(sm_ReplayScriptedSpeechSlot[i].VoiceNameHash, sm_ReplayScriptedSpeechSlot[i].ContextNameHash, sm_ReplayScriptedSpeechSlot[i].VariationNumber);
sprintf(tempString, "context %u voice %u Speech %d Waveslot %d State %d Played %d", sm_ReplayScriptedSpeechSlot[i].ContextNameHash, sm_ReplayScriptedSpeechSlot[i].VoiceNameHash,
sm_ReplayScriptedSpeechSlot[i].ScriptedSpeechSound ? 1 : 0, index, state, played); grcDebugDraw::Text(Vector2(0.1f, lineBase), Color32(255,255,255), tempString ); lineBase += lineInc;
}
#endif // GTA_REPLAY
lineBase += lineInc; lineBase += lineInc;
for(int i=0; i<g_MaxScriptedSpeechSlots; i++)
{
sprintf(tempString, "ScripSlot : vac %d refCount %d ", g_SpeechManager.IsScriptSpeechSlotVacant(i), g_SpeechManager.GetScriptSpeechSlotRefCount(i)); grcDebugDraw::Text(Vector2(0.1f, lineBase), Color32(255,255,255), tempString ); lineBase += lineInc;
}
}
char buf[256];
u32 soundHash = atStringHash(sm_ScriptTriggerOverride);
// On Screen script banks log
f32 offset = 0.05f;
if(sm_DrawScriptBanksLoaded)
{
for (int i=0; i<AUD_MAX_SCRIPT_BANK_SLOTS; i++)
{
char state[64];
formatf(state, "LOADED :");
if(m_BankSlots[i].BankId != AUD_INVALID_BANK_ID)
{
const char * scriptName = "none";
#if !__FINAL
for(int j=0; j < AUD_MAX_SCRIPTS; j++)
{
audScript * script = GetScript(j);
if(script)
{
if(script->BankSlotIndex & BIT(i))
{
GtaThread* pThread = static_cast<GtaThread*>(scrThread::GetThread(script->ScriptThreadId));
scriptName = pThread ? pThread->GetScriptName() : "none";
}
}
}
#endif
bool isReleased = m_BankSlots[i].ReferenceCount == 0;
char buf[128];
Color32 color = Color_green;
if(isReleased)
{
color = Color_blue;
formatf(state,"FREE:");
}
else if( m_BankSlots[i].State == AUD_SCRIPT_BANK_LOADING )
{
color = Color_yellow;
formatf(state,"LOADING...");
}
if (m_BankSlots[i].HintLoaded)
{
color = Color_LightBlue;
formatf(state,"HINTED:");
}
formatf(buf, "%s [Script %s] [Script Bank %s] [Bank Idx %u] [Script bank slot %d]",state,scriptName,m_BankSlots[i].BankSlot->GetLoadedBankName(), m_BankSlots[i].BankSlot->GetLoadedBankId(), i);
grcDebugDraw::Text(Vector2(0.05f, offset), color,buf,true,1.0f,1.0f,1);
offset += 0.02f;
}
}
}
if(sm_DrawScriptStream && m_StreamSound)
{
GtaThread* streamThread = static_cast<GtaThread*>(scrThread::GetThread(m_Scripts[sm_ScriptInChargeOfTheStream].ScriptThreadId));
const char *streamScriptName = streamThread ? streamThread->GetScriptName() : "none";
formatf(buf, "Script: %s, Stream : %s, script idx : %d, type %d",streamScriptName,m_StreamSound->GetName(),sm_ScriptInChargeOfTheStream, m_StreamType);
grcDebugDraw::Text(Vector2(0.05f, offset), Color32(255,255,255),buf,true,1.0f,1.0f,1);
offset += 0.02f;
}
//Script sounds log
if(sm_LogScriptTriggeredSounds && m_FileHandle == INVALID_FILE_HANDLE)
{
m_FileHandle = CFileMgr::OpenFileForWriting("common:/ScriptTriggeredSoundsLog.csv");
if(m_FileHandle != INVALID_FILE_HANDLE)
{
const char *header = "Time,Name,Sound,Sound idx,script idx\r\n";
CFileMgr::Write(m_FileHandle, header, istrlen(header));
CFileMgr::CloseFile(m_FileHandle);
m_FileHandle = CFileMgr::OpenFileForAppending("common:/ScriptTriggeredSoundsLog.csv");
}
}
for(u32 soundIdx = 0; soundIdx < AUD_MAX_SCRIPT_SOUNDS; soundIdx++)
{
if(m_ScriptSounds[soundIdx].Sound)
{
const char * scriptName = "none";
audScript * script = GetScript(m_ScriptSounds[soundIdx].ScriptIndex);
if(script)
{
GtaThread* pThread = static_cast<GtaThread*>(scrThread::GetThread(script->ScriptThreadId));
scriptName = pThread ? pThread->GetScriptName() : "none";
}
if(sm_DrawScriptTriggeredSounds)
{
audScriptSound scriptSound = m_ScriptSounds[soundIdx];
if(scriptSound.SoundSetHash)
{
formatf(buf, "Script: %s, Soundset: %s, Field: %s, Sound: %s, Sound Idx: %d, Script Idx: %d",scriptName, m_ScriptSounds[soundIdx].SoundSetTriggeredName, m_ScriptSounds[soundIdx].SoundTriggeredName, m_ScriptSounds[soundIdx].Sound->GetName(),soundIdx, scriptSound.ScriptIndex);
}
else
{
formatf(buf, "Script: %s, Sound:%s, Sound Idx: %d, Script Idx: %d",scriptName, m_ScriptSounds[soundIdx].Sound->GetName(), soundIdx, scriptSound.ScriptIndex);
}
grcDebugDraw::Text(Vector2(0.05f, offset), Color32(255,255,255),buf,true,1.0f,1.0f,1);
offset += 0.02f;
}
if(sm_InWorldDrawScriptTriggeredSounds)
{
bool found = true;
if(sm_FilterScriptTriggeredSounds)
{
found = false;
naDisplayf("sound name :%s text :%s ",m_ScriptSounds[soundIdx].Sound->GetName(),sm_ScriptTriggerOverride);
if(atStringHash(m_ScriptSounds[soundIdx].Sound->GetName()) == soundHash)
{
found = true;
}
if (soundIdx == (u32)strtol(sm_ScriptTriggerOverride,NULL,0))
{
found = true;
}
}
if(found)
{
audScriptSound scriptSound = m_ScriptSounds[soundIdx];
formatf(buf, "Script: %s, SoundTriggeredName : %s, Soundset: %s , Sound: %s sound idx : %d, script idx : %d",scriptName, m_ScriptSounds[soundIdx].SoundTriggeredName, m_ScriptSounds[soundIdx].SoundSetTriggeredName,m_ScriptSounds[soundIdx].Sound->GetName(),soundIdx, scriptSound.ScriptIndex);
grcDebugDraw::Text(m_ScriptSounds[soundIdx].Sound->GetRequestedPosition(), Color32(255, 255, 255, 255),buf,true,1);
}
}
if(m_FileHandle != INVALID_FILE_HANDLE && m_ScriptSounds[soundIdx].Sound)
{
audScriptSound scriptSound = m_ScriptSounds[soundIdx];
formatf(buf, "%f,%s,%s,%s,%s,%d,%d\r\n",fwTimer::GetTimeInMilliseconds(),scriptName
,m_ScriptSounds[soundIdx].SoundTriggeredName, m_ScriptSounds[soundIdx].SoundSetTriggeredName,m_ScriptSounds[soundIdx].Sound->GetName(),soundIdx, scriptSound.ScriptIndex);
CFileMgr::Write(m_FileHandle, buf, istrlen(buf));
}
}
}
if(!sm_LogScriptTriggeredSounds && m_FileHandle != INVALID_FILE_HANDLE)
{
CFileMgr::CloseFile(m_FileHandle);
m_FileHandle = INVALID_FILE_HANDLE;
}
if(g_DisplayScriptedLineInfo && sm_ScriptedLineDebugInfo[0] != '\0')
{
grcDebugDraw::Text(Vector2(0.05f, 0.8f), Color32(255,255,255),sm_ScriptedLineDebugInfo,true,1.0f,1.0f,1);
}
if(g_DisplayScriptSoundIdInfo)
{
f32 yCoord = 0.02f - g_ScriptSoundIdInfoScroll;
grcDebugDraw::Text(Vector2(0.05f, yCoord), Color_white, "Slot", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.1f, yCoord), Color_white, "Owner", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.25f, yCoord), Color_white, "Sound", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.4f, yCoord), Color_white, "Sound State", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.55f, yCoord), Color_white, "Age", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.7f, yCoord), Color_white, "Frame Allocated", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.85f, yCoord), Color_white, "Script Has Ref", true, 1.f, 1.f, -1);
yCoord += 0.04f;
const char* soundStates[AUD_SOUND_STATE_MAX + 1] = {"Dormant","Preparing","Playing","Waiting to be Deleted","Max"};
for (u32 scriptSoundIndex = 0; scriptSoundIndex < AUD_MAX_SCRIPT_SOUNDS; scriptSoundIndex++)
{
// This is the logic used in FindFreeSoundId, so invert it to find all used slots
if (!(m_ScriptSounds[scriptSoundIndex].ScriptIndex == -1 && m_ScriptSounds[scriptSoundIndex].NetworkId == audScriptSound::INVALID_NETWORK_ID))
{
formatf(buf, "%d", scriptSoundIndex);
grcDebugDraw::Text(Vector2(0.05f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, "%s", m_ScriptSounds[scriptSoundIndex].ScriptName);
grcDebugDraw::Text(Vector2(0.1f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, m_ScriptSounds[scriptSoundIndex].SoundRef.IsValid() ? g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_ScriptSounds[scriptSoundIndex].SoundRef) : "Invalid");
grcDebugDraw::Text(Vector2(0.25f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, "%s", m_ScriptSounds[scriptSoundIndex].Sound? soundStates[m_ScriptSounds[scriptSoundIndex].Sound->GetPlayState()] : "No Sound");
grcDebugDraw::Text(Vector2(0.4f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, "%.02f", (fwTimer::GetTimeInMilliseconds() - m_ScriptSounds[scriptSoundIndex].TimeAllocated) * 0.001f);
grcDebugDraw::Text(Vector2(0.55f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, "%u", m_ScriptSounds[scriptSoundIndex].FrameAllocated);
grcDebugDraw::Text(Vector2(0.7f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
formatf(buf, "%s", m_ScriptSounds[scriptSoundIndex].ScriptHasReference? "true" : "false");
grcDebugDraw::Text(Vector2(0.85f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
}
yCoord += 0.02f;
}
}
if(g_DisplayScriptBankUsage)
{
f32 yCoord = 0.02f - g_ScriptSoundIdInfoScroll;
grcDebugDraw::Text(Vector2(0.05f, yCoord), Color_white, "Slot", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.1f, yCoord), Color_white, "Bank", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.3f, yCoord), Color_white, "State", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.4f, yCoord), Color_white, "Ref Count", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.5f, yCoord), Color_white, "Network Script A", true, 1.f, 1.f, -1);
grcDebugDraw::Text(Vector2(0.7f, yCoord), Color_white, "Network Script B", true, 1.f, 1.f, -1);
yCoord += 0.04f;
for (s32 i=0; i<AUD_MAX_SCRIPT_BANK_SLOTS; i++)
{
formatf(buf, "%d", i);
grcDebugDraw::Text(Vector2(0.05f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
if(m_BankSlots[i].BankId != AUD_INVALID_BANK_ID)
{
formatf(buf, "%s", audWaveSlot::GetBankName(m_BankSlots[i].BankId));
grcDebugDraw::Text(Vector2(0.1f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
static const char* stateNames[3] = { "Queued", "Loading", "Loaded" };
formatf(buf, "%s", stateNames[m_BankSlots[i].State]);
grcDebugDraw::Text(Vector2(0.3f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
}
formatf(buf, "%d", m_BankSlots[i].ReferenceCount);
grcDebugDraw::Text(Vector2(0.4f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
if(m_BankSlots[i].NetworkScripts[0].IsValid())
{
formatf(buf, "%s (%u)", m_BankSlots[i].NetworkScripts[0].GetScriptName(), (u32)m_BankSlots[i].NetworkScripts[0].GetScriptNameHash());
grcDebugDraw::Text(Vector2(0.5f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
}
if(m_BankSlots[i].NetworkScripts[1].IsValid())
{
formatf(buf, "%s (%u)", m_BankSlots[i].NetworkScripts[1].GetScriptName(), (u32)m_BankSlots[i].NetworkScripts[1].GetScriptNameHash());
grcDebugDraw::Text(Vector2(0.7f, yCoord), Color_white, buf, true, 1.f, 1.f, -1);
}
yCoord += 0.02f;
}
}
if(g_DrawScriptSpeechSlots)
{
char str[1024];
Color32 color;
f32 yPos = 0.3f;
f32 yInc = 0.025f;
f32 xPos = 0.4f;
formatf(str, sizeof(str), "SCRIPT SPEECH SLOTS");
grcDebugDraw::Text(Vector2(xPos, yPos), Color_purple, str);
yPos += yInc;
for (s32 currentPreloadSet=0; currentPreloadSet<2; currentPreloadSet++)
{
for (s32 i=0; i<AUD_MAX_SCRIPTED_SPEECH_SLOTS_TO_LOAD; i++)
{
{
sprintf(str, "ScriptedSpeechSlotInfo %d %d Waveslot: %s, LoadedWave: %u, context: %s, voicename: %s",
currentPreloadSet, i,
m_ScriptedSpeechSlot[i][currentPreloadSet].WaveSlot->GetSlotName(),
m_ScriptedSpeechSlot[i][currentPreloadSet].WaveSlot->GetLoadedWaveNameHash(),
m_ScriptedSpeechSlot[i][currentPreloadSet].Context,
m_ScriptedSpeechSlot[i][currentPreloadSet].VoiceName
);
grcDebugDraw::Text(Vector2(xPos, yPos), Color_white, str);
yPos += yInc;
if (m_ScriptedSpeechSlot[i][currentPreloadSet].ScriptedSpeechSound)
{
sprintf(str, "SoundInfo %d %d Waveslot: %p, WaveNameHash: %u BankId: %u",
currentPreloadSet, i,
m_ScriptedSpeechSlot[i][currentPreloadSet].ScriptedSpeechSound->GetWaveSlot()->GetSlotName(),
m_ScriptedSpeechSlot[i][currentPreloadSet].ScriptedSpeechSound->GetWaveNameHash(),
m_ScriptedSpeechSlot[i][currentPreloadSet].ScriptedSpeechSound->GetBankId());
grcDebugDraw::Text(Vector2(xPos, yPos), Color_white, str);
}
yPos += yInc;
}
}
}
}
}
void audScriptAudioEntity::DumpScriptSoundIdUsage()
{
char str[1024];
FileHandle fileHandle = CFileMgr::OpenFileForWriting("common:/ScriptSoundIdUsage.csv");
if(fileHandle != INVALID_FILE_HANDLE)
{
const char *header = "Slot,Owner,Sound,State,Age,Frame Allocated,Script Has Ref\r\n";
CFileMgr::Write(fileHandle, header, istrlen(header));
const char* soundStates[AUD_SOUND_STATE_MAX + 1] = {"Dormant","Preparing","Playing","Waiting to be Deleted","Max"};
for (u32 scriptSoundIndex = 0; scriptSoundIndex < AUD_MAX_SCRIPT_SOUNDS; scriptSoundIndex++)
{
// This is the logic used in FindFreeSoundId, so invert it to find all used slots
if (!(m_ScriptSounds[scriptSoundIndex].ScriptIndex == -1 && m_ScriptSounds[scriptSoundIndex].NetworkId == audScriptSound::INVALID_NETWORK_ID))
{
formatf(str, "%d,%s,%s,%s,%.02f,%u,%s\r\n",
scriptSoundIndex,
m_ScriptSounds[scriptSoundIndex].ScriptName,
m_ScriptSounds[scriptSoundIndex].SoundRef.IsValid() ? g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_ScriptSounds[scriptSoundIndex].SoundRef) : "Invalid",
m_ScriptSounds[scriptSoundIndex].Sound? soundStates[m_ScriptSounds[scriptSoundIndex].Sound->GetPlayState()] : "No Sound",
(fwTimer::GetTimeInMilliseconds() - m_ScriptSounds[scriptSoundIndex].TimeAllocated) * 0.001f,
m_ScriptSounds[scriptSoundIndex].FrameAllocated,
m_ScriptSounds[scriptSoundIndex].ScriptHasReference? "true" : "false"
);
CFileMgr::Write(fileHandle, str, istrlen(str));
}
}
audDisplayf("Successfully dumped script sound usage to common:/ScriptSoundIdUsage.csv");
CFileMgr::CloseFile(fileHandle);
}
else
{
audErrorf("Failed to open common:/ScriptSoundIdUsage.csv");
}
}
void MissionCompleteCB()
{
g_ScriptAudioEntity.TriggerMissionComplete("FRANKLIN_BIG_01");
}
void TriggerAmbientSpeechCB()
{
g_ScriptAudioEntity.SayAmbientSpeechFromPosition(g_AmbientSpeechDebugContext, atStringHash(g_AmbientSpeechDebugVoice),
Vector3(g_AmbientSpeechDebugPosX, g_AmbientSpeechDebugPosY, g_AmbientSpeechDebugPosZ),
g_AmbientSpeechDebugParams);
}
void audScriptAudioEntity::ForceLowInterrupt()
{
if(m_Ped[g_InterruptSpeakerNum])
{
RegisterScriptRequestedInterrupt(m_Ped[g_InterruptSpeakerNum],"", "",false, true);
}
}
void audScriptAudioEntity::ForceMediumInterrupt()
{
if(m_Ped[g_InterruptSpeakerNum])
{
RegisterScriptRequestedInterrupt(m_Ped[g_InterruptSpeakerNum],"", "",false, true);
}
}
void audScriptAudioEntity::ForceHighInterrupt()
{
if(m_Ped[g_InterruptSpeakerNum])
{
RegisterScriptRequestedInterrupt(m_Ped[g_InterruptSpeakerNum],"", "",false, true);
}
}
void audScriptAudioEntity::DebugEnableFlag(const char *flagName)
{
// Note - using the string version here only to test the map; code interaction with flags
// should use the flag ids directly.
const audScriptAudioFlags::FlagId flagId = audScriptAudioFlags::FindFlagId(flagName);
g_ScriptAudioEntity.SetFlagState(flagId, true);
}
void audScriptAudioEntity::DebugDisableFlag(const char *flagName)
{
// Note - using the string version here only to test the map; code interaction with flags
// should use the flag ids directly.
const audScriptAudioFlags::FlagId flagId = audScriptAudioFlags::FindFlagId(flagName);
g_ScriptAudioEntity.SetFlagState(flagId, false);
}
void audScriptAudioEntity::AddWidgets(bkBank &bank)
{
bank.PushGroup("audScriptAudioEntity",false);
bank.AddToggle("Display Script Sound ID Usage", &g_DisplayScriptSoundIdInfo);
bank.AddToggle("Display Script Bank Usage", &g_DisplayScriptBankUsage);
bank.AddSlider("Sound ID Usage Display Scroll", &g_ScriptSoundIdInfoScroll, 0, 1.5f, 0.1f);
bank.AddButton("Dump Script Sound Id Usage (common:/ScriptSoundIdUsage.csv)", datCallback(MFA(audScriptAudioEntity::DumpScriptSoundIdUsage), &g_ScriptAudioEntity));
bank.AddButton("Log Script Bank Usage", datCallback(MFA(audScriptAudioEntity::LogScriptBanks), &g_ScriptAudioEntity));
bank.AddButton("TriggerMissionComplete", CFA(MissionCompleteCB));
bank.AddToggle("Debug Replay Scripted Speech", &sm_DrawReplayScriptSpeechSlots);
bank.AddButton("Debug Scripted Conv NOW", datCallback(CFA(DebugScriptedConversationStatic)));
bank.AddToggle("Debug scripted convs", &g_DebugScriptedConvs);
bank.AddToggle("g_DisplayScriptedLineInfo", &g_DisplayScriptedLineInfo);
bank.AddSlider("g_MinRagdollTimeBeforeConvPause", &g_MinRagdollTimeBeforeConvPause, 0, 5000, 10);
bank.AddSlider("g_MinNotRagdollTimeBeforeConvRestart", &g_MinNotRagdollTimeBeforeConvRestart, 0, 5000, 10);
bank.AddToggle("g_AnimTriggersControllingConversation", &g_AnimTriggersControllingConversation);
bank.AddToggle("g_FakeConvAnimTrigger", &g_FakeConvAnimTrigger);
bank.AddToggle("g_DuckForConvAfterPreload", &g_DuckForConvAfterPreload);
bank.AddToggle("g_ConversationDebugSpew", &g_ConversationDebugSpew);
bank.AddToggle("Force Mobile Interference", &g_ForceMobileInterference);
bank.AddToggle("g_AdjustOverlapForLipsyncPredelay", &g_AdjustOverlapForLipsyncPredelay);
bank.AddToggle("g_AdjustTriggerTimeForZeroPlaytime", &g_AdjustTriggerTimeForZeroPlaytime);
bank.AddToggle("g_AdjustTriggerForZeroEvenIfMarkedOverlap", &g_AdjustTriggerForZeroEvenIfMarkedOverlap);
bank.AddSlider("g_AdditionalScriptedSpeechPause", &g_AdditionalScriptedSpeechPause, 0, 1000, 10);
bank.AddToggle("g_TrackMissingDialogue", &g_TrackMissingDialogue);
bank.AddToggle("g_TrackMissingDialogueMP", &g_TrackMissingDialogueMP);
bank.AddSlider("g_MinLowPainBikeReaction", &g_MinLowPainBikeReaction, 0.0f, 100.0f, 0.01f);
bank.AddToggle("g_EnableAircraftWarningSpeech", &g_EnableAircraftWarningSpeech);
bank.PushGroup("Conversation Interrupts",true);
bank.AddButton("Force Low Interrupt", datCallback(MFA(audScriptAudioEntity::ForceLowInterrupt), &g_ScriptAudioEntity));
bank.AddButton("Force Medium Interrupt", datCallback(MFA(audScriptAudioEntity::ForceMediumInterrupt), &g_ScriptAudioEntity));
bank.AddButton("Force High Interrupt", datCallback(MFA(audScriptAudioEntity::ForceHighInterrupt), &g_ScriptAudioEntity));
bank.AddSlider("Player Speaker Number", &g_InterruptSpeakerNum, 0, 8, 1);
bank.AddToggle("g_DisableInterrupts", &g_DisableInterrupts);
bank.AddToggle("g_ForceScriptedInterrupts", &g_ForceScriptedInterrupts);
bank.AddToggle("g_DisableAirborneInterrupts", &g_DisableAirborneInterrupts);
bank.AddToggle("g_AllowMuliWantedInterrupts", &g_AllowMuliWantedInterrupts);
bank.AddSlider("g_MinTimeCarInAirForInterrupt", &g_MinTimeCarInAirForInterrupt, 0, 20000, 10);
bank.AddSlider("g_MinTimeCarOnGroundForInterrupt", &g_MinTimeCarOnGroundForInterrupt, 0, 20000, 10);
bank.AddSlider("g_MinTimeCarOnGroundToRestart", &g_MinTimeCarOnGroundToRestart, 0, 20000, 10);
bank.AddSlider("g_MinAirBorneTimeToKillConversation", &g_MinAirBorneTimeToKillConversation, 0, 20000, 10);
bank.AddSlider("g_MaxAirBorneTimeToApplyRestartOffset", &g_MaxAirBorneTimeToApplyRestartOffset, 0, 20000, 10);
bank.AddSlider("g_MinCarRollForInterrupt", &g_MinCarRollForInterrupt, -1.0f, 0.0f, 0.01f);
bank.AddSlider("g_MinLowReactionDamage", &g_MinLowReactionDamage, 0.0f, 200.0f, 1.0f);
bank.AddSlider("g_MinMediumReactionDamage", &g_MinMediumReactionDamage, 0.0f, 200.0f, 1.0f);
bank.AddSlider("g_MinHighReactionDamage", &g_MinHighReactionDamage, 0.0f, 200.0f, 1.0f);
bank.AddSlider("g_TimeToPauseConvOnCollision", &g_TimeToPauseConvOnCollision, 0, 10000, 50);
bank.AddSlider("g_MinTimeBetweenCollisionReactions", &g_MinTimeBetweenCollisionReactions, 0, 10000, 50);
bank.AddSlider("g_MinTimeBetweenCollisionTriggers", &g_MinTimeBetweenCollisionTriggers, 0, 10000, 50);
bank.AddSlider("g_TimeToWaitForInterruptToPrepare", &g_TimeToWaitForInterruptToPrepare, 0, 5000, 50);
bank.AddToggle("g_ForceNonPlayerInterrupt", &g_ForceNonPlayerInterrupt);
bank.AddText("Low Interrupt Context", g_szLowInterruptContext, sizeof(g_szLowInterruptContext));
bank.AddText("Medium Interrupt Context", g_szMediumInterruptContext, sizeof(g_szLowInterruptContext));
bank.AddText("High Interrupt Context", g_szHighInterruptContext, sizeof(g_szLowInterruptContext));
bank.PopGroup();
bank.PushGroup("Ambient speech", true);
bank.AddButton("TriggerMissionComplete", CFA(TriggerAmbientSpeechCB));
bank.AddText("g_AmbientSpeechDebugContext", &g_AmbientSpeechDebugContext[0], sizeof(g_AmbientSpeechDebugContext));
bank.AddText("g_AmbientSpeechDebugVoice", &g_AmbientSpeechDebugVoice[0], sizeof(g_AmbientSpeechDebugVoice));
bank.AddText("g_AmbientSpeechDebugParams", &g_AmbientSpeechDebugParams[0], sizeof(g_AmbientSpeechDebugParams));
bank.AddSlider("g_AmbientSpeechDebugPosX", &g_AmbientSpeechDebugPosX, -10000.0f, 10000.0f, 0.1f);
bank.AddSlider("g_AmbientSpeechDebugPosY", &g_AmbientSpeechDebugPosY, -10000.0f, 10000.0f, 0.1f);
bank.AddSlider("g_AmbientSpeechDebugPosZ", &g_AmbientSpeechDebugPosZ, -1000.0f, 1000.0f, 0.1f);
bank.PopGroup();
bank.PushGroup("Debug script sounds",true);
bank.AddToggle("Screen debug playing script-triggered sounds", &sm_DrawScriptTriggeredSounds);
bank.AddToggle("In world debug playing script-triggered sounds", &sm_InWorldDrawScriptTriggeredSounds);
bank.AddToggle("Screen debug playing script-stream sound", &sm_DrawScriptStream);
bank.AddToggle("Log script-triggered sounds playing.", &sm_LogScriptTriggeredSounds);
bank.AddToggle("Show script banks loaded.", &sm_DrawScriptBanksLoaded);
bank.AddToggle("Filter script-triggered sounds", &sm_FilterScriptTriggeredSounds);
bank.AddText("By sound id or sound name", sm_ScriptTriggerOverride, 64, false);
bank.PopGroup();
bank.PushGroup("Script audio flags", true);
for(s32 i = 0; i < audScriptAudioFlags::kNumScriptAudioFlags; i++)
{
char buttonName[128];
const audScriptAudioFlags::FlagId flagId = (audScriptAudioFlags::FlagId)i;
formatf(buttonName, "Enable %s", audScriptAudioFlags::GetName(flagId));
bank.AddButton(buttonName, datCallback(CFA1(audScriptAudioEntity::DebugEnableFlag), (void*)audScriptAudioFlags::GetName(flagId)));
formatf(buttonName, "Disable %s", audScriptAudioFlags::GetName(flagId));
bank.AddButton(buttonName, datCallback(CFA1(audScriptAudioEntity::DebugDisableFlag), (void*)audScriptAudioFlags::GetName(flagId)));
}
bank.PushGroup("Enable counts");
for(s32 i = 0; i < audScriptAudioFlags::kNumScriptAudioFlags; i++)
{
bank.AddText(audScriptAudioFlags::GetName((audScriptAudioFlags::FlagId)i), &g_ScriptAudioEntity.m_FlagState[i], true);
}
bank.PopGroup();
bank.PopGroup();
bank.PopGroup();
}
#endif
/******************************************************
* audScript
*****************************************************/
audScene* audScript::GetScene(u32 sceneHash)
{
audScriptScene * scriptScene = scenes;
while(scriptScene)
{
if(scriptScene->m_scene && scriptScene->m_scene->IsInstanceOf(sceneHash))
{
return scriptScene->m_scene;
}
scriptScene = scriptScene->m_next;
}
return NULL;
}
bool audScript::StartScene(u32 sceneHash)
{
MixerScene * sceneSettings = DYNAMICMIXMGR.GetObject<MixerScene>(sceneHash);
audScene * existingScene = GetScene(sceneHash);
if(existingScene)
{
return true;
}
audScriptScene * sScene = scenes;
audScriptScene * prevSS = NULL;
while(sScene)
{
if(!sScene->m_scene)
{
//Found a slot that used to contain a scene but is vacant so use it
break;
}
prevSS = sScene;
sScene = sScene->m_next;
}
if(!sScene) //We've reached the end of the list so add a new slot on
{
sScene = rage_new audScriptScene();
}
if(prevSS)
{
prevSS->m_next = sScene;
}
else
{
//Must be at the head
scenes = sScene;
}
//Tell the dynamic mixer to kick off this scene and pass through m_scene
//to pick up the reference
bool ready = true;
if(sceneSettings)
{
DYNAMICMIXER.StartScene(sceneSettings, &sScene->m_scene, this);
#if GTA_REPLAY
if(sScene && sScene->m_scene)
{
sScene->m_scene->SetHash(sceneHash);
if(CReplayMgr::ShouldRecord())
{
if(sceneHash == ATSTRINGHASH("DEATH_SCENE", 0x5A583A13) || sceneHash == ATSTRINGHASH("QUIET_SCENE", 0x85BA09E8))
{
CPed* pPlayerPed = FindPlayerPed();
bool isPlayerDead = pPlayerPed && pPlayerPed->ShouldBeDead();
bool isPlayerArrested = pPlayerPed && pPlayerPed->GetIsArrested();
if(isPlayerDead || isPlayerArrested)
{
return ready;
}
}
CReplayMgr::RecordPersistantFx<CPacketDynamicMixerScene>(
CPacketDynamicMixerScene(sceneHash, false, 0),
CTrackedEventInfo<tTrackedSceneType>(sScene->m_scene), NULL, true);
}
}
#endif //GTA_REPLAY
}
return ready;
}
void audScript::StopScene(u32 sceneHash)
{
audScene* scene = GetScene(sceneHash);
if(scene)
{
scene->Stop();
}
}
void audScript::StopScenes()
{
while(scenes)
{
if(scenes->m_scene)
{
if(scenes->m_scene->IsInstanceOf(g_ScriptAudioEntity.GetPoliceScannerSceneHash()))
{
g_ScriptAudioEntity.ResetPoliceScannerVariables();
}
scenes->m_scene->Stop();
}
audScriptScene * next = scenes->m_next;
delete scenes;
scenes = next;
}
}
void audScript::SetSceneVariable(u32 sceneHash, u32 variableHash, f32 value)
{
audScene* scene = GetScene(sceneHash);
if(scene)
{
scene->SetVariableValue(variableHash, value);
}
}
bool audScript::SceneHasFinishedFade(u32 sceneHash)
{
audScene * scene = GetScene(sceneHash);
if(scene)
{
return scene->HasFinishedFade();
}
return true;
}
//Functions for manipulating script-scoped variables
const variableNameValuePair * audScript::GetVariableAddress(u32 nameHash) const
{
//Loop through our own variable block to see if it's one of ours
for(s32 i=0; i < g_MaxAudScriptVariables; i++)
{
if(m_Variables[i].hash == nameHash)
{
//We own the variable it's asking about, so return its address
return &(m_Variables[i]);
}
else if(m_Variables[i].hash == 0)
{
return NULL;
}
}
return NULL;
}
//const variableNameValuePair * audScript::GetVariableAddress(const char * name)
//{
// return GetVariableAddress(atStringHash(name));
//}
//
void audScript::SetVariableValue(u32 nameHash, f32 value)
{
//Look through our variable block to see if it exists already
for(s32 i=0; i < g_MaxAudScriptVariables; i++)
{
if(m_Variables[i].hash == nameHash)
{
//the variable exists, so set it
m_Variables[i].value = value;
return;
}
else if (m_Variables[i].hash == 0)
{
//we've got the first empty one, so set this one to be the new variable
//and bail out
m_Variables[i].hash = nameHash;
m_Variables[i]. value = value;
return;
}
}
naAssertf(0, "An audScript has run out of variables; consider using less variables or upping the max number of variables");
}
void audScript::SetVariableValue(const char * name, float value)
{
SetVariableValue(atStringHash(name), value);
}
void audScript::Shutdown()
{
ReleaseScriptWeaponAudio();
StopScenes();
g_AmbientAudioEntity.CancelScriptPedDensityChanges(ScriptThreadId);
g_AmbientAudioEntity.CancelScriptWallaSoundSet(ScriptThreadId);
for(s32 ambientZoneLoop = 0; ambientZoneLoop < m_ScriptAmbientZoneChanges.GetCount(); ambientZoneLoop++)
{
g_AmbientAudioEntity.ClearZoneNonPersistentStatus(m_ScriptAmbientZoneChanges[ambientZoneLoop], true);
}
m_ScriptAmbientZoneChanges.clear();
for(int i=0; i<m_PedsSetToAngry.GetCount(); ++i)
{
if(m_PedsSetToAngry[i])
m_PedsSetToAngry[i]->SetPedIsAngry(false);
}
m_PedsSetToAngry.clear();
for(int i=0; i<m_PriorityVehicles.GetCount(); i++)
{
if(m_PriorityVehicles[i])
{
m_PriorityVehicles[i]->SetScriptPriority(AUD_VEHICLE_SCRIPT_PRIORITY_NONE, ScriptThreadId);
}
}
m_PriorityVehicles.clear();
audSpeechAudioEntity::RemoveContextBlocks(ScriptThreadId);
s32 scriptIndex = g_ScriptAudioEntity.GetIndexFromScriptThreadId(ScriptThreadId);
scenes = NULL;
if(scriptIndex == audScriptAudioEntity::sm_ScriptInChargeOfTheStream)
{
g_ScriptAudioEntity.StopStream();
}
if(scriptIndex == g_SpeechManager.GetTennisScriptId())
{
g_SpeechManager.UnrequestTennisBanks();
}
if(m_FlagResetState.AreAnySet())
{
for(s32 i = 0; i < audScriptAudioFlags::kNumScriptAudioFlags; i++)
{
if(m_FlagResetState.IsSet(i))
{
g_ScriptAudioEntity.ResetFlagState((audScriptAudioFlags::FlagId)i);
}
}
}
if(m_SlowMoMode)
{
audWarningf("Automatically deactivating slow-mo mode %u for script", m_SlowMoMode);
audNorthAudioEngine::DeactivateSlowMoMode(m_SlowMoMode);
m_SlowMoMode = 0;
}
if(m_ScriptVehicleHash != 0u)
{
audVehicleAudioEntity::PreloadScriptVehicleAudio(0u, scriptIndex);
m_ScriptVehicleHash = 0u;
}
// Ditch any pending music events owned by this script. If this shows up in a profile then we'll need
// to flag internally whether this is necessary for each script instance; there should only ever be a few
// active events, so it will hopefully be cheap enough to ignore.
g_InteractiveMusicManager.GetEventManager().CancelAllEvents(ScriptThreadId);
// We're done - clear out the slot
ScriptThreadId = THREAD_INVALID;
BankSlotIndex = 0;
BankOverNetwork = 0;
PlayerBits = AUD_NET_ALL_PLAYERS;
m_ScriptisShuttingDown = false;
}
extern audScannerManager g_AudioScannerManager;
void audScriptAudioEntity::OnFlagStateChange(audScriptAudioFlags::FlagId flagId, const bool state)
{
#if __BANK
audDisplayf("Script audio flag %s now %s (from %s)", audScriptAudioFlags::GetName(flagId), state ? "enabled" : "disabled", CTheScripts::GetCurrentScriptNameAndProgramCounter());
#endif
// If you want to kick off some custom logic when a script audio flag changes state, add a handler here.
switch(flagId)
{
case audScriptAudioFlags::RadioOverlapDisabled:
audRadioStation::DisableOverlappedTracks(state);
break;
case audScriptAudioFlags::PoliceScannerDisabled:
g_AudioScannerManager.DisableScannerFlagChanged(state);
default:
// Not all flags will need to state changes - ie where code is happy to poll IsFlagSet()
break;
}
}
void audScriptAudioEntity::SetFlagState(const audScriptAudioFlags::FlagId flagId, const bool state)
{
const bool prevState = IsFlagSet(flagId);
if(state)
{
m_FlagState[flagId]++;
}
else
{
if(audVerifyf(prevState, "script trying to disable an audio flag (%d / %s) that is not currently set (from %s)", flagId, audScriptAudioFlags::GetName(flagId), CTheScripts::GetCurrentScriptNameAndProgramCounter()))
{
m_FlagState[flagId]--;
}
}
const bool newState = IsFlagSet(flagId);
if(newState != prevState)
{
Assert(state == newState);
OnFlagStateChange(flagId, state);
}
}
void audScript::SetAudioFlag(const char *flagName, const bool state)
{
const audScriptAudioFlags::FlagId flagId = audScriptAudioFlags::FindFlagId(flagName);
if(audScriptAudioFlags::IsValid(flagId))
{
return SetAudioFlag(flagId, state);
}
}
void audScript::SetAudioFlag(const audScriptAudioFlags::FlagId flagId, const bool state)
{
const bool prevState = m_FlagResetState.IsSet(flagId);
// Silently ignore multiple requests for the same state
if(state != prevState)
{
g_ScriptAudioEntity.SetFlagState(flagId, state);
m_FlagResetState.Set(flagId, state);
}
}
void audScript::ActivateSlowMoMode(const char *mode)
{
const u32 newMode = atStringHash(mode);
if(newMode == m_SlowMoMode)
{
return;
}
else if(!audVerifyf(m_SlowMoMode == 0, "Script activating a slow-mo mode %s without first deactivating %u", mode, m_SlowMoMode))
{
audNorthAudioEngine::DeactivateSlowMoMode(m_SlowMoMode);
}
m_SlowMoMode = newMode;
audNorthAudioEngine::ActivateSlowMoMode(newMode);
}
void audScript::DeactivateSlowMoMode(const char *mode)
{
const u32 newMode = atStringHash(mode);
if(newMode == m_SlowMoMode)
{
audNorthAudioEngine::DeactivateSlowMoMode(newMode);
m_SlowMoMode = 0;
}
else
{
audWarningf("Script deactivating slow mo mode %s, but have set %u", mode, m_SlowMoMode);
audNorthAudioEngine::DeactivateSlowMoMode(newMode);
}
}
bool audScript::RequestScriptWeaponAudio(const char * itemName)
{
const CItemInfo * info = CWeaponInfoManager::GetInfo(atStringHash(itemName));
if(info)
{
m_ScriptWeaponAudioHash = info->GetAudioHash();
return g_WeaponAudioEntity.AddScriptWeapon(info->GetAudioHash());
}
naAssertf(info, "SCRIPT: Couldn't find weapon %s in REQUEST_WEAPON_AUDIO", itemName);
return true; //Can't find the weapon but return true to no block the script
}
void audScript::ReleaseScriptWeaponAudio()
{
if(m_ScriptWeaponAudioHash)
{
g_WeaponAudioEntity.ReleaseScriptWeapon();
m_ScriptWeaponAudioHash = 0;
}
}
bool audScript::RequestScriptVehicleAudio(const u32 vehicleModelName)
{
if(audVehicleAudioEntity::PreloadScriptVehicleAudio(vehicleModelName, g_ScriptAudioEntity.GetIndexFromScriptThreadId(ScriptThreadId)))
{
m_ScriptVehicleHash = vehicleModelName;
return true;
}
else
{
return false;
}
}
audScript::~audScript()
{
Shutdown();
}