Files
GTASource/game/audio/speechmanager.h
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

678 lines
21 KiB
C++

//
// audio/speechmanager.h
//
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
//
#ifndef AUD_SPEECHMANAGER_H
#define AUD_SPEECHMANAGER_H
#include "audio/speechaudioentity.h"
#include "audioengine/entity.h"
#include "gameobjects.h"
#include "script/script.h"
#include "control/replay/replay.h"
//class audSpeechSound;
#include "audiosoundtypes/speechsound.h"
#define AUD_DEFERRED_SPEECH_DEBUG_PRINT (__DEV && 1)
#define AUD_MAX_PHONE_CONVERSATIONS_PER_VOICE (6)
const u8 g_MaxAmbientSpeechSlots = 6;
const u8 g_MaxScriptedSpeechSlots = 8;
const u32 g_PhraseMemorySize = 70;
const u32 g_MaxVariationNameLength = 128;
const u32 g_PhoneConvMemorySize = 100;
enum
{
AUD_SPEECH_SLOT_VACANT,
AUD_SPEECH_SLOT_ALLOCATED,
AUD_SPEECH_SLOT_WALLA,
AUD_SPEECH_SLOT_STOPPING
};
enum
{
AUD_PAIN_SLOT_LOADING,
AUD_PAIN_SLOT_READY,
AUD_PAIN_SLOT_IN_USE,
AUD_PAIN_SLOT_USED_UP,
AUD_PAIN_SLOT_FINISHING
};
enum
{
AUD_PAIN_VOICE_TYPE_PLAYER,
AUD_PAIN_VOICE_TYPE_MALE,
AUD_PAIN_VOICE_TYPE_FEMALE,
AUD_PAIN_VOICE_TYPE_SPECIFIC,
AUD_PAIN_VOICE_TYPE_BREATHING,
AUD_PAIN_VOICE_NUM_TYPES
};
enum
{
AUD_PAIN_LEVEL_WEAK,
AUD_PAIN_LEVEL_NORMAL,
AUD_PAIN_LEVEL_TOUGH,
AUD_PAIN_LEVEL_MIXED,
AUD_PAIN_NUM_LEVELS
};
enum{
AUD_BREATHING_TYPE_NONE,
AUD_BREATHING_TYPE_MICHAEL,
AUD_BREATHING_TYPE_FRANKLIN,
AUD_BREATHING_TYPE_TREVOR,
AUD_BREATHING_TYPE_MP_MALE,
AUD_BREATHING_TYPE_MP_FEMALE,
AUD_BREATHING_TYPE_TOTAL
};
//change back to 2 if we bring back more female types than just normal
#define PAIN_DEBUG_NUM_GENDERS (1)
enum
{
//bank-loaded
AUD_PAIN_ID_PAIN_LOW_WEAK,
AUD_PAIN_ID_PAIN_LOW_GENERIC,
AUD_PAIN_ID_PAIN_LOW_TOUGH,
AUD_PAIN_ID_PAIN_MEDIUM_WEAK,
AUD_PAIN_ID_PAIN_MEDIUM_GENERIC,
AUD_PAIN_ID_PAIN_MEDIUM_TOUGH,
AUD_PAIN_ID_DEATH_LOW_WEAK,
AUD_PAIN_ID_DEATH_LOW_GENERIC,
AUD_PAIN_ID_DEATH_LOW_TOUGH,
AUD_PAIN_ID_HIGH_FALL_DEATH,
AUD_PAIN_ID_CYCLING_EXHALE,
AUD_PAIN_ID_EXHALE,
AUD_PAIN_ID_INHALE,
AUD_PAIN_ID_SHOVE,
AUD_PAIN_ID_WHEEZE,
AUD_PAIN_ID_DEATH_HIGH_SHORT,
AUD_PAIN_ID_PAIN_HIGH_WEAK,
AUD_PAIN_ID_PAIN_HIGH_GENERIC,
AUD_PAIN_ID_PAIN_HIGH_TOUGH,
AUD_PAIN_ID_SCREAM_SHOCKED_WEAK,
AUD_PAIN_ID_SCREAM_SHOCKED_GENERIC,
AUD_PAIN_ID_SCREAM_SHOCKED_TOUGH,
AUD_PAIN_ID_DEATH_GARGLE,
AUD_PAIN_ID_CLIMB_LARGE,
AUD_PAIN_ID_CLIMB_SMALL,
AUD_PAIN_ID_JUMP,
AUD_PAIN_ID_MELEE_SMALL_GRUNT,
AUD_PAIN_ID_MELEE_LARGE_GRUNT,
AUD_PAIN_ID_SNEEZE,
AUD_PAIN_ID_NUM_BANK_LOADED,
//wave-loaded
AUD_PAIN_ID_PAIN_GARGLE,
AUD_PAIN_ID_DEATH_HIGH_MEDIUM,
AUD_PAIN_ID_DEATH_HIGH_LONG,
AUD_PAIN_ID_COUGH,
AUD_PAIN_ID_ON_FIRE,
AUD_PAIN_ID_HIGH_FALL,
AUD_PAIN_ID_SUPER_HIGH_FALL,
AUD_PAIN_ID_SCREAM_PANIC,
AUD_PAIN_ID_SCREAM_PANIC_SHORT,
AUD_PAIN_ID_SCREAM_SCARED,
AUD_PAIN_ID_SCREAM_TERROR_WEAK,
AUD_PAIN_ID_SCREAM_TERROR_GENERIC,
AUD_PAIN_ID_SCREAM_TERROR_TOUGH,
AUD_PAIN_ID_TAZER,
AUD_PAIN_ID_DEATH_UNDERWATER,
AUD_PAIN_ID_COWER,
AUD_PAIN_ID_WHIMPER,
AUD_PAIN_ID_DYING_MOAN,
AUD_PAIN_ID_PAIN_RAPIDS,
AUD_PAIN_ID_NUM_TOTAL
};
enum
{
AUD_PAIN_COUGH,
AUD_HIGH_FALL,
AUD_SUPER_HIGH_FALL,
AUD_HIGH_FALL_DEATH,
AUD_SCREAM_PANIC,
AUD_SCREAM_PANIC_SHORT,
AUD_SCREAM_SCARED,
AUD_SCREAM_SHOCKED,
AUD_SCREAM_TERROR,
AUD_PAIN_INHALE,
AUD_PAIN_EXHALE,
AUD_PAIN_SHOVE,
AUD_PAIN_WHEEZE,
AUD_PAIN_NONE,
AUD_PAIN_LOW,
AUD_PAIN_MEDIUM,
AUD_PAIN_HIGH,
AUD_ON_FIRE,
AUD_DEATH_LOW,
AUD_DEATH_HIGH,
AUD_PAIN_TAZER,
AUD_DEATH_UNDERWATER,
AUD_COWER,
AUD_WHIMPER,
AUD_CLIMB_LARGE,
AUD_CLIMB_SMALL,
AUD_JUMP,
AUD_MELEE_SMALL_GRUNT,
AUD_MELEE_LARGE_GRUNT,
AUD_DYING_MOAN,
AUD_CYCLING_EXHALE,
AUD_PAIN_RAPIDS,
AUD_PAIN_SNEEZE,
AUD_NUM_PAIN_TYPES
};
class audSpeechAudioEntity;
namespace rage {class audWaveSlot;}
struct tVoice;
struct audSpeechSlot
{
public:
audSpeechSlot() :
PlayState(AUD_SPEECH_SLOT_VACANT),
SpeechAudioEntity(NULL),
WaveSlot(NULL),
Voicename(0),
VariationNumber(-1),
Priority(0.0f),
WallaPlayCount(0),
TimeCanPlayWalla(0),
UseFirstHalfForWalla(true)
{
#if __BANK
Context[0] = '\0';
#endif
};
u8 PlayState;
audSpeechAudioEntity* SpeechAudioEntity;
audWaveSlot* WaveSlot;
u32 Voicename;
#if __BANK
char Context[64];
#endif
s32 VariationNumber;
f32 Priority;
u32 WallaPlayCount;
u32 TimeCanPlayWalla;
bool UseFirstHalfForWalla;
};
struct audPainSlot
{
public:
audPainSlot() :
SlotState(AUD_PAIN_SLOT_USED_UP),
PainLevel(AUD_PAIN_LEVEL_NORMAL),
WaveSlot(NULL),
BankId(0),
VoiceNameHash(0)
{
#if __BANK
VoiceName[0] = '\0';
#endif
};
u8 SlotState;
u8 PainLevel;
audWaveSlot* WaveSlot;
u16 BankId;
u32 VoiceNameHash;
#if __BANK
char VoiceName[64];
#endif
};
struct audAnimalContextInfo
{
audAnimalContextInfo() :
ContextPHash(0),
NumVars(0),
LastVarUsed(0),
NextTimeCanPlay(0),
MinTimeBetween(-1),
MaxTimeBetween(-1),
UsedUp(0),
ForcesSwap(false)
{
};
u32 ContextPHash;
u32 NumVars;
u32 LastVarUsed;
u32 NextTimeCanPlay;
s32 MinTimeBetween;
s32 MaxTimeBetween;
s8 UsedUp;
bool ForcesSwap;
};
struct audAnimalBankSlot
{
audAnimalBankSlot() :
WaveSlot(NULL),
BankdID(~0U),
VoiceName(0),
Owner(kAnimalNone),
IsLoading(false),
FailedLastTime(false)
{
};
atArray<audAnimalContextInfo> ContextInfo;
audWaveSlot* WaveSlot;
u32 BankdID;
u32 VoiceName;
AnimalType Owner;
bool IsLoading;
bool FailedLastTime;
};
struct audAnimalInfo
{
audAnimalInfo() :
Data(NULL),
AnimalType(kAnimalNone),
Priority(0.0f),
SlotNumber(-1)
{
};
AnimalParams* Data;
AnimalType AnimalType;
f32 Priority;
s8 SlotNumber;
};
struct audAnimalQSortStruct
{
audAnimalQSortStruct() :
AnimalType(kAnimalNone),
Priority(0.0f)
{
};
AnimalType AnimalType;
f32 Priority;
};
class audSpeechManager : public audEntity // it might not actually need to be an audEntity - depends if it actually triggers sounds itself.
{
public:
AUDIO_ENTITY_NAME(audSpeechManager);
#if __BANK
static void AddWidgets(bkBank &bank);
#endif
#if GTA_REPLAY
friend void CReplayMgr::ClearSpeechSlots();
void FreeAllAmbientSpeechSlots();
#endif // GTA_REPLAY
void Init();
void Shutdown();
void Update();
void UpdateAnimalBanks();
bool IsStreamingBusyForContext(u32 contextPHash);
void ReserveAmbientSpeechSlotForScript();
void ReleaseAmbientSpeechSlotForScript();
s32 GetAmbientSpeechSlot(audSpeechAudioEntity* requestingEntity, bool* hadToStopSpeech=NULL, f32 priority=3.0, scrThreadId scriptThreadId = THREAD_INVALID
#if AUD_DEFERRED_SPEECH_DEBUG_PRINT
, const char* context = NULL
#endif
, bool isHeli = false);
f32 GetRealAmbientSpeechPriority(audSpeechAudioEntity* requestingEntity, f32 priority);
void MarkSlotAsAllocated(int slotIndex, audSpeechAudioEntity* requestingEntity, f32 priority);
void FreeAmbientSpeechSlot(s32 slot, bool okForEntityToBeNull = false);
void PopulateAmbientSpeechSlotWithPlayingSpeechInfo(s32 slot, u32 voiceName,
#if __BANK
const char* context,
#endif
s32 variation);
bool IsSlotVacant(s32 slotIndex);
bool IsSafeToReuseSlot(s32 slotIndex, const audSpeechAudioEntity* entity);
bool IsScriptSpeechSlotVacant(s32 slotIndex);
s32 GetScriptSpeechSlotRefCount(s32 slotIndex);
s32 GetScriptedSpeechSlot(audSpeechAudioEntity* requestingEntity);
void FreeAllScriptedSpeechSlots();
void FreeScriptedSpeechSlot(s32 slot);
u32 GetNumVacantAmbientSlots();
s32 WhenWasVariationLastPlayed(u32 voiceHash, u32 contextHash, u32 variationNum, u32& timeInMs);
void BuildMemoryListForVoiceAndContext(u32 voiceHash, u32 contextHash, u32& memorySize, u32 aPhraseMemory[g_PhraseMemorySize][4]);
void AddVariationToHistory(u32 voiceHash, u32 contextHash, u32 variationNum, u32 timeInMs);
void GetTimeWhenContextCanNextPlay(SpeechContext *speechContext, u32* timeCanNextPlay, bool* isControlledByContext);
void GetTimeWhenTriggeredPrimaryContextCanNextPlay(TriggeredSpeechContext *speechContext, u32* timeCanNextPlay, bool* isControlledByContext);
void GetTimeWhenTriggeredBackupContextCanNextPlay(TriggeredSpeechContext *speechContext, u32* timeCanNextPlay, bool* isControlledByContext);
void UpdateTimeContextWasLastUsed(const char *contextName, CPed* speaker, u32 timeInMs);
void UpdateTimeContextWasLastUsed(SpeechContext *speechContext, u32 timeInMs);
u32 GetRepeatTimeContextCanPlayOnVoice(SpeechContext *speechContext);
u32 GetRepeatTimePrimaryTriggeredContextCanPlayOnVoice(TriggeredSpeechContext *speechContext);
u32 GetRepeatTimeBackupTriggeredContextCanPlayOnVoice(TriggeredSpeechContext *speechContext);
u32 GetTimeLastPlayedForContext(u32 contextPHash);
void UpdateTimeTriggeredContextWasLastUsed(TriggeredSpeechContext* speechContext, u32 timeInMs);
void UpdateTimePrimaryTriggeredContextWasLastUsed(TriggeredSpeechContext* speechContext, u32 timeInMs);
void UpdateTimeBackupTriggeredContextWasLastUsed(TriggeredSpeechContext* speechContext, u32 timeInMs);
u32 GetVoiceForPhoneConversation(u32 pedVoiceGroup);
u8 GetPhoneConversationVariationFromVoiceHash(u32 voiceHash);
void AddPhoneConversationVariationToHistory(u32 voiceHash, u32 variation, u32 timeInMs);
s32 WhenWasPhoneConversationLastPlayed(u32 voiceHash, u32 variation, u32& timeInMs);
SpeechContext *GetSpeechContext(const char* contextName, CPed* speaker, bool* returningDefault = NULL);
SpeechContext *GetSpeechContext(const char* contextName, CPed* speaker, CPed* replyingPed = NULL, bool* returningDefault = NULL);
SpeechContext *GetSpeechContext(u32 contextHash, CPed* speaker, bool* returningDefault = NULL);
SpeechContext *GetSpeechContext(u32 contextHash, CPed* speaker, CPed* replyingPed=NULL, bool* returningDefault = NULL);
SpeechContext* ResolveVirtualSpeechContext(const SpeechContext* speechContext, CPed* speaker, CPed* replyingPed = NULL);
// Temporary hack to get the 'loudness' of a context - this'll live in the speech game object very soon
//s32 GetVolumeForContext(const char* context);
// New, proper version, that goes to the game object
//s32 GetVolumeForContext(const s32 contextIndex);
bool DoesContextHaveANoGenderVersion(SpeechContext *speechContext);
audWaveSlot* GetAmbientWaveSlotFromId(s32 slotId);
audWaveSlot* GetScriptedWaveSlotFromId(s32 slotId);
void SpeechAudioEntityBeingDeleted(audSpeechAudioEntity* audioEntity);
bool IsPainLoaded(u32 painVoiceType);
const char* GetPainLevelString(u8 toughness, bool shouldLowerToughness, bool& isSpecificPain) const;
audWaveSlot* GetWaveSlotForPain(u32 painVoiceType);
u8 GetBankNumForPain(u32 painVoiceType);
void PlayedPain(u32 painVoiceType, u32 painType);
u32 GetVariationForPain(u32 painVoiceType, u32 painType);
u32 GetWaveLoadedPainVoiceFromVoiceType(u32 painVoiceType, const CPed* ped);
#if GTA_REPLAY
void PlayedPainForReplay(u32 painVoiceType, u32 painType, u8 numTimes, u8 nextVariation, u32 nextTimeToLoad, bool isJumpingOrRwnd, bool isGoingForwards);
void GetPlayedPainForReplayVars(u32 painVoiceType, u8* numTimes, u8* nextVariation, u32& nextTimeToLoad, u8& currentPainBank) const;
void SetPlayedPainForReplayVars(u32 painVoiceType, const u8* numTimes, const u8* nextVariation, const u32& nextTimeToLoad, const u8& currentPainBank);
void SetBankNumForPain(u32 painVoiceType, u8 bank);
void SetVariationForPain(u32 painVoiceType, u32 painType, u8 variation);
u8 GetNumTimesPlayed(u32 painVoiceType, u32 painType) const;
u32 GetNextTimeToLoad(u32 painVoiceType) const;
#endif // GTA_REPLAY
void PlayedBreath(PlayerVocalEffectType vfxType);
const char* GetBreathingInfo(PlayerVocalEffectType eEffectType, u32& voiceNameHash, u8& variation);
u32 GetPrimaryVoice(u32 pedVoiceGroup);
u32 GetGangVoice(u32 pedVoiceGroup);
u32 GetBestVoice(u32 pedVoiceGroup, u32 contextPHash, CPed* speaker, bool allowBackupPVG=true, s32* overallMinReferenceArg=NULL);
void AddReferenceToVoice(u32 pedVoiceGroup, u32 voiceNameHash BANK_ONLY(, audSpeechAudioEntity* speechEnt = NULL));
void RemoveReferenceToVoice(u32 pedVoiceGroup, u32 voiceNameHash);
u32 GetBestPrimaryVoice(PedVoiceGroups* pedVoiceGroupObject, s32& minReferences, u32 contextPartialHash=0);
u32 GetBestMiniVoice(PedVoiceGroups* pedVoiceGroupObject, s32& minReferences, u32 contextPartialHash);
u16 GetPossibleConversationTopicsForPvg(u32 pvg);
void SetPlayerPainRootBankName(const CPed& newPlayer);
void SetPlayerPainRootBankName(const char* bankName);
s32 GetVariationForNearAnimalContext(AnimalType animalType, const char* context);
s32 GetVariationForNearAnimalContext(AnimalType animalType, u32 contextPHash);
void NearAnimalContextPlayed(AnimalType animalType, u32 contextPHash);
void FarAnimalContextPlayed(u32 contextPHash, u32 timeInMs);
bool CanFarAnimalContextPlayed(u32 contextPHash, u32 timeInMs);
void GetWaveSlotAndVoiceNameForAnimal(AnimalType animalType, audWaveSlot*& waveSlot, u32& voiceName);
void IncrementAnimalAppearance(AnimalType animalType) {m_NumAnimalAppearancesSinceLastUpdate[animalType]++;}
void SetIfNearestAnimalInstance(AnimalType animalType, Vec3V_In pos);
void IncrementAnimalsMakingSound(AnimalType animalType);
void DecrementAnimalsMakingSound(AnimalType animalType);
u8 GetNumAnimalsMakingSound(AnimalType animalType);
void ForceAnimalBankUpdate() {m_ForceAnimalBankUpdate = true;}
bool GetIsCurrentlyLoadingAnimalBank();
void RegisterPedAimedAtOrHurt(u8 toughness, bool shouldLowerToughness, bool hurt);
void RequestTennisBanks(CPed* opponent BANK_ONLY(, bool debugingTennis=false));
void UnrequestTennisBanks();
audWaveSlot* GetTennisVocalizationInfo(CPed* m_Parent, const TennisVocalEffectType& tennisVFXType, u32& contextPhash, u32& voiceHash, u32& variation);
bool GetIsTennisUsingAnimalBanks() { return m_TennisIsUsingAnimalBanks; }
s32 GetTennisScriptId() { return m_TennisScriptIndex; }
/*
#if __DEV
void GetMaxReferencesForPVG(u32 pvg, s32& full, s32& gang, s32& mini, s32& miniDriving);
#endif
*/
#if GTA_REPLAY
void SetReplayPainBanks(u8 prevBank, u8 nextBank, bool backwards)
{
if(m_ReplayNeedToReconcile == false)
{ // Replay hasn't started modifying vars yet so copy across what
// they all should be
for(int pvt = 0; pvt < AUD_PAIN_VOICE_NUM_TYPES; ++pvt)
{
for(int pt = 0; pt < AUD_PAIN_ID_NUM_BANK_LOADED; ++pt)
{
m_ReplayNumTimesPainPlayed[pvt][pt] = m_NumTimesPainPlayed[pvt][pt];
m_ReplayNextVariationForPain[pvt][pt] = m_NextVariationForPain[pvt][pt];
}
m_ReplayNextTimeToLoadPain[pvt] = m_NextTimeToLoadPain[pvt];
}
}
m_ReplayPrevPainBank = prevBank;
m_ReplayNextPainBank = nextBank;
m_ReplayNeedToReconcile = true;
m_ReplayWaveBankChangeWasLast = true;
m_ReplayWasGoingForwards = !backwards;
}
void ReconcilePainBank()
{
//
if(m_ReplayNeedToReconcile)
{
u8 nextPainBank = m_ReplayNextPainBank;
// Special case for going backwards...
// If a bank change was the most recent action then go to the next
// bank
if(m_ReplayWaveBankChangeWasLast && !m_ReplayWasGoingForwards)
{
nextPainBank = (nextPainBank % 2) + 1;
}
// See if we're going to need to force a load of the next bank...
if(m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] != nextPainBank)
{
m_NeedToLoad[AUD_PAIN_VOICE_TYPE_PLAYER] = true;
}
m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] = nextPainBank;
// If we need to load the next bank then go backwards one as the loading will increment for us
if(m_NeedToLoad[AUD_PAIN_VOICE_TYPE_PLAYER] == true)
{
m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] = (m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] % 2) + 1;
}
// Loop through and set all the managers variables from what we've stored from replay
bool skipBankAhead = false;
for(int pvt = 0; pvt < AUD_PAIN_VOICE_NUM_TYPES; ++pvt)
{
for(int pt = 0; pt < AUD_PAIN_ID_NUM_BANK_LOADED; ++pt)
{
m_NumTimesPainPlayed[pvt][pt] = m_ReplayNumTimesPainPlayed[pvt][pt];
m_NextVariationForPain[pvt][pt] = m_ReplayNextVariationForPain[pvt][pt];
// Is this bank going to be out of pains to play?
if(m_NumTimesPainPlayed[pvt][pt] == m_NumPainInCurrentBank[pvt][pt])
skipBankAhead = true;
}
m_NextTimeToLoadPain[pvt] = m_ReplayNextTimeToLoadPain[pvt];
}
if(skipBankAhead && m_ReplayWasGoingForwards && !m_ReplayWaveBankChangeWasLast)
{
m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] = (m_CurrentPainBank[AUD_PAIN_VOICE_TYPE_PLAYER] % 2) + 1;
}
}
// Reset vars
m_ReplayNeedToReconcile = false;
m_ReplayWaveBankChangeWasLast = false;
m_ReplayWasGoingForwards = false;
}
#endif // GTA_REPLAY
#if __BANK
void ResetSpecificPainDrawStats();
#endif
private:
void UpdatePainSlots();
void SelectSpecificPainBank(u32 currentTime);
void ResetSpecificPainBankTrackingVars();
void UpdateBreathingBanks();
const char* GetRandomContextForChurn();
void UpdateGlobalSpeechMetrics();
void LoadAnimalBank(AnimalType animalId, s8 slotNum=-1);
void SortAnimalList(audAnimalQSortStruct* outList);
bool IsAMiniDrivingContext(char* context);
bool IsAMiniNonDrivingContext(char* context);
bool IsAGangContext(char* context);
u8 GetMiniVoiceType(const u32 voiceNameHash);
u32 GetBestGangVoice(PedVoiceGroups* pedVoiceGroupObject, s32& minReferences, u32 contextPartialHash);
tVoice* GetPrimaryVoices(u32 pedVoiceGroup, u8& numPrimaryVoices);
tVoice* GetMiniVoices(u32 pedVoiceGroup, u8& numMiniVoices);
tVoice* GetGangVoices(u32 pedVoiceGroup, u8& numMiniVoices);
tVoice* GetGangVoices(PedVoiceGroups* pedVoiceGroupObject, u8& numMiniVoices);
void UpdateTennisBanks();
void LoadTennisBanks();
#if __BANK
void DrawSpeechSlots();
void DrawPainPlayedTotals();
void DrawSpecificPainInfo();
void DrawAnimalBanks();
#endif
audSpeechSlot m_AmbientSpeechSlots[g_MaxAmbientSpeechSlots];
u8 m_AmbientSpeechSlotToNextSearchFrom;
audSpeechSlot m_ScriptedSpeechSlots[g_MaxScriptedSpeechSlots];
//audSpeechSlot m_PlayerSpeechSlot;
audSpeechSound* m_ChurnSpeechSound;
s32 m_ChurnSlot;
scrThreadId m_ScriptOwningReservedSlot;
u8 m_ScriptReservedSlotNum;
atRangeArray<audPainSlot, AUD_PAIN_VOICE_NUM_TYPES+1> m_PainSlots;
atRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES> m_CurrentPainBank;
atRangeArray<u32, AUD_PAIN_VOICE_NUM_TYPES> m_NextTimeToLoadPain;
atMultiRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES, AUD_PAIN_ID_NUM_BANK_LOADED> m_NextVariationForPain;
atMultiRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES, AUD_PAIN_ID_NUM_BANK_LOADED> m_NumTimesPainPlayed;
atMultiRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES, AUD_PAIN_ID_NUM_BANK_LOADED> m_NumPainInCurrentBank;
atRangeArray<u32, AUD_PAIN_VOICE_NUM_TYPES> m_TimeLastLoaded;
u32 m_TimeSpecificBankLastLoaded;
s8 m_PainTypeLoading;
s8 m_SpecificPainLevelToLoad;
s8 m_SpecificPainLevelLoaded;
bool m_SpecificPainToLoadIsMale;
bool m_SpecificPainLoadingIsMale;
bool m_SpecificPainLoadedIsMale;
atRangeArray<bool, AUD_PAIN_VOICE_NUM_TYPES> m_NeedToLoad;
#if GTA_REPLAY
u8 m_ReplayNextPainBank;
bool m_ReplayShouldSkipBankOnJump;
bool m_ReplayNeedToReconcile;
u8 m_ReplayPrevPainBank;
atMultiRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES, AUD_PAIN_ID_NUM_BANK_LOADED> m_ReplayNumTimesPainPlayed;
atMultiRangeArray<u8, AUD_PAIN_VOICE_NUM_TYPES, AUD_PAIN_ID_NUM_BANK_LOADED> m_ReplayNextVariationForPain;
atRangeArray<u32, AUD_PAIN_VOICE_NUM_TYPES> m_ReplayNextTimeToLoadPain;
bool m_ReplayBankVarsForJumping;
bool m_ReplayWaveBankChangeWasLast;
bool m_ReplayWasGoingForwards;
#endif // GTA_REPLAY
u32 m_LastTimeSpecificPainBankSelected;
//3 for WEAK, NORMAL, TOUGH, 2 for MALE, FEMALE
atMultiRangeArray<u32, 3, 2> m_PedsAimedAtOrHurtSinceLastSpecificPainBankSelected;
// 0 is voice, 1 is contexthash, 2 is the variation numm 3 is time
atMultiRangeArray<u32, g_PhraseMemorySize, 4> m_PhraseMemory;
u32 m_NextPhraseMemoryWriteIndex;
// 0 is voice, 1 is last variation used, 2 is time the variation last played
atMultiRangeArray<u32, g_PhoneConvMemorySize,3> m_PhoneConversationMemory;
u32 m_NextPhoneConvMemoryWriteIndex;
atRangeArray<u8, AUD_NUM_PLAYER_VFX_TYPES> m_BreathVariationsInCurrentBank;
atRangeArray<u8, AUD_NUM_PLAYER_VFX_TYPES> m_NextVariationForBreath;
u8 m_BreathingTypeCurrentlyLoaded;
u8 m_BreathingTypeToLoad;
atArray<audAnimalBankSlot> m_AnimalBankSlots;
atRangeArray<audAnimalInfo, ANIMALTYPE_MAX> m_AnimalInfoArray;
atRangeArray<u32, ANIMALTYPE_MAX> m_NumAnimalAppearancesSinceLastUpdate;
atRangeArray<f32, ANIMALTYPE_MAX> m_NearestAnimalAppearancesSinceLastUpdate;
atRangeArray<u32, ANIMALTYPE_MAX> m_NextAnimalBankNumToLoad;
atRangeArray<u32, ANIMALTYPE_MAX> m_NumAnimalBanks;
audAnimalQSortStruct m_SortedAnimalList[ANIMALTYPE_MAX];
atRangeArray<u8, ANIMALTYPE_MAX> m_NumAnimalsMakingSound;
u32 m_TimeOfLastFullAnimalBankUpdate;
u16 m_MaxNumAnimalSlots;
bool m_ForceAnimalBankUpdate;
bool m_LoadingAfterAnimalUpdate;
atMultiRangeArray<u32, 2, AUD_NUM_PLAYER_VFX_TYPES> m_NumTennisContextPHashes;
atMultiRangeArray<u32, 2, AUD_NUM_PLAYER_VFX_TYPES> m_NumTennisVariations;
atMultiRangeArray<u32, 2, AUD_NUM_PLAYER_VFX_TYPES> m_LastTennisVariations;
RegdPed m_TennisOpponent;
s32 m_TennisScriptIndex;
s8 m_CurrentTennisSlotLoading;
bool m_TennisIsUsingAnimalBanks;
bool m_TennisOpponentIsMale;
bool m_TennisPlayerIsMichael;
bool m_TennisPlayerIsFranklin;
bool m_TennisPlayerIsTrevor;
#if __BANK
u32 m_DebugSpecPainCountTotal;
u32 m_DebugNonSpecPainCountTotal;
atMultiRangeArray<u32,AUD_PAIN_LEVEL_MIXED,PAIN_DEBUG_NUM_GENDERS> m_DebugSpecPainTimeLastLoaded;
atMultiRangeArray<u32,AUD_PAIN_LEVEL_MIXED,PAIN_DEBUG_NUM_GENDERS> m_DebugNonSpecPainCount;
u32 m_DebugSpecPainBankLoadCountTotal;
u32 m_DebugNonSpecPainBankLoadCountTotal;
atMultiRangeArray<u32,AUD_PAIN_LEVEL_MIXED,PAIN_DEBUG_NUM_GENDERS> m_DebugSpecPainBankLoadCount;
#endif
};
extern audSpeechManager g_SpeechManager;
#endif // AUD_SPEECHMANAGER_H