2847 lines
90 KiB
C++
2847 lines
90 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE : messages.cpp
|
|
// PURPOSE : Messages handling. Keeps track of messages that are to be displayed.
|
|
// AUTHOR : Obbe, GraemeW, DerekP (13/12/99)
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// C headers
|
|
#include <stdio.h>
|
|
|
|
// fw headers
|
|
#include "fwsys/timer.h"
|
|
#include "fwmaths/angle.h"
|
|
|
|
// game headers
|
|
#include "messages.h"
|
|
#include "audio/frontendaudioentity.h"
|
|
#include "camera/viewports/ViewportManager.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/debug/DebugDirector.h"
|
|
#include "camera/Caminterface.h"
|
|
#include "camera/viewports/ViewportManager.h"
|
|
#include "Control/GameLogic.h"
|
|
#include "control/replay/replay.h"
|
|
#include "core/game.h"
|
|
#include "cutscene\CutSceneManagerNew.h"
|
|
#include "debug\Debug.h"
|
|
#include "frontend\BusySpinner.h"
|
|
#include "frontend\PauseMenu.h"
|
|
#include "frontend/WarningScreen.h"
|
|
#include "frontend\NewHud.h"
|
|
#include "Frontend/Scaleform/ScaleFormStore.h"
|
|
#include "frontend/VideoEditor/VideoEditorInterface.h"
|
|
#include "frontend/ui_channel.h"
|
|
#include "game\Clock.h"
|
|
#include "peds/Ped.h"
|
|
#include "Stats\StatsMgr.h"
|
|
#include "renderer/DrawLists/DrawListMgr.h"
|
|
#include "SaveLoad/savegame_autoload.h"
|
|
#include "SaveLoad/savegame_messages.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "script/script_channel.h"
|
|
#include "script/script_hud.h"
|
|
#include "system/controlMgr.h"
|
|
#include "Text\Text.h"
|
|
#include "Text\TextFormat.h"
|
|
#include "Text\TextConversion.h"
|
|
|
|
// Parser headers
|
|
#include "parser/manager.h"
|
|
#include "Text\dialogueCharacters_parser.h"
|
|
|
|
|
|
#include "data/aes_init.h"
|
|
AES_INIT_C;
|
|
|
|
TEXT_OPTIMISATIONS()
|
|
|
|
CQueuedMessage CMessages::BriefMessages[MAX_NUM_BRIEF_MESSAGES];
|
|
|
|
CPreviousMessages CMessages::PreviousDialogueMessages;
|
|
CPreviousMessages CMessages::PreviousMissionMessages;
|
|
CPreviousMessages CMessages::PreviousHelpTextMessages;
|
|
|
|
bool CMessages::bTitleActive = false;
|
|
|
|
u32 CMessages::m_PreviousSubtitleTimer = 0;
|
|
|
|
char CHelpMessage::m_HelpMessageText[MAX_HELP_TEXT_SLOTS][MAX_CHARS_IN_MESSAGE];
|
|
bool CHelpMessage::m_bHelpMessagesFading[MAX_HELP_TEXT_SLOTS];
|
|
bool CHelpMessage::m_bTriggerSound[MAX_HELP_TEXT_SLOTS];
|
|
CHelpMessage::eTEXT_STATE CHelpMessage::m_iNewText[MAX_HELP_TEXT_SLOTS];
|
|
eTEXT_STYLE CHelpMessage::m_iStyle[MAX_HELP_TEXT_SLOTS];
|
|
eTEXT_ARROW_ORIENTATION CHelpMessage::m_iArrowDirection[MAX_HELP_TEXT_SLOTS];
|
|
s32 CHelpMessage::m_iFloatingTextOffset[MAX_HELP_TEXT_SLOTS];
|
|
Color32 CHelpMessage::m_iColour[MAX_HELP_TEXT_SLOTS];
|
|
Vector2 CHelpMessage::m_vScreenPosition[MAX_HELP_TEXT_SLOTS];
|
|
s32 CHelpMessage::m_OverrideDuration[MAX_HELP_TEXT_SLOTS];
|
|
|
|
// Dialogue Character Data
|
|
#define DEFAULT_CHARACTER_TXD "CHAR_DEFAULT"
|
|
#define DIALOGUE_CHARACTERS_META_FILE "common:/data/ui/dialogueCharacters"
|
|
|
|
SDialogueCharacters CMessages::ms_dialogueCharacters;
|
|
|
|
// ******************************************************************************************************
|
|
// ** SDialogueCharacters
|
|
// ******************************************************************************************************
|
|
// Gets the character txd associated with the specified voice name hash
|
|
const char* SDialogueCharacters::GetCharacterTXD(u32 iVoiceNameHash, u32 iMissionNameHash/*=0*/)
|
|
{
|
|
if(iVoiceNameHash != 0)
|
|
{
|
|
for(int i = 0; i < m_characterInfo.GetCount(); ++i)
|
|
{
|
|
u32 charMissionHash = m_characterInfo[i].m_MissionNameHash;
|
|
if ( m_characterInfo[i].m_VoiceNameHash == iVoiceNameHash &&
|
|
( charMissionHash == 0 || charMissionHash == iMissionNameHash) )
|
|
{
|
|
return m_characterInfo[i].m_txd.GetCStr();
|
|
}
|
|
}
|
|
}
|
|
|
|
return DEFAULT_CHARACTER_TXD;
|
|
}
|
|
|
|
// ******************************************************************************************************
|
|
// ** CNumberWithinMessage
|
|
// ******************************************************************************************************
|
|
void CNumberWithinMessage::Set(s32 IntegerToDisplay)
|
|
{
|
|
m_Integer = IntegerToDisplay;
|
|
m_NumberOfDecimalPlaces = DISPLAY_INTEGER;
|
|
}
|
|
|
|
void CNumberWithinMessage::Set(float FloatToDisplay, s32 NumberOfDecimalPlaces)
|
|
{
|
|
m_Float = FloatToDisplay;
|
|
m_NumberOfDecimalPlaces = NumberOfDecimalPlaces;
|
|
}
|
|
|
|
|
|
// Returns length of new string
|
|
s32 CNumberWithinMessage::FillString(char *pStringToFill, s32 MaxLengthOfString) const
|
|
{
|
|
switch (m_NumberOfDecimalPlaces)
|
|
{
|
|
case NO_NUMBER_TO_DISPLAY :
|
|
Assertf(0, "CNumberWithinMessage::FillString - text contains a ~1~ but there is no number to display");
|
|
safecpy(pStringToFill, "", MaxLengthOfString);
|
|
break;
|
|
|
|
case DISPLAY_INTEGER : // Treat as an integer
|
|
formatf(pStringToFill, MaxLengthOfString, "%d", m_Integer);
|
|
break;
|
|
|
|
default : // Treat as a float
|
|
Assertf(m_NumberOfDecimalPlaces >= 0, "CNumberWithinMessage::FillString - number of decimal places (%d) for float (%f) is less than 0", m_NumberOfDecimalPlaces, m_Float);
|
|
Assertf(m_NumberOfDecimalPlaces < 5, "CNumberWithinMessage::FillString - trying to display a floating point number (%f) to more than 5 decimal places (%d)", m_Float, m_NumberOfDecimalPlaces);
|
|
formatf(pStringToFill, MaxLengthOfString, "%.*f", m_NumberOfDecimalPlaces, m_Float);
|
|
break;
|
|
}
|
|
|
|
return istrlen(pStringToFill);
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CNumberWithinMessage::DebugPrintContents()
|
|
{
|
|
switch (m_NumberOfDecimalPlaces)
|
|
{
|
|
case NO_NUMBER_TO_DISPLAY :
|
|
break;
|
|
|
|
case DISPLAY_INTEGER : // Treat as an integer
|
|
scriptDisplayf("Integer Value = %d", m_Integer);
|
|
break;
|
|
|
|
default : // Treat as a float
|
|
Assertf(m_NumberOfDecimalPlaces >= 0, "CNumberWithinMessage::DebugPrintContents - number of decimal places (%d) for float (%f) is less than 0", m_NumberOfDecimalPlaces, m_Float);
|
|
Assertf(m_NumberOfDecimalPlaces < 5, "CNumberWithinMessage::DebugPrintContents - trying to display a floating point number (%f) to more than 5 decimal places (%d)", m_Float, m_NumberOfDecimalPlaces);
|
|
scriptDisplayf("Float Value = %.*f", m_NumberOfDecimalPlaces, m_Float);
|
|
break;
|
|
}
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
|
|
bool CNumberWithinMessage::operator==(const CNumberWithinMessage& otherNumber) const
|
|
{
|
|
// Should I be comparing m_Colour as well?
|
|
|
|
if (m_NumberOfDecimalPlaces == otherNumber.m_NumberOfDecimalPlaces)
|
|
{
|
|
switch (m_NumberOfDecimalPlaces)
|
|
{
|
|
case NO_NUMBER_TO_DISPLAY :
|
|
return true;
|
|
break;
|
|
|
|
case DISPLAY_INTEGER :
|
|
if (m_Integer == otherNumber.m_Integer)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
default :
|
|
if (m_Float == otherNumber.m_Float)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CSubStringWithinMessage
|
|
// ******************************************************************************************************
|
|
void CSubStringWithinMessage::SetTextLabel(const char *pTextLabel)
|
|
{
|
|
m_pText = TheText.Get(pTextLabel);
|
|
m_BlockContainingThisText = TheText.GetBlockContainingLastReturnedString();
|
|
}
|
|
|
|
void CSubStringWithinMessage::SetTextLabelHashKey(u32 HashKeyOfTextLabel)
|
|
{
|
|
char StringContainingHashKey[24];
|
|
formatf(StringContainingHashKey, 24, "%u", HashKeyOfTextLabel);
|
|
|
|
m_pText = TheText.Get(HashKeyOfTextLabel, StringContainingHashKey);
|
|
m_BlockContainingThisText = TheText.GetBlockContainingLastReturnedString();
|
|
}
|
|
|
|
void CSubStringWithinMessage::SetCharPointer(const char *pLocalizedString)
|
|
{
|
|
m_pText = pLocalizedString;
|
|
m_BlockContainingThisText = CHAR_POINTER;
|
|
}
|
|
|
|
void CSubStringWithinMessage::SetLiteralString(const char *pLiteralString, eLiteralStringType literalStringType, bool bUseDebugPortionOfSingleFrameArray)
|
|
{
|
|
uLiteralString.m_LiteralStringIndex = static_cast<s16>(CScriptHud::ScriptLiteralStrings.SetLiteralString(pLiteralString, literalStringType, bUseDebugPortionOfSingleFrameArray));
|
|
uLiteralString.m_LiteralStringType = static_cast<u16>(literalStringType);
|
|
|
|
m_BlockContainingThisText = LITERAL_STRING;
|
|
}
|
|
|
|
void CSubStringWithinMessage::SetPersistentLiteralStringOccursInSubtitles(bool bOccursInSubtitles)
|
|
{
|
|
if (Verifyf(m_BlockContainingThisText == LITERAL_STRING, "CSubStringWithinMessage::SetPersistentLiteralStringOccursInSubtitles - expected this string to be a literal string"))
|
|
{
|
|
if (Verifyf(uLiteralString.m_LiteralStringType == LITERAL_STRING_TYPE_PERSISTENT, "CSubStringWithinMessage::SetPersistentLiteralStringOccursInSubtitles - Expected literal strings in subtitles to always be persistent"))
|
|
{
|
|
CScriptHud::ScriptLiteralStrings.SetPersistentLiteralOccursInSubtitles(uLiteralString.m_LiteralStringIndex, bOccursInSubtitles);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSubStringWithinMessage::Clear(bool ClearPersistentLiterals, bool bClearingFromPreviousBriefs)
|
|
{
|
|
if (m_BlockContainingThisText == LITERAL_STRING)
|
|
{
|
|
if (uLiteralString.m_LiteralStringType == LITERAL_STRING_TYPE_PERSISTENT)
|
|
{
|
|
if (ClearPersistentLiterals)
|
|
{
|
|
CScriptHud::ScriptLiteralStrings.ClearPersistentString(uLiteralString.m_LiteralStringIndex, bClearingFromPreviousBriefs);
|
|
}
|
|
}
|
|
uLiteralString.m_LiteralStringIndex = -1;
|
|
}
|
|
else
|
|
{
|
|
m_pText = NULL;
|
|
}
|
|
|
|
m_BlockContainingThisText = NO_STRING_TO_DISPLAY;
|
|
m_Colour = HUD_COLOUR_INVALID;
|
|
}
|
|
|
|
bool CSubStringWithinMessage::UsesTextFromThisBlock(s32 TextBlockIndex)
|
|
{
|
|
if (m_BlockContainingThisText == TextBlockIndex)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
u32 CSubStringWithinMessage::GetStringCharacterCount()
|
|
{
|
|
if (m_BlockContainingThisText == NO_STRING_TO_DISPLAY)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return CTextConversion::GetCharacterCount(GetString());
|
|
}
|
|
|
|
|
|
const char *CSubStringWithinMessage::GetString() const
|
|
{
|
|
switch (m_BlockContainingThisText)
|
|
{
|
|
case NO_STRING_TO_DISPLAY :
|
|
return NULL;
|
|
|
|
case LITERAL_STRING :
|
|
if ( (uLiteralString.m_LiteralStringType == LITERAL_STRING_TYPE_SINGLE_FRAME) && !CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{ // If we're trying to access a single frame literal outside the Render Thread then we need to access the write buffer of the double buffer
|
|
return CScriptHud::ScriptLiteralStrings.GetSingleFrameLiteralStringFromWriteBuffer(uLiteralString.m_LiteralStringIndex);
|
|
}
|
|
else
|
|
{
|
|
return CScriptHud::ScriptLiteralStrings.GetLiteralString(uLiteralString.m_LiteralStringIndex, (CSubStringWithinMessage::eLiteralStringType) uLiteralString.m_LiteralStringType);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return m_pText;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSubStringWithinMessage::DetermineWhichPreviousBriefArrayToUse(bool &bAddToPreviousBriefs, bool &bAddToDialogueBriefs)
|
|
{
|
|
switch (m_BlockContainingThisText)
|
|
{
|
|
case GTA5_GLOBAL_TEXT_SLOT : // main text block
|
|
case LITERAL_STRING : // not sure what to do for this - going to try leaving the two flags as they are
|
|
case NO_STRING_TO_DISPLAY : // not sure what to do for this - going to try leaving the two flags as they are
|
|
case MISSION_TEXT_SLOT :
|
|
case CREDITS_TEXT_SLOT :
|
|
case MINIGAME_TEXT_SLOT :
|
|
case ODDJOB_TEXT_SLOT :
|
|
case DLC_TEXT_SLOT0 :
|
|
case DLC_TEXT_SLOT1 :
|
|
case DLC_TEXT_SLOT2 :
|
|
break;
|
|
|
|
case MISSION_DIALOGUE_TEXT_SLOT :
|
|
case AMBIENT_DIALOGUE_TEXT_SLOT :
|
|
case DLC_MISSION_DIALOGUE_TEXT_SLOT :
|
|
case DLC_AMBIENT_DIALOGUE_TEXT_SLOT :
|
|
case DLC_MISSION_DIALOGUE_TEXT_SLOT2 :
|
|
bAddToDialogueBriefs = true;
|
|
break;
|
|
default :
|
|
bAddToPreviousBriefs = false;
|
|
bAddToDialogueBriefs = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CSubStringWithinMessage::SetLiteralStringOccursInPreviousBriefs()
|
|
{
|
|
if (m_BlockContainingThisText == LITERAL_STRING)
|
|
{
|
|
if (Verifyf(uLiteralString.m_LiteralStringType == LITERAL_STRING_TYPE_PERSISTENT, "CSubStringWithinMessage::SetLiteralStringOccursInPreviousBriefs - Expected literal strings in subtitles to always be persistent"))
|
|
{
|
|
CScriptHud::ScriptLiteralStrings.SetPersistentLiteralOccursInPreviousBriefs(uLiteralString.m_LiteralStringIndex, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CSubStringWithinMessage::operator==(const CSubStringWithinMessage& otherSubString) const
|
|
{
|
|
// Should I be comparing m_Colour as well?
|
|
|
|
// Should I actually be calling CTextConversion::charStrcmp to check that the characters
|
|
// in both strings match?
|
|
if (m_BlockContainingThisText == otherSubString.m_BlockContainingThisText)
|
|
{
|
|
switch (m_BlockContainingThisText)
|
|
{
|
|
case NO_STRING_TO_DISPLAY :
|
|
return true;
|
|
break;
|
|
|
|
case LITERAL_STRING :
|
|
if (uLiteralString.m_LiteralStringIndex == otherSubString.uLiteralString.m_LiteralStringIndex)
|
|
{
|
|
if (uLiteralString.m_LiteralStringType == otherSubString.uLiteralString.m_LiteralStringType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CHAR_POINTER :
|
|
return m_pText == otherSubString.m_pText; // this MAY want to compare strcmps if they're pointing at different values... but none of the other fields do
|
|
|
|
|
|
default :
|
|
if (m_pText == otherSubString.m_pText)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CSubtitleMessage
|
|
// ******************************************************************************************************
|
|
void CSubtitleMessage::Set(char const *pText, s32 TextBlock,
|
|
CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
bool bUsesUnderscore, bool bHelpText, u32 iVoiceNameHash/*=0*/, u32 iMissionNameHash/*=0*/)
|
|
{
|
|
Clear(false, false);
|
|
|
|
if (pArrayOfSubStrings)
|
|
{
|
|
if (Verifyf(SizeOfSubStringArray <= MAX_SUBSTRINGS_IN_SUBTITLES, "CSubtitleMessage::Set - new subtitle has more than %d substrings", MAX_SUBSTRINGS_IN_SUBTITLES))
|
|
{
|
|
for (u32 string_loop = 0; string_loop < SizeOfSubStringArray; string_loop++)
|
|
{
|
|
m_SubstringsWithinMessage[string_loop] = pArrayOfSubStrings[string_loop];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pArrayOfNumbers)
|
|
{
|
|
if (Verifyf(SizeOfNumberArray <= MAX_NUMBERS_IN_SUBTITLES, "CSubtitleMessage::Set - new subtitle has more than %d numbers", MAX_NUMBERS_IN_SUBTITLES))
|
|
{
|
|
for (u32 number_loop = 0; number_loop < SizeOfNumberArray; number_loop++)
|
|
{
|
|
m_NumbersWithinMessage[number_loop] = pArrayOfNumbers[number_loop];
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pMessage = pText;
|
|
m_TextBlockContainingThisMessage = TextBlock;
|
|
|
|
m_bUseUnderscore = bUsesUnderscore;
|
|
m_bIsHelpText = bHelpText;
|
|
m_iVoiceNameHash = iVoiceNameHash;
|
|
m_iMissionNameHash = iMissionNameHash;
|
|
}
|
|
|
|
|
|
void CSubtitleMessage::Clear(bool ClearPersistentLiterals, bool bClearingFromPreviousBriefs)
|
|
{
|
|
m_pMessage = NULL;
|
|
m_TextBlockContainingThisMessage = -1;
|
|
|
|
u32 loop = 0;
|
|
for (loop = 0; loop < MAX_NUMBERS_IN_SUBTITLES; loop++)
|
|
{
|
|
m_NumbersWithinMessage[loop].Clear();
|
|
}
|
|
|
|
for (loop = 0; loop < MAX_SUBSTRINGS_IN_SUBTITLES; loop++)
|
|
{
|
|
m_SubstringsWithinMessage[loop].Clear(ClearPersistentLiterals, bClearingFromPreviousBriefs);
|
|
}
|
|
|
|
m_bUseUnderscore = false;
|
|
m_bIsHelpText = false;
|
|
}
|
|
|
|
bool CSubtitleMessage::UsesTextFromThisBlock(s32 TextBlockIndex)
|
|
{
|
|
if (m_TextBlockContainingThisMessage == TextBlockIndex)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (u32 loop = 0; loop < MAX_SUBSTRINGS_IN_SUBTITLES; loop++)
|
|
{
|
|
if (m_SubstringsWithinMessage[loop].UsesTextFromThisBlock(TextBlockIndex))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CSubtitleMessage::FillInString(char *pStringToFillIn, u32 MaxLengthOfString)
|
|
{
|
|
CMessages::InsertNumbersAndSubStringsIntoString(m_pMessage,
|
|
m_NumbersWithinMessage, MAX_NUMBERS_IN_SUBTITLES,
|
|
m_SubstringsWithinMessage, MAX_SUBSTRINGS_IN_SUBTITLES,
|
|
pStringToFillIn, MaxLengthOfString);
|
|
|
|
#if __WIN32PC
|
|
CMessages::InsertPlayerControlKeysInString(pStringToFillIn);
|
|
#endif // __WIN32PC
|
|
}
|
|
|
|
bool CSubtitleMessage::DoesMainTextMatch(CSubtitleMessage &OtherSubtitle)
|
|
{
|
|
if ( strcmp( m_pMessage, OtherSubtitle.m_pMessage ) == 0 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSubtitleMessage::Matches(CSubtitleMessage &OtherSubtitle)
|
|
{
|
|
if (m_pMessage != OtherSubtitle.m_pMessage)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_TextBlockContainingThisMessage != OtherSubtitle.m_TextBlockContainingThisMessage)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
for (u32 number_loop = 0; number_loop < MAX_NUMBERS_IN_SUBTITLES; number_loop++)
|
|
{
|
|
if (m_NumbersWithinMessage[number_loop] != OtherSubtitle.m_NumbersWithinMessage[number_loop])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (u32 sub_string_loop = 0; sub_string_loop < MAX_SUBSTRINGS_IN_SUBTITLES; sub_string_loop++)
|
|
{
|
|
if (m_SubstringsWithinMessage[sub_string_loop] != OtherSubtitle.m_SubstringsWithinMessage[sub_string_loop])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CSubtitleMessage::DetermineWhichPreviousBriefArrayToUse(bool &bAddToPreviousBriefs, bool &bAddToDialogueBriefs, bool &bAddToHelpTextBriefs)
|
|
{
|
|
bAddToPreviousBriefs = true;
|
|
bAddToDialogueBriefs = false;
|
|
|
|
if (m_bIsHelpText)
|
|
{
|
|
bAddToHelpTextBriefs = true;
|
|
return;
|
|
}
|
|
|
|
switch (m_TextBlockContainingThisMessage)
|
|
{
|
|
case GTA5_GLOBAL_TEXT_SLOT : // main text block
|
|
case MISSION_TEXT_SLOT :
|
|
case CREDITS_TEXT_SLOT :
|
|
case MINIGAME_TEXT_SLOT :
|
|
case ODDJOB_TEXT_SLOT :
|
|
case DLC_TEXT_SLOT0 :
|
|
case DLC_TEXT_SLOT1 :
|
|
case DLC_TEXT_SLOT2 :
|
|
break;
|
|
case PHONE_TEXT_SLOT:
|
|
case MISSION_DIALOGUE_TEXT_SLOT :
|
|
case AMBIENT_DIALOGUE_TEXT_SLOT :
|
|
case DLC_MISSION_DIALOGUE_TEXT_SLOT :
|
|
case DLC_AMBIENT_DIALOGUE_TEXT_SLOT :
|
|
case DLC_MISSION_DIALOGUE_TEXT_SLOT2 :
|
|
bAddToDialogueBriefs = true;
|
|
break;
|
|
default :
|
|
bAddToPreviousBriefs = false;
|
|
bAddToDialogueBriefs = false;
|
|
return;
|
|
}
|
|
|
|
for (u32 SubStringLoop = 0; SubStringLoop < MAX_SUBSTRINGS_IN_SUBTITLES; SubStringLoop++)
|
|
{
|
|
m_SubstringsWithinMessage[SubStringLoop].DetermineWhichPreviousBriefArrayToUse(bAddToPreviousBriefs, bAddToDialogueBriefs);
|
|
if (!bAddToPreviousBriefs)
|
|
{
|
|
bAddToDialogueBriefs = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CSubtitleMessage::SetAllLiteralStringsOccurInPreviousBriefs()
|
|
{
|
|
for (u32 sub_string_loop = 0; sub_string_loop < MAX_SUBSTRINGS_IN_SUBTITLES; sub_string_loop++)
|
|
{
|
|
m_SubstringsWithinMessage[sub_string_loop].SetLiteralStringOccursInPreviousBriefs();
|
|
}
|
|
}
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CQueuedMessage
|
|
// ******************************************************************************************************
|
|
void CQueuedMessage::Set(char const *pText, s32 TextBlock,
|
|
u32 Duration, bool bAddToPrevBriefs, ePreviousBriefOverride OverrideOfPreviousBriefList,
|
|
CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
bool bUsesUnderscore, u32 iVoiceNameHash/*=0*/, u32 iMissionNameHash/*=0*/)
|
|
{
|
|
m_MessageDetails.Set(pText, TextBlock,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
bUsesUnderscore, false, iVoiceNameHash, iMissionNameHash);
|
|
|
|
m_Duration = Duration;
|
|
m_WhenStarted = fwTimer::GetTimeInMilliseconds();
|
|
m_bAddToPreviousBriefs = bAddToPrevBriefs;
|
|
m_OverrideBriefList = OverrideOfPreviousBriefList;
|
|
m_bNewlyAddedToTheHeadOfTheQueue = false;
|
|
|
|
// If main text begins with ~z~ then it won't be displayed if subtitles are switched off
|
|
// (There could be a ~x~ before the ~z~)
|
|
// Should I bother testing if pShortText begins with ~z~?
|
|
if (CMessages::IsThisTextSwitchedOffByFrontendSubtitleSetting(pText))
|
|
{
|
|
m_bDisplayEvenIfSubtitlesAreSwitchedOff = false;
|
|
}
|
|
else
|
|
{
|
|
m_bDisplayEvenIfSubtitlesAreSwitchedOff = true;
|
|
}
|
|
}
|
|
|
|
|
|
void CQueuedMessage::Clear(bool ClearPersistentLiterals, bool bClearingFromPreviousBriefs)
|
|
{
|
|
m_MessageDetails.Clear(ClearPersistentLiterals, bClearingFromPreviousBriefs);
|
|
|
|
m_Duration = 0;
|
|
m_WhenStarted = 0;
|
|
|
|
m_bAddToPreviousBriefs = true;
|
|
m_OverrideBriefList = PREVIOUS_BRIEF_NO_OVERRIDE;
|
|
m_bNewlyAddedToTheHeadOfTheQueue = false;
|
|
m_bDisplayEvenIfSubtitlesAreSwitchedOff = false;
|
|
}
|
|
|
|
bool CQueuedMessage::HasDisplayTimeExpired()
|
|
{
|
|
if (fwTimer::GetTimeInMilliseconds() > (m_WhenStarted + m_Duration) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void CQueuedMessage::AddToPreviousBriefsIfRequired()
|
|
{
|
|
if (m_bNewlyAddedToTheHeadOfTheQueue)
|
|
{
|
|
m_bNewlyAddedToTheHeadOfTheQueue = false;
|
|
|
|
if (!IsEmpty())
|
|
{
|
|
if (m_bAddToPreviousBriefs)
|
|
{
|
|
CMessages::AddToPreviousBriefArray(m_MessageDetails, m_OverrideBriefList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ******************************************************************************************************
|
|
// ** CMessages
|
|
// ******************************************************************************************************
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::Init
|
|
// PURPOSE: initialises the class
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::Init()
|
|
{
|
|
const s32 MAX_NUM_PREVIOUS_BRIEF_ITEMS = 20;
|
|
|
|
PreviousHelpTextMessages.Init(MAX_NUM_PREVIOUS_BRIEF_ITEMS);
|
|
PreviousDialogueMessages.Init(MAX_NUM_PREVIOUS_BRIEF_ITEMS);
|
|
PreviousMissionMessages.Init(MAX_NUM_PREVIOUS_BRIEF_ITEMS);
|
|
|
|
InitDialogueMeta();
|
|
}
|
|
|
|
#if __BANK
|
|
void CMessages::InitWidgets()
|
|
{
|
|
bkBank *pBank = BANKMGR.FindBank(UI_DEBUG_BANK_NAME);
|
|
|
|
if (pBank)
|
|
{
|
|
pBank->AddButton("Reload Dialogue Meta File", &CMessages::InitDialogueMeta);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::Init
|
|
// PURPOSE: initialises dialogue character data
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::InitDialogueMeta()
|
|
{
|
|
// Init Dialogue Character Info
|
|
PARSER.LoadObject(DIALOGUE_CHARACTERS_META_FILE, "meta", ms_dialogueCharacters);
|
|
uiDebugf1("Dialogue Character Metadata Loaded");
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::Init
|
|
// PURPOSE: initialises the class at game startup
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::Init(unsigned initMode)
|
|
{
|
|
if(initMode == INIT_SESSION)
|
|
{
|
|
s32 C;
|
|
|
|
SetMissionTitleActive(false);
|
|
|
|
for (C = 0; C < MAX_NUM_BRIEF_MESSAGES; C++)
|
|
{
|
|
BriefMessages[C].Clear(false, false);
|
|
}
|
|
|
|
ClearAllMessagesDisplayedByGame();
|
|
|
|
CHelpMessage::Init();
|
|
CSubtitleText::Init();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::Shutdown
|
|
// PURPOSE: shuts down the class at game shutdown
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::Shutdown(unsigned shutdownMode)
|
|
{
|
|
if(shutdownMode == SHUTDOWN_CORE)
|
|
{
|
|
TheText.Unload();
|
|
PreviousHelpTextMessages.Shutdown();
|
|
PreviousDialogueMessages.Shutdown();
|
|
PreviousMissionMessages.Shutdown();
|
|
CLoadingText::Shutdown();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearAllBriefMessages
|
|
// PURPOSE: Wipes the brief messages array
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearAllBriefMessages()
|
|
{
|
|
PreviousHelpTextMessages.ClearAllPreviousBriefs();
|
|
PreviousDialogueMessages.ClearAllPreviousBriefs();
|
|
PreviousMissionMessages.ClearAllPreviousBriefs();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearAllMessagesDisplayedByGame
|
|
// PURPOSE: This will be called when the language changes and will make sure no
|
|
// messages are displayed on screen
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearAllMessagesDisplayedByGame()
|
|
{
|
|
CHelpMessage::ClearAll();
|
|
ClearMessages();
|
|
ClearAllBriefMessages();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock
|
|
// PURPOSE: clears all messages that belong to the text block passed in, but leaves
|
|
// the rest intact
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearAllDisplayedMessagesThatBelongToThisTextBlock(s32 TextBlock, bool bClearPreviousBriefs) // , bool bIgnoreMissionTitle)
|
|
{
|
|
Assertf(TextBlock >= 0, "ClearAllDisplayedMessagesThatBelongToThisTextBlock - TextBlock index should be >= 0");
|
|
|
|
// Brief Messages
|
|
for (s32 loop = 0; loop < MAX_NUM_BRIEF_MESSAGES; loop++)
|
|
{
|
|
if (BriefMessages[loop].UsesTextFromThisBlock(TextBlock))
|
|
{
|
|
BriefMessages[loop].Clear(true, false);
|
|
}
|
|
}
|
|
|
|
// Brief Messages - shift up any remaining messages to fill the gaps
|
|
s32 CopyToIndex = 0;
|
|
while (CopyToIndex < MAX_NUM_BRIEF_MESSAGES)
|
|
{
|
|
if (BriefMessages[CopyToIndex].IsEmpty())
|
|
{
|
|
s32 CopyFromIndex = (CopyToIndex + 1);
|
|
while ( (CopyFromIndex < MAX_NUM_BRIEF_MESSAGES) && (BriefMessages[CopyFromIndex].IsEmpty()) )
|
|
{
|
|
CopyFromIndex++;
|
|
}
|
|
|
|
if (CopyFromIndex < MAX_NUM_BRIEF_MESSAGES)
|
|
{
|
|
BriefMessages[CopyToIndex] = BriefMessages[CopyFromIndex];
|
|
if (CopyToIndex == 0)
|
|
{
|
|
BriefMessages[0].SetWhenStarted(fwTimer::GetTimeInMilliseconds());
|
|
BriefMessages[0].SetNewlyAddedToTheHeadOfTheQueue(true);
|
|
}
|
|
BriefMessages[CopyFromIndex].Clear(false, false);
|
|
}
|
|
}
|
|
|
|
CopyToIndex++;
|
|
}
|
|
|
|
// Previous Briefs
|
|
if (bClearPreviousBriefs)
|
|
{
|
|
ClearAllPreviousBriefsThatBelongToThisTextBlock(TextBlock);
|
|
} // end of if (bClearPreviousBriefs)
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearMessages
|
|
// PURPOSE: Clears all the messages (the brief ones anyway)
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearMessages()
|
|
{
|
|
for (s32 C = 0; C < MAX_NUM_BRIEF_MESSAGES; C++)
|
|
{
|
|
BriefMessages[C].Clear(true, false);
|
|
}
|
|
}
|
|
|
|
// Return false if copy fails to complete (because destination string isn't long enough)
|
|
bool CMessages::CopyGxtStringIntoStringAtIndex(char *pDestString, u32 &DestIndex, u32 MaxDestStringLength, const char *pSourceGxtString, u32 SourceStringLength)
|
|
{
|
|
if (pSourceGxtString == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (SourceStringLength == 0)
|
|
{
|
|
SourceStringLength = CTextConversion::GetByteCount(pSourceGxtString);
|
|
}
|
|
|
|
u32 CopyLoop = 0;
|
|
while (CopyLoop < SourceStringLength)
|
|
{
|
|
if (DestIndex < MaxDestStringLength)
|
|
{
|
|
pDestString[DestIndex++] = pSourceGxtString[CopyLoop++];
|
|
}
|
|
else
|
|
{
|
|
#if __ASSERT
|
|
Assertf(0, "CMessages::CopyGxtStringIntoStringAtIndex - destination string has length %d. It's not long enough. Source string begins %s", MaxDestStringLength, pSourceGxtString );
|
|
#endif // __ASSERT
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return false if copy fails to complete (because destination string isn't long enough, or source string is too long to store in temporary char array)
|
|
bool CMessages::CopyStringIntoStringAtIndex(char *pDestString, u32 &DestIndex, u32 MaxDestStringLength, const char *pSourceString, u32 SourceStringLength)
|
|
{
|
|
if (pSourceString == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (SourceStringLength == 0)
|
|
{
|
|
SourceStringLength = ustrlen(pSourceString);
|
|
}
|
|
|
|
return CopyGxtStringIntoStringAtIndex(pDestString, DestIndex, MaxDestStringLength, pSourceString, SourceStringLength);
|
|
}
|
|
|
|
void CMessages::CopyColouredGxtStringIntoStringAtIndex(char *pDestString, u32 &DestIndex, u32 MaxDestStringLength, eHUD_COLOURS ColourOfSourceString, const char *pSourceGxtString, u32 SourceStringLength)
|
|
{
|
|
bool bResult = false;
|
|
|
|
const u32 MaxLengthOfColourString = 16;
|
|
char ColourString[MaxLengthOfColourString];
|
|
|
|
if (HUD_COLOUR_INVALID != ColourOfSourceString)
|
|
{
|
|
formatf(ColourString, MaxLengthOfColourString, "~HC_%d~", ColourOfSourceString);
|
|
bResult = CopyStringIntoStringAtIndex(pDestString, DestIndex, MaxDestStringLength, ColourString);
|
|
Assertf(bResult, "CMessages::CopyColouredGxtStringIntoStringAtIndex - failed to insert colour for substring/number into main string");
|
|
}
|
|
|
|
bResult = CopyGxtStringIntoStringAtIndex(pDestString, DestIndex, MaxDestStringLength, pSourceGxtString, SourceStringLength);
|
|
Assertf(bResult, "CMessages::CopyColouredGxtStringIntoStringAtIndex - failed to insert substring/number into main string");
|
|
|
|
// Maybe it's more flexible to leave it up to the line of text to reset the colour in case it should be reset to something other than COLOUR_STANDARD
|
|
// if (HUD_COLOUR_INVALID != ColourOfSourceString)
|
|
// { // Now write ~s~ after the substring/number to reset the colour for the rest of the line of text
|
|
// formatf(ColourString, MaxLengthOfColourString, "~%c~", FONT_CHAR_TOKEN_COLOUR_STANDARD);
|
|
// bResult = CopyStringIntoStringAtIndex(pDestString, DestIndex, MaxDestStringLength, ColourString);
|
|
// Assertf(bResult, "CMessages::CopyColouredGxtStringIntoStringAtIndex - failed to insert ~s~ for substring/number into main string");
|
|
// }
|
|
}
|
|
|
|
|
|
eTokenFormat CMessages::CheckForInsertionToken(const char *pOriginalString, const u32 CharacterIndex, u32 &IndexInteger)
|
|
{
|
|
eTokenFormat Format = TOKEN_FORMAT_NONE;
|
|
|
|
if (pOriginalString[CharacterIndex] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{
|
|
char CharactersToCheck[4];
|
|
bool bReachedEndOfOriginalString = false;
|
|
for (u32 loop = 0; loop < 4; loop++)
|
|
{
|
|
if (bReachedEndOfOriginalString || (pOriginalString[CharacterIndex + 1 + loop] == rage::TEXT_CHAR_TERMINATOR) )
|
|
{
|
|
bReachedEndOfOriginalString = true;
|
|
CharactersToCheck[loop] = rage::TEXT_CHAR_TERMINATOR;
|
|
}
|
|
else
|
|
{
|
|
CharactersToCheck[loop] = pOriginalString[CharacterIndex + 1 + loop];
|
|
}
|
|
}
|
|
|
|
if (CharactersToCheck[1] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{ // Check if it's ~a~ or ~1~. Could still be something else like ~n~
|
|
if (CharactersToCheck[0] == FONT_CHAR_TOKEN_NUMBER)
|
|
{
|
|
Format = TOKEN_FORMAT_NUMBER_ORIGINAL;
|
|
}
|
|
else if (CharactersToCheck[0] == FONT_CHAR_TOKEN_SUBSTRING)
|
|
{
|
|
Format = TOKEN_FORMAT_SUBSTRING_ORIGINAL;
|
|
}
|
|
}
|
|
else if (CharactersToCheck[1] == FONT_CHAR_TOKEN_UNDERLINE)
|
|
{ // Check if it's ~a_.~ or ~1_.~ Could still be something else
|
|
if (CharactersToCheck[3] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{
|
|
if (CharactersToCheck[0] == FONT_CHAR_TOKEN_NUMBER)
|
|
{
|
|
Format = TOKEN_FORMAT_NUMBER_INDEXED;
|
|
}
|
|
else if (CharactersToCheck[0] == FONT_CHAR_TOKEN_SUBSTRING)
|
|
{
|
|
Format = TOKEN_FORMAT_SUBSTRING_INDEXED;
|
|
}
|
|
|
|
if (Format != TOKEN_FORMAT_NONE)
|
|
{
|
|
if (Verifyf( (CharactersToCheck[2] >= '0') && (CharactersToCheck[2] <= '9'),
|
|
"CMessages::CheckForInsertionToken - expected an integer after _ inside ~a_~ or ~1_~ but character is %c", CharactersToCheck[2]))
|
|
{
|
|
IndexInteger = CharactersToCheck[2] - '0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Format;
|
|
}
|
|
|
|
u32 CMessages::GetArrayIndexAccordingToFormat(const eTokenFormat FormatUsedForThisPosition, const u32 IndexInteger, eTokenFormat &MethodToUseForInserting, const u32 NumberOfItemsAlreadyInserted, const char * ASSERT_ONLY(pOriginalStringForAssertMessage))
|
|
{
|
|
if (MethodToUseForInserting == TOKEN_FORMAT_NONE)
|
|
{ // If this is the first element of this type (number or substring) to be inserted into the main string
|
|
// then use the method (original or indexed) that has just been read from the first token
|
|
MethodToUseForInserting = FormatUsedForThisPosition;
|
|
}
|
|
else
|
|
{ // Otherwise check that the current token is using the same method (original or indexed)
|
|
// as the first token for this type (number or substring)
|
|
if (FormatUsedForThisPosition != MethodToUseForInserting)
|
|
{
|
|
#if __ASSERT
|
|
Displayf("CMessages::GetArrayIndexAccordingToFormat - Main string is %s", pOriginalStringForAssertMessage );
|
|
Assertf(0, "CMessages::GetArrayIndexAccordingToFormat - Main string uses a combination of ~a~ and ~a_.~ or a combination of ~1~ and ~1_.~. See TTY for details");
|
|
#endif // __ASSERT
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
switch (MethodToUseForInserting)
|
|
{
|
|
case TOKEN_FORMAT_NONE :
|
|
Assertf(0, "CMessages::GetArrayIndexAccordingToFormat - didn't expect MethodToUseForInserting to be TOKEN_FORMAT_NONE at this stage");
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_ORIGINAL :
|
|
case TOKEN_FORMAT_SUBSTRING_ORIGINAL :
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_INDEXED :
|
|
case TOKEN_FORMAT_SUBSTRING_INDEXED :
|
|
return IndexInteger; // Return the index that has been read from the token
|
|
break;
|
|
}
|
|
|
|
return NumberOfItemsAlreadyInserted; // For original method, the index will count up from 0
|
|
}
|
|
|
|
|
|
void CMessages::InsertNumberIntoString(char *pNewString, u32 &NewCharLoop, const u32 MaxSizeOfNewString, const CNumberWithinMessage *pArrayOfNumbers, const u32 SizeOfNumberArray, const u32 ArrayElementToUse, const char * ASSERT_ONLY(pOriginalStringForAssertMessage))
|
|
{
|
|
eHUD_COLOURS ColourForThisTextComponent = HUD_COLOUR_INVALID;
|
|
|
|
const s32 MaxLengthOfNumberString = 10;
|
|
char NumberAsAsciiString[MaxLengthOfNumberString];
|
|
|
|
u32 NumberStringLength = 0;
|
|
if (pArrayOfNumbers && (ArrayElementToUse < SizeOfNumberArray))
|
|
{
|
|
NumberStringLength = pArrayOfNumbers[ArrayElementToUse].FillString(NumberAsAsciiString, MaxLengthOfNumberString);
|
|
ColourForThisTextComponent = pArrayOfNumbers[ArrayElementToUse].GetColour();
|
|
}
|
|
else
|
|
{
|
|
safecpy(NumberAsAsciiString, "", MaxLengthOfNumberString);
|
|
}
|
|
|
|
Assertf(NumberStringLength != 0, "The number of numbers doesn't match the number of ~1~ in \"%s\"",
|
|
pOriginalStringForAssertMessage );
|
|
|
|
CopyColouredGxtStringIntoStringAtIndex(pNewString, NewCharLoop, MaxSizeOfNewString, ColourForThisTextComponent, NumberAsAsciiString, NumberStringLength);
|
|
}
|
|
|
|
|
|
void CMessages::InsertSubStringIntoString(char *pNewString, u32 &NewCharLoop, const u32 MaxSizeOfNewString, const CSubStringWithinMessage *pArrayOfSubStrings, const u32 SizeOfSubStringArray, const u32 ArrayElementToUse, const char * ASSERT_ONLY(pOriginalStringForAssertMessage))
|
|
{
|
|
eHUD_COLOURS ColourForThisTextComponent = HUD_COLOUR_INVALID;
|
|
|
|
const char *pSubStringToInsert = NULL;
|
|
if (pArrayOfSubStrings && (ArrayElementToUse < SizeOfSubStringArray))
|
|
{
|
|
pSubStringToInsert = pArrayOfSubStrings[ArrayElementToUse].GetString();
|
|
ColourForThisTextComponent = pArrayOfSubStrings[ArrayElementToUse].GetColour();
|
|
}
|
|
|
|
#if __ASSERT
|
|
if (pSubStringToInsert == NULL)
|
|
{
|
|
Displayf("Main string is %s", pOriginalStringForAssertMessage );
|
|
for (s32 debug_loop = 0; debug_loop < SizeOfSubStringArray; debug_loop++)
|
|
{
|
|
const char *pGxtSubString = pArrayOfSubStrings[debug_loop].GetString();
|
|
if (pGxtSubString)
|
|
{
|
|
Displayf("SubString %d is %s", debug_loop, pGxtSubString );
|
|
}
|
|
else
|
|
{
|
|
Displayf("SubString %d is empty", debug_loop);
|
|
}
|
|
}
|
|
Assertf(0, "CMessages::InsertSubStringIntoString - The number of substrings doesn't match the number of ~a~ in the main string. See TTY for details");
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
CopyColouredGxtStringIntoStringAtIndex(pNewString, NewCharLoop, MaxSizeOfNewString, ColourForThisTextComponent, pSubStringToInsert);
|
|
}
|
|
|
|
|
|
void CMessages::InsertNumbersAndSubStringsIntoString(const char *pOriginalString,
|
|
const CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
const CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
char *pNewString, u32 MaxSizeOfNewString)
|
|
{
|
|
if (pNewString == NULL)
|
|
{
|
|
Assertf(0, "CMessages::InsertNumbersAndSubStringsIntoString - Destination string is NULL");
|
|
return;
|
|
}
|
|
|
|
if (pOriginalString == NULL)
|
|
{
|
|
pNewString[0] = rage::TEXT_CHAR_TERMINATOR;
|
|
return;
|
|
}
|
|
|
|
if (pArrayOfNumbers == NULL)
|
|
{
|
|
SizeOfNumberArray = 0;
|
|
}
|
|
|
|
if (pArrayOfSubStrings == NULL)
|
|
{
|
|
SizeOfSubStringArray = 0;
|
|
}
|
|
|
|
u32 NumberOfNumbersInserted = 0;
|
|
u32 NumberOfSubStringsInserted = 0;
|
|
|
|
eTokenFormat MethodToUseForInsertingNumbers = TOKEN_FORMAT_NONE;
|
|
eTokenFormat MethodToUseForInsertingSubStrings = TOKEN_FORMAT_NONE;
|
|
|
|
u32 CharLoop = 0;
|
|
u32 NewCharLoop = 0;
|
|
while ( (pOriginalString[CharLoop] != rage::TEXT_CHAR_TERMINATOR) && (NewCharLoop < MaxSizeOfNewString) )
|
|
{
|
|
bool bNumberOrStringInserted = false;
|
|
u32 IndexInteger = 0;
|
|
eTokenFormat FormatUsedForThisPosition = CheckForInsertionToken(pOriginalString, CharLoop, IndexInteger);
|
|
|
|
if (FormatUsedForThisPosition != TOKEN_FORMAT_NONE)
|
|
{
|
|
switch (FormatUsedForThisPosition)
|
|
{
|
|
case TOKEN_FORMAT_NONE :
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_ORIGINAL :
|
|
case TOKEN_FORMAT_NUMBER_INDEXED :
|
|
IndexInteger = GetArrayIndexAccordingToFormat(FormatUsedForThisPosition, IndexInteger, MethodToUseForInsertingNumbers, NumberOfNumbersInserted, pOriginalString);
|
|
InsertNumberIntoString(pNewString, NewCharLoop, MaxSizeOfNewString, pArrayOfNumbers, SizeOfNumberArray, IndexInteger, pOriginalString);
|
|
NumberOfNumbersInserted++;
|
|
break;
|
|
case TOKEN_FORMAT_SUBSTRING_ORIGINAL :
|
|
case TOKEN_FORMAT_SUBSTRING_INDEXED :
|
|
IndexInteger = GetArrayIndexAccordingToFormat(FormatUsedForThisPosition, IndexInteger, MethodToUseForInsertingSubStrings, NumberOfSubStringsInserted, pOriginalString);
|
|
InsertSubStringIntoString(pNewString, NewCharLoop, MaxSizeOfNewString, pArrayOfSubStrings, SizeOfSubStringArray, IndexInteger, pOriginalString);
|
|
NumberOfSubStringsInserted++;
|
|
break;
|
|
}
|
|
|
|
switch (FormatUsedForThisPosition)
|
|
{
|
|
case TOKEN_FORMAT_NONE :
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_ORIGINAL :
|
|
case TOKEN_FORMAT_SUBSTRING_ORIGINAL :
|
|
bNumberOrStringInserted = true;
|
|
CharLoop += 3;
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_INDEXED :
|
|
case TOKEN_FORMAT_SUBSTRING_INDEXED :
|
|
bNumberOrStringInserted = true;
|
|
CharLoop += 5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bNumberOrStringInserted)
|
|
{ // Just copy the current character from the OriginalString to the NewString
|
|
if (NewCharLoop < MaxSizeOfNewString)
|
|
{
|
|
pNewString[NewCharLoop++] = pOriginalString[CharLoop++];
|
|
}
|
|
#if __ASSERT
|
|
else
|
|
{
|
|
Assertf(0, "CMessages::InsertNumbersAndSubStringsIntoString - NewString has length %d. It's not long enough. Original string is %s", MaxSizeOfNewString, pOriginalString );
|
|
}
|
|
#endif // __ASSERT
|
|
}
|
|
}
|
|
|
|
if (NewCharLoop >= MaxSizeOfNewString)
|
|
{
|
|
#if __ASSERT
|
|
Assertf(0, "CMessages::InsertNumbersAndSubStringsIntoString - string is too long. The maximum length is %d. Original string is %s", MaxSizeOfNewString, pOriginalString );
|
|
#endif // __ASSERT
|
|
|
|
NewCharLoop = MaxSizeOfNewString-1;
|
|
|
|
u32 NumberOfTildes = 0;
|
|
for (u32 char_loop = 0; char_loop < NewCharLoop; char_loop++)
|
|
{
|
|
if (pNewString[char_loop] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{
|
|
NumberOfTildes++;
|
|
}
|
|
}
|
|
|
|
if ((NumberOfTildes % 2) == 1)
|
|
{ // There are an odd number of tildes so add one as the second last character.
|
|
// This is so that CFont::GetTokenType doesn't have to scan forward until it finds a closing tilde.
|
|
// It seems to already handle an unrecognised token.
|
|
pNewString[NewCharLoop-1] = FONT_CHAR_TOKEN_DELIMITER;
|
|
}
|
|
}
|
|
|
|
pNewString[NewCharLoop] = rage::TEXT_CHAR_TERMINATOR;
|
|
|
|
// This is what differentiates LITERAL_STRING_TYPE_FOR_IMMEDIATE_USE from LITERAL_STRING_TYPE_SINGLE_FRAME
|
|
// After creating your immediate-use literals, you should call InsertNumbersAndSubStringsIntoString() to construct your larger string.
|
|
// As soon as the larger string has been constructed, we assume that it is safe to clear all the immediate-use literals on this thread (update or render).
|
|
CScriptHud::ScriptLiteralStrings.ClearArrayOfImmediateUseLiteralStrings(false);
|
|
}
|
|
|
|
|
|
void CMessages::GetExpectedNumberOfTextComponents(char *pGxtString, s32 &ReturnNumberOfNumbers, s32 &ReturnNumberOfSubstrings)
|
|
{
|
|
ReturnNumberOfNumbers = 0;
|
|
ReturnNumberOfSubstrings = 0;
|
|
|
|
if (pGxtString == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
u32 CharLoop = 0;
|
|
while ( (pGxtString[CharLoop] != rage::TEXT_CHAR_TERMINATOR) )
|
|
{
|
|
u32 IndexInteger = 0;
|
|
eTokenFormat FormatUsedForThisPosition = CheckForInsertionToken(pGxtString, CharLoop, IndexInteger);
|
|
|
|
switch (FormatUsedForThisPosition)
|
|
{
|
|
case TOKEN_FORMAT_NONE :
|
|
CharLoop++;
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_ORIGINAL :
|
|
ReturnNumberOfNumbers++;
|
|
CharLoop += 3;
|
|
break;
|
|
case TOKEN_FORMAT_SUBSTRING_ORIGINAL :
|
|
ReturnNumberOfSubstrings++;
|
|
CharLoop += 3;
|
|
break;
|
|
case TOKEN_FORMAT_NUMBER_INDEXED :
|
|
ReturnNumberOfNumbers++;
|
|
CharLoop += 5;
|
|
break;
|
|
case TOKEN_FORMAT_SUBSTRING_INDEXED :
|
|
ReturnNumberOfSubstrings++;
|
|
CharLoop += 5;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __WIN32PC
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::InsertPlayerControlKeysInString
|
|
// PURPOSE: Insert player control key descriptions into text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::InsertPlayerControlKeysInString(char* UNUSED_PARAM(pBigString))
|
|
{
|
|
|
|
}
|
|
#endif // #if __WIN32PC
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::Update
|
|
// PURPOSE: updates any messages we may have
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::Update(void)
|
|
{
|
|
s32 iCopyIndex;
|
|
|
|
if (!BriefMessages[0].IsEmpty())
|
|
{ // May have run out
|
|
if (BriefMessages[0].HasDisplayTimeExpired())
|
|
{
|
|
BriefMessages[0].Clear(true, false);
|
|
|
|
// Copy the new ones in
|
|
iCopyIndex = 0;
|
|
while (iCopyIndex < (MAX_NUM_BRIEF_MESSAGES-1) && !BriefMessages[iCopyIndex+1].IsEmpty())
|
|
{
|
|
BriefMessages[iCopyIndex] = BriefMessages[iCopyIndex+1];
|
|
iCopyIndex++;
|
|
}
|
|
BriefMessages[iCopyIndex].Clear(false, false);
|
|
|
|
// Set the start time of new message. (If there is one)
|
|
BriefMessages[0].SetWhenStarted(fwTimer::GetTimeInMilliseconds());
|
|
|
|
if (!BriefMessages[0].IsEmpty())
|
|
{ // Add the message to the previous brief array when it is first displayed
|
|
BriefMessages[0].SetNewlyAddedToTheHeadOfTheQueue(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
BriefMessages[0].AddToPreviousBriefsIfRequired();
|
|
|
|
if (CPauseMenu::IsActive() && CWarningScreen::IsActive())
|
|
return;
|
|
|
|
#if __BANK
|
|
if (!CHudTools::bDisplayHud)
|
|
return;
|
|
#endif
|
|
|
|
|
|
//
|
|
// update the subtitles:
|
|
//
|
|
|
|
CRGBA colour;
|
|
|
|
char StringToDisplay[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
|
|
// Update subtitles depending on when the script wants subtitles displayed
|
|
BriefMessages[0].FillInString(StringToDisplay, NELEM(StringToDisplay));
|
|
|
|
bool bDisplaySubtitles = true;
|
|
|
|
// if hud is hidden then dont display subtitles when pausemenu is active
|
|
if (CNewHud::IsFullHudHidden())
|
|
{
|
|
if ( CPauseMenu::IsActive()
|
|
#if GTA_REPLAY
|
|
|| CVideoEditorInterface::IsActive()
|
|
#endif
|
|
)
|
|
{
|
|
bDisplaySubtitles = false;
|
|
}
|
|
}
|
|
|
|
if ( (StringToDisplay[0] != 0 && bDisplaySubtitles)
|
|
&& (CPauseMenu::GetMenuPreference(PREF_SUBTITLES) || BriefMessages[0].GetDisplayEvenIfSubtitlesAreSwitchedOff() ) )
|
|
{
|
|
// set value:
|
|
CSubtitleText::SetMessageText(StringToDisplay);
|
|
}
|
|
else
|
|
{
|
|
CSubtitleText::ClearMessage();
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearThisPrint
|
|
// PURPOSE: clear this message if its the one passed in
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearThisPrint(CSubtitleMessage &Message, bool bCompareMainStringsOnly)
|
|
{
|
|
bool bAStringHasBeenCleared = true;
|
|
|
|
char FirstString[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
char SecondString[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
|
|
if (!bCompareMainStringsOnly)
|
|
{
|
|
Message.FillInString(FirstString, NELEM(FirstString));
|
|
}
|
|
|
|
while (bAStringHasBeenCleared)
|
|
{
|
|
bAStringHasBeenCleared = false;
|
|
u32 Index = 0;
|
|
while (!bAStringHasBeenCleared && (Index < MAX_NUM_BRIEF_MESSAGES) && !BriefMessages[Index].IsEmpty())
|
|
{
|
|
bool bStringsAreEqual = false;
|
|
|
|
if (bCompareMainStringsOnly)
|
|
{
|
|
if (BriefMessages[Index].DoesMainTextMatch(Message))
|
|
{
|
|
bStringsAreEqual = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BriefMessages[Index].FillInString(SecondString, NELEM(SecondString));
|
|
|
|
if ( strcmp(FirstString, SecondString) == 0 )
|
|
{
|
|
bStringsAreEqual = true;
|
|
}
|
|
}
|
|
|
|
if (bStringsAreEqual)
|
|
{
|
|
BriefMessages[Index].Clear(true, false);
|
|
|
|
// Copy the new ones in
|
|
u32 CopyIndex = Index;
|
|
while (CopyIndex < (MAX_NUM_BRIEF_MESSAGES-1) && !BriefMessages[CopyIndex+1].IsEmpty())
|
|
{
|
|
BriefMessages[CopyIndex] = BriefMessages[CopyIndex+1];
|
|
CopyIndex++;
|
|
}
|
|
BriefMessages[CopyIndex].Clear(false, false);
|
|
|
|
if ( (Index == 0) && (!BriefMessages[0].IsEmpty()) )
|
|
{ // If there's a new message to display
|
|
BriefMessages[0].SetWhenStarted(fwTimer::GetTimeInMilliseconds());
|
|
BriefMessages[0].SetNewlyAddedToTheHeadOfTheQueue(true);
|
|
}
|
|
|
|
bAStringHasBeenCleared = true;
|
|
}
|
|
else
|
|
{
|
|
Index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::IsThisMessageEmpty
|
|
// PURPOSE: returns whether this string is "empty" (pText is NULL, StringLength == 0, or pText contains only "~z~")
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CMessages::IsThisMessageEmpty(char const* pText)
|
|
{
|
|
bool bMessageEmpty = (pText == NULL);
|
|
if(pText)
|
|
{
|
|
s32 StringLength = CTextConversion::GetByteCount(pText);
|
|
if(StringLength == 0 ||
|
|
(StringLength == 3 && pText[0] == FONT_CHAR_TOKEN_DELIMITER && pText[1] == FONT_CHAR_TOKEN_DIALOGUE && pText[2] == FONT_CHAR_TOKEN_DELIMITER))
|
|
{
|
|
bMessageEmpty = true;
|
|
}
|
|
}
|
|
|
|
return bMessageEmpty;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::DoesTextBeginWithThisControlCharacter
|
|
// PURPOSE: returns whether this string of text begins with the token passed in
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CMessages::DoesTextBeginWithThisControlCharacter(char const *pText, s32 TokenToSearchFor, s32 TokenToIgnore)
|
|
{
|
|
enum eSearchStringState
|
|
{
|
|
EXPECTING_OPENING_DELIMITER,
|
|
EXPECTING_CONTROL_CHARACTER,
|
|
EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_SEARCH_FOR,
|
|
EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_IGNORE
|
|
};
|
|
|
|
if (pText == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
s32 StringLength = CTextConversion::GetByteCount(pText);
|
|
bool bContinueChecking = true;
|
|
s32 CharLoop = 0;
|
|
eSearchStringState State = EXPECTING_OPENING_DELIMITER;
|
|
while ( (CharLoop < StringLength) && bContinueChecking)
|
|
{
|
|
switch (State)
|
|
{
|
|
case EXPECTING_OPENING_DELIMITER :
|
|
if (pText[CharLoop] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{
|
|
State = EXPECTING_CONTROL_CHARACTER;
|
|
}
|
|
else
|
|
{ // String does not begin with a control character so return false
|
|
bContinueChecking = false;
|
|
}
|
|
break;
|
|
|
|
case EXPECTING_CONTROL_CHARACTER :
|
|
if (pText[CharLoop] == TokenToSearchFor)
|
|
{ // Found ~TokenToSearchFor so now expect to find a closing delimiter
|
|
State = EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_SEARCH_FOR;
|
|
}
|
|
else if (pText[CharLoop] == TokenToIgnore)
|
|
{ // Found ~TokenToIgnore. Simon said this could happen so ignore it and continue looking for ~TokenToSearchFor~
|
|
State = EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_IGNORE;
|
|
}
|
|
else
|
|
{ // String does not begin with ~TokenToSearchFor~ or ~TokenToIgnore~ so return false
|
|
bContinueChecking = false;
|
|
}
|
|
break;
|
|
|
|
case EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_SEARCH_FOR :
|
|
if (pText[CharLoop] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{ // Found a complete ~TokenToSearchFor~ so return true
|
|
return true;
|
|
}
|
|
else
|
|
{ // String does not begin with ~TokenToSearchFor~ so return false
|
|
bContinueChecking = false;
|
|
}
|
|
break;
|
|
|
|
case EXPECTING_CLOSING_DELIMITER_AFTER_FINDING_TOKEN_TO_IGNORE :
|
|
if (pText[CharLoop] == FONT_CHAR_TOKEN_DELIMITER)
|
|
{ // Found a complete ~TokenToIgnore~ so continue looking for ~TokenToSearchFor~
|
|
State = EXPECTING_OPENING_DELIMITER;
|
|
}
|
|
else
|
|
{ // String does not begin with ~TokenToIgnore~ so give up
|
|
bContinueChecking = false;
|
|
}
|
|
break;
|
|
|
|
}
|
|
CharLoop++;
|
|
}
|
|
|
|
// String does not begin with ~TokenToSearchFor~ so return false
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ShouldThisMessageBeExcludedFromThePreviousBriefs
|
|
// PURPOSE: returns whether the text should be added to the brief or not
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CMessages::ShouldThisMessageBeExcludedFromThePreviousBriefs(char const *pText)
|
|
{
|
|
return (IsThisMessageEmpty(pText) || DoesTextBeginWithThisControlCharacter(pText, FONT_CHAR_TOKEN_EXCLUDE_FROM_PREVIOUS_BRIEFS, FONT_CHAR_TOKEN_DIALOGUE));
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::IsThisTextSwitchedOffByFrontendSubtitleSetting
|
|
// PURPOSE: If text begins with ~z~ then it won't be displayed if Subtitles are
|
|
// switched off
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CMessages::IsThisTextSwitchedOffByFrontendSubtitleSetting(char const *pText)
|
|
{ // If text begins with ~z~ then it won't be displayed if Subtitles are switched off
|
|
return (DoesTextBeginWithThisControlCharacter(pText, FONT_CHAR_TOKEN_DIALOGUE, FONT_CHAR_TOKEN_EXCLUDE_FROM_PREVIOUS_BRIEFS));
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::AddMessage
|
|
// PURPOSE: Adds one message to the Q to be displayed
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::AddMessage(char const* pText, s32 TextBlock,
|
|
u32 Duration, bool bJumpQ, bool bAddToPrevBriefs, ePreviousBriefOverride PreviousBriefOverride,
|
|
CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
bool bUsesUnderscore, u32 iVoiceNameHash/*=0*/, u32 iMissionNameHash/*=0*/)
|
|
{
|
|
#if __ASSERT
|
|
char TestString[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
|
|
InsertNumbersAndSubStringsIntoString(pText,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
TestString, NELEM(TestString) );
|
|
|
|
#if __WIN32PC
|
|
InsertPlayerControlKeysInString(&TestString[0]);
|
|
#endif // __WIN32PC
|
|
u32 StringLength = CTextConversion::GetByteCount(TestString);
|
|
Assertf(StringLength < MAX_CHARS_IN_EXTENDED_MESSAGE, "CMessages::AddMessage - string is too long");
|
|
#endif // __ASSERT
|
|
|
|
if (ShouldThisMessageBeExcludedFromThePreviousBriefs(pText))
|
|
{
|
|
bAddToPrevBriefs = false;
|
|
PreviousBriefOverride = PREVIOUS_BRIEF_NO_OVERRIDE;
|
|
}
|
|
|
|
s32 Index = 0;
|
|
if (!bJumpQ)
|
|
{ // Find the next empty one.
|
|
while (Index < (MAX_NUM_BRIEF_MESSAGES) && !BriefMessages[Index].IsEmpty())
|
|
{
|
|
Index++;
|
|
}
|
|
}
|
|
if (Index < MAX_NUM_BRIEF_MESSAGES)
|
|
{ // Add this one
|
|
BriefMessages[Index].Clear(true, false);
|
|
|
|
BriefMessages[Index].Set(pText, TextBlock,
|
|
Duration, bAddToPrevBriefs, PreviousBriefOverride,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
bUsesUnderscore, iVoiceNameHash, iMissionNameHash);
|
|
|
|
if (Index == 0)
|
|
{ // Add the new message to the previous brief array when it is first displayed
|
|
BriefMessages[0].SetNewlyAddedToTheHeadOfTheQueue(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CPreviousHelpMessages
|
|
// ******************************************************************************************************
|
|
/*
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousHelpMessages::Init
|
|
// PURPOSE:
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousHelpMessages::Init(s32 MaxSizeOfArray)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages == NULL, "CPreviousMessages::Init - array of previous messages has already been allocated");
|
|
|
|
m_MaxNumberOfPreviousMessages = MaxSizeOfArray;
|
|
m_pArrayOfPreviousMessages = rage_new CHelpMessageText[m_MaxNumberOfPreviousMessages];
|
|
Assert(m_pArrayOfPreviousMessages);
|
|
|
|
ClearAllPreviousBriefs();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousHelpMessages::Shutdown
|
|
// PURPOSE:
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousHelpMessages::Shutdown()
|
|
{
|
|
if (m_pArrayOfPreviousMessages)
|
|
{
|
|
delete[] m_pArrayOfPreviousMessages;
|
|
m_pArrayOfPreviousMessages = NULL;
|
|
}
|
|
|
|
m_MaxNumberOfPreviousMessages = 0;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousHelpMessages::ClearAllPreviousBriefs
|
|
// PURPOSE:
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousHelpMessages::ClearAllPreviousBriefs()
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousHelpMessages::ClearAllPreviousBriefs - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return;
|
|
|
|
for (s32 loop = 0; loop < m_MaxNumberOfPreviousMessages; loop++)
|
|
{
|
|
m_pArrayOfPreviousMessages[loop].Clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousHelpMessages::AddBrief
|
|
// PURPOSE: adds message
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousHelpMessages::AddBrief(char *pMessage)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousHelpMessages::AddBrief - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return;
|
|
|
|
// find our current number of previous brief messages:
|
|
s32 NumberOfPreviousBriefs = m_MaxNumberOfPreviousMessages-1;
|
|
while ( (NumberOfPreviousBriefs > 0) && (m_pArrayOfPreviousMessages[NumberOfPreviousBriefs].cHelpMessage[0] != '\0') )
|
|
{
|
|
NumberOfPreviousBriefs--;
|
|
}
|
|
|
|
if (NumberOfPreviousBriefs > 0) // we have some previous messages, so lets readjust the list so we can add the new one at the top
|
|
{
|
|
s32 copy_loop = (NumberOfPreviousBriefs - 1);
|
|
|
|
if (NumberOfPreviousBriefs == m_MaxNumberOfPreviousMessages)
|
|
{ // array of previous briefs is full - need to clear any literal strings for the final one (about to drop off the end of the array)
|
|
m_pArrayOfPreviousMessages[copy_loop].Clear();
|
|
copy_loop -= 1;
|
|
}
|
|
|
|
for (; copy_loop >= 0; copy_loop--)
|
|
{
|
|
m_pArrayOfPreviousMessages[copy_loop + 1] = m_pArrayOfPreviousMessages[copy_loop];
|
|
}
|
|
}
|
|
|
|
CTextConversion::charStrcpy(m_pArrayOfPreviousMessages[0].cHelpMessage, pMessage, MAX_CHARS_IN_MESSAGE);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousHelpMessages::FillStringWithPreviousMessage
|
|
// PURPOSE: copies a previous brief message into a string
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CPreviousHelpMessages::FillStringWithPreviousMessage(s32 IndexOfString, char *pGxtStringToFill, u32 MaxLengthOfString)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::FillStringWithPreviousMessage - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return false;
|
|
|
|
Assertf(IndexOfString >= 0, "CPreviousMessages::FillStringWithPreviousMessage - array index is negative");
|
|
Assertf(IndexOfString < m_MaxNumberOfPreviousMessages, "CPreviousMessages::FillStringWithPreviousMessage - array index is too large");
|
|
|
|
if ( (IndexOfString < 0) || (IndexOfString >= m_MaxNumberOfPreviousMessages) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CTextConversion::charStrcpy(pGxtStringToFill, GetMessageText(IndexOfString), MaxLengthOfString);
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CPreviousMessages
|
|
// ******************************************************************************************************
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::Init
|
|
// PURPOSE: Inits previous brief
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousMessages::Init(s32 MaxSizeOfArray)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages == NULL, "CPreviousMessages::Init - array of previous messages has already been allocated");
|
|
|
|
m_MaxNumberOfPreviousMessages = MaxSizeOfArray;
|
|
m_pArrayOfPreviousMessages = rage_new CSubtitleMessage[m_MaxNumberOfPreviousMessages];
|
|
Assert(m_pArrayOfPreviousMessages);
|
|
|
|
for (s32 loop = 0; loop < m_MaxNumberOfPreviousMessages; loop++)
|
|
{
|
|
m_pArrayOfPreviousMessages[loop].Clear(false, false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::Shutdown
|
|
// PURPOSE: shutdown of the brief messages
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousMessages::Shutdown()
|
|
{
|
|
if (m_pArrayOfPreviousMessages)
|
|
{
|
|
delete[] m_pArrayOfPreviousMessages;
|
|
m_pArrayOfPreviousMessages = NULL;
|
|
}
|
|
|
|
m_MaxNumberOfPreviousMessages = 0;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::ClearAllPreviousBriefs
|
|
// PURPOSE: clear the contents of all previous briefs
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousMessages::ClearAllPreviousBriefs()
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::ClearAllPreviousBriefs - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return;
|
|
|
|
for (s32 loop = 0; loop < m_MaxNumberOfPreviousMessages; loop++)
|
|
{
|
|
m_pArrayOfPreviousMessages[loop].Clear(true, true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::ClearAllPreviousBriefsBelongingToThisTextBlock
|
|
// PURPOSE: clear the contents of all previous briefs if it matches the text block
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousMessages::ClearAllPreviousBriefsBelongingToThisTextBlock(s32 TextBlock)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::ClearAllPreviousBriefsBelongingToThisTextBlock - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return;
|
|
|
|
for (s32 loop = 0; loop < m_MaxNumberOfPreviousMessages; loop++)
|
|
{
|
|
if (m_pArrayOfPreviousMessages[loop].UsesTextFromThisBlock(TextBlock))
|
|
{
|
|
m_pArrayOfPreviousMessages[loop].Clear(true, true);
|
|
}
|
|
}
|
|
|
|
// Previous Briefs - shift up any remaining messages to fill the gaps
|
|
s32 CopyToIndex = 0;
|
|
s32 CopyFromIndex = 0;
|
|
while (CopyToIndex < m_MaxNumberOfPreviousMessages)
|
|
{
|
|
if (m_pArrayOfPreviousMessages[CopyToIndex].IsEmpty())
|
|
{
|
|
CopyFromIndex = (CopyToIndex + 1);
|
|
while ( (CopyFromIndex < m_MaxNumberOfPreviousMessages) && (m_pArrayOfPreviousMessages[CopyFromIndex].IsEmpty()) )
|
|
{
|
|
CopyFromIndex++;
|
|
}
|
|
|
|
if (CopyFromIndex < m_MaxNumberOfPreviousMessages)
|
|
{
|
|
m_pArrayOfPreviousMessages[CopyToIndex] = m_pArrayOfPreviousMessages[CopyFromIndex];
|
|
m_pArrayOfPreviousMessages[CopyFromIndex].Clear(false, false);
|
|
}
|
|
}
|
|
|
|
CopyToIndex++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::AddBrief
|
|
// PURPOSE: adds message to the brief
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CPreviousMessages::AddBrief(CSubtitleMessage &SubtitleMessage)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::AddBrief - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return;
|
|
|
|
// find our current number of previous brief messages:
|
|
s32 NumberOfPreviousBriefs = 0;
|
|
while ( (NumberOfPreviousBriefs < m_MaxNumberOfPreviousMessages) && !m_pArrayOfPreviousMessages[NumberOfPreviousBriefs].IsEmpty() )
|
|
{
|
|
if (m_pArrayOfPreviousMessages[NumberOfPreviousBriefs].Matches(SubtitleMessage))
|
|
{
|
|
return;
|
|
}
|
|
|
|
NumberOfPreviousBriefs++;
|
|
}
|
|
|
|
if (NumberOfPreviousBriefs > 0) // we have some previous messages, so lets readjust the list so we can add the new one at the top
|
|
{
|
|
s32 copy_loop = (NumberOfPreviousBriefs - 1);
|
|
|
|
if (NumberOfPreviousBriefs == m_MaxNumberOfPreviousMessages)
|
|
{ // array of previous briefs is full - need to clear any literal strings for the final one (about to drop off the end of the array)
|
|
m_pArrayOfPreviousMessages[copy_loop].Clear(true, true);
|
|
copy_loop -= 1;
|
|
}
|
|
|
|
for (; copy_loop >= 0; copy_loop--)
|
|
{
|
|
m_pArrayOfPreviousMessages[copy_loop + 1] = m_pArrayOfPreviousMessages[copy_loop];
|
|
}
|
|
}
|
|
|
|
m_pArrayOfPreviousMessages[0] = SubtitleMessage;
|
|
m_pArrayOfPreviousMessages[0].SetAllLiteralStringsOccurInPreviousBriefs();
|
|
}
|
|
|
|
|
|
bool CPreviousMessages::IsPreviousMessageEmpty(s32 IndexOfString)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::IsPreviousMessageEmpty - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return true;
|
|
|
|
Assertf(IndexOfString >= 0, "CPreviousMessages::IsPreviousMessageEmpty - array index is negative");
|
|
Assertf(IndexOfString < m_MaxNumberOfPreviousMessages, "CPreviousMessages::IsPreviousMessageEmpty - array index is too large");
|
|
|
|
if ( (IndexOfString < 0) || (IndexOfString >= m_MaxNumberOfPreviousMessages) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(!m_pArrayOfPreviousMessages[IndexOfString].IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::FillStringWithPreviousMessage
|
|
// PURPOSE: copies a previous brief message into a string
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CPreviousMessages::FillStringWithPreviousMessage(s32 IndexOfString, char *pGxtStringToFill, u32 MaxLengthOfString)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::FillStringWithPreviousMessage - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return false;
|
|
|
|
Assertf(IndexOfString >= 0, "CPreviousMessages::FillStringWithPreviousMessage - array index is negative");
|
|
Assertf(IndexOfString < m_MaxNumberOfPreviousMessages, "CPreviousMessages::FillStringWithPreviousMessage - array index is too large");
|
|
|
|
if ( (IndexOfString < 0) || (IndexOfString >= m_MaxNumberOfPreviousMessages) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!m_pArrayOfPreviousMessages[IndexOfString].IsEmpty())
|
|
{
|
|
m_pArrayOfPreviousMessages[IndexOfString].FillInString(pGxtStringToFill, MaxLengthOfString);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CPreviousMessages::GetPreviousMessageCharacterTXD
|
|
// PURPOSE: returns the previous message's character txd
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
const char* CPreviousMessages::GetPreviousMessageCharacterTXD(s32 IndexOfMsg)
|
|
{
|
|
Assertf(m_pArrayOfPreviousMessages, "CPreviousMessages::GetPreviousMessageCharacterTXD - array of previous messages has not been allocated");
|
|
if (!m_pArrayOfPreviousMessages)
|
|
return DEFAULT_CHARACTER_TXD;
|
|
|
|
Assertf(IndexOfMsg >= 0, "CPreviousMessages::GetPreviousMessageCharacterTXD - array index is negative");
|
|
Assertf(IndexOfMsg < m_MaxNumberOfPreviousMessages, "CPreviousMessages::GetPreviousMessageCharacterTXD - array index is too large");
|
|
|
|
if ( (IndexOfMsg < 0) || (IndexOfMsg >= m_MaxNumberOfPreviousMessages) )
|
|
{
|
|
return DEFAULT_CHARACTER_TXD;
|
|
}
|
|
|
|
if(!m_pArrayOfPreviousMessages[IndexOfMsg].IsEmpty())
|
|
{
|
|
u32 iVoiceNameHash = m_pArrayOfPreviousMessages[IndexOfMsg].GetVoiceNameHash();
|
|
u32 iMissionNameHash = m_pArrayOfPreviousMessages[IndexOfMsg].GetMissionNameHash();
|
|
return CMessages::ms_dialogueCharacters.GetCharacterTXD(iVoiceNameHash, iMissionNameHash);
|
|
}
|
|
|
|
return DEFAULT_CHARACTER_TXD;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : CMessages::AddToPreviousBriefArray
|
|
// PURPOSE : Adds a message as the first previous message. Any messages already stored
|
|
// are moved back one space to accommodate the new one. Only MAX_NUM_PREVIOUS_BRIEFS
|
|
// previous messages are remembered
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::AddToPreviousBriefArray(CSubtitleMessage &SubtitleMessage, ePreviousBriefOverride PreviousBriefOverride)
|
|
{
|
|
bool bAddToPreviousBriefs = false;
|
|
bool bAddToDialogueBriefs = false;
|
|
bool bAddToHelpTextBriefs = false;
|
|
SubtitleMessage.DetermineWhichPreviousBriefArrayToUse(bAddToPreviousBriefs, bAddToDialogueBriefs, bAddToHelpTextBriefs);
|
|
|
|
// If bAddToPreviousBriefs is false at this stage then I think it's safer to leave it as false
|
|
// even if PreviousBriefOverride has a "force" value
|
|
switch (PreviousBriefOverride)
|
|
{
|
|
case PREVIOUS_BRIEF_NO_OVERRIDE :
|
|
break;
|
|
case PREVIOUS_BRIEF_FORCE_DIALOGUE :
|
|
bAddToDialogueBriefs = true;
|
|
break;
|
|
case PREVIOUS_BRIEF_FORCE_GOD_TEXT :
|
|
bAddToDialogueBriefs = false;
|
|
break;
|
|
}
|
|
|
|
if (bAddToPreviousBriefs)
|
|
{
|
|
if (bAddToDialogueBriefs)
|
|
{
|
|
PreviousDialogueMessages.AddBrief(SubtitleMessage);
|
|
}
|
|
else if (bAddToHelpTextBriefs)
|
|
{
|
|
PreviousHelpTextMessages.AddBrief(SubtitleMessage);
|
|
}
|
|
else
|
|
{
|
|
PreviousMissionMessages.AddBrief(SubtitleMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::ClearAllPreviousBriefsThatBelongToThisTextBlock
|
|
// PURPOSE: clears any brief that belongs to the passed text block
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CMessages::ClearAllPreviousBriefsThatBelongToThisTextBlock(s32 TextBlock)
|
|
{
|
|
PreviousDialogueMessages.ClearAllPreviousBriefsBelongingToThisTextBlock(TextBlock);
|
|
PreviousMissionMessages.ClearAllPreviousBriefsBelongingToThisTextBlock(TextBlock);
|
|
PreviousHelpTextMessages.ClearAllPreviousBriefsBelongingToThisTextBlock(TextBlock);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::GetMaxNumberOfPreviousMessages
|
|
// PURPOSE: returns the number of previous messages in use
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
s32 CMessages::GetMaxNumberOfPreviousMessages(ePreviousMessageTypes iMessageType)
|
|
{
|
|
switch (iMessageType)
|
|
{
|
|
case PREV_MESSAGE_TYPE_DIALOG:
|
|
return PreviousDialogueMessages.GetMaxNumberOfMessages();
|
|
case PREV_MESSAGE_TYPE_MISSION:
|
|
return PreviousMissionMessages.GetMaxNumberOfMessages();
|
|
case PREV_MESSAGE_TYPE_HELP:
|
|
return PreviousHelpTextMessages.GetMaxNumberOfMessages();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CMessages::FillStringWithPreviousMessage
|
|
// PURPOSE: fills the passed string with a brief message
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CMessages::FillStringWithPreviousMessage(ePreviousMessageTypes iMessageType, s32 IndexOfString, char *pGxtStringToFill, u32 MaxLengthOfStringToFill)
|
|
{
|
|
switch (iMessageType)
|
|
{
|
|
case PREV_MESSAGE_TYPE_DIALOG:
|
|
return PreviousDialogueMessages.FillStringWithPreviousMessage(IndexOfString, pGxtStringToFill, MaxLengthOfStringToFill);
|
|
case PREV_MESSAGE_TYPE_MISSION:
|
|
return PreviousMissionMessages.FillStringWithPreviousMessage(IndexOfString, pGxtStringToFill, MaxLengthOfStringToFill);
|
|
case PREV_MESSAGE_TYPE_HELP:
|
|
return PreviousHelpTextMessages.FillStringWithPreviousMessage(IndexOfString, pGxtStringToFill, MaxLengthOfStringToFill);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMessages::IsPreviousMessageEmpty(ePreviousMessageTypes iMessageType, s32 IndexOfString)
|
|
{
|
|
switch (iMessageType)
|
|
{
|
|
case PREV_MESSAGE_TYPE_DIALOG:
|
|
return PreviousDialogueMessages.IsPreviousMessageEmpty(IndexOfString);
|
|
case PREV_MESSAGE_TYPE_MISSION:
|
|
return PreviousMissionMessages.IsPreviousMessageEmpty(IndexOfString);
|
|
case PREV_MESSAGE_TYPE_HELP:
|
|
return PreviousHelpTextMessages.IsPreviousMessageEmpty(IndexOfString);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const char* CMessages::GetPreviousMessageCharacterTXD(s32 IndexOfMsg)
|
|
{
|
|
return PreviousDialogueMessages.GetPreviousMessageCharacterTXD(IndexOfMsg);
|
|
}
|
|
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CHelpMessage
|
|
// ******************************************************************************************************
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::Init
|
|
// PURPOSE: initialises some variables
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::Init()
|
|
{
|
|
for (s32 i = 0; i < MAX_HELP_TEXT_SLOTS; i++)
|
|
{
|
|
m_HelpMessageText[i][0] = 0;
|
|
m_bHelpMessagesFading[i] = false;
|
|
m_iNewText[i] = HELP_TEXT_STATE_NONE;
|
|
m_vScreenPosition[i].x = -9999.0f;
|
|
m_vScreenPosition[i].y = -9999.0f;
|
|
m_iArrowDirection[i] = HELP_TEXT_ARROW_FORCE_RESET;
|
|
m_iFloatingTextOffset[i] = 0;
|
|
m_iStyle[i] = HELP_TEXT_STYLE_NORMAL;
|
|
m_iColour[i] = CRGBA(0,0,0,214);
|
|
m_bTriggerSound[i] = false;
|
|
m_OverrideDuration[i] = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetMessageText
|
|
// PURPOSE: sets the content of the help message text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetMessageText(s32 iId, const char *pHelpMessage,
|
|
const CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
const CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
bool bPlaySound, bool bDisplayForever, s32 OverrideDuration, bool WIN32PC_ONLY(bIME))
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CHelpMessage::SetMessageText only allowed to be called on UPDATE thread");
|
|
return;
|
|
}
|
|
|
|
#if RSG_PC
|
|
if(ioKeyboard::ImeIsInProgress() && !bIME)
|
|
return;
|
|
#endif // RSG_PC
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
if (!pHelpMessage)
|
|
{
|
|
if (DoesMessageTextExist(iId))
|
|
{
|
|
Clear(iId, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char NewString[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
|
|
CMessages::InsertNumbersAndSubStringsIntoString(pHelpMessage,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
NewString, NELEM(NewString));
|
|
|
|
#if __WIN32PC
|
|
CMessages::InsertPlayerControlKeysInString(&NewString[0]);
|
|
#endif // #if __WIN32PC
|
|
|
|
#if __ASSERT
|
|
s32 StringLength = CTextConversion::GetByteCount(NewString);
|
|
Assertf(StringLength < MAX_CHARS_IN_MESSAGE, "CHelpMessage::SetMessageText - string is too long");
|
|
#endif
|
|
|
|
|
|
if ( (CNewHud::GetHelpTextWaitingForActionScriptResponse(iId)) || ( strcmp(NewString, m_HelpMessageText[iId]) != 0 ) )
|
|
{
|
|
safecpy( m_HelpMessageText[iId], NewString);
|
|
|
|
m_iNewText[iId] = HELP_TEXT_STATE_NEW;
|
|
m_bHelpMessagesFading[iId] = false;
|
|
m_vScreenPosition[iId].x = -9999.0f;
|
|
m_vScreenPosition[iId].y = -9999.0f;
|
|
m_iArrowDirection[iId] = HELP_TEXT_ARROW_FORCE_RESET;
|
|
m_iFloatingTextOffset[iId] = 0;
|
|
m_iStyle[iId] = HELP_TEXT_STYLE_NORMAL;
|
|
m_iColour[iId] = CRGBA(0,0,0,214);
|
|
m_bTriggerSound[iId] = bPlaySound;
|
|
m_OverrideDuration[iId] = OverrideDuration;
|
|
|
|
// will ignore any CLEAR_HELP_TEXT callbacks that may come in from a previous CLEAR as we have set the help text again
|
|
CNewHud::SetHelpTextWaitingForActionScriptResponse(iId, false);
|
|
}
|
|
else
|
|
{
|
|
/*#if __ASSERT
|
|
//
|
|
// simple check to catch any instances where help text is set 2 frames in a row.
|
|
// this is a waste
|
|
//
|
|
static u32 iPreviousFrameHelpSet = 0;
|
|
|
|
if (iPreviousFrameHelpSet == GRCDEVICE.GetFrameCounter()-1)
|
|
{
|
|
uiAssertf(0, "Help text shouldn't be called every frame - use the FOREVER param and CLEAR instead");
|
|
}
|
|
|
|
iPreviousFrameHelpSet = GRCDEVICE.GetFrameCounter();
|
|
#endif // __ASSERT*/
|
|
|
|
m_iNewText[iId] = HELP_TEXT_STATE_SAME;
|
|
m_bTriggerSound[iId] = false;
|
|
}
|
|
|
|
if (bDisplayForever)
|
|
{
|
|
m_iNewText[iId] = HELP_TEXT_STATE_FOREVER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CHelpMessage::SetMessageTextAndAddToBrief(s32 iId, const char *pHelpMessageLabel,
|
|
CNumberWithinMessage *pArrayOfNumbers, u32 SizeOfNumberArray,
|
|
CSubStringWithinMessage *pArrayOfSubStrings, u32 SizeOfSubStringArray,
|
|
bool bPlaySound, bool bDisplayForever, s32 OverrideDuration)
|
|
{
|
|
|
|
char *pHelpMessage = TheText.Get(pHelpMessageLabel);
|
|
s32 MainTextBlock = TheText.GetBlockContainingLastReturnedString();
|
|
|
|
CHelpMessage::SetMessageText(iId, pHelpMessage,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
bPlaySound, bDisplayForever, OverrideDuration);
|
|
|
|
CSubtitleMessage PreviousBrief;
|
|
|
|
PreviousBrief.Set(pHelpMessage, MainTextBlock,
|
|
pArrayOfNumbers, SizeOfNumberArray,
|
|
pArrayOfSubStrings, SizeOfSubStringArray,
|
|
false, true);
|
|
|
|
CMessages::AddToPreviousBriefArray(PreviousBrief, CScriptHud::GetNextMessagePreviousBriefOverride());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetScreenPosition
|
|
// PURPOSE: sets the position of the help text on the screen
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetScreenPosition(s32 iId, Vector2 vScreenPos)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
m_vScreenPosition[iId] = vScreenPos;
|
|
|
|
if (m_vScreenPosition[iId].x > 1.0f-OFFSET_FROM_EDGE_OF_SCREEN)
|
|
m_vScreenPosition[iId].x = 1.0f-OFFSET_FROM_EDGE_OF_SCREEN;
|
|
|
|
if (m_vScreenPosition[iId].x < OFFSET_FROM_EDGE_OF_SCREEN)
|
|
m_vScreenPosition[iId].x = OFFSET_FROM_EDGE_OF_SCREEN;
|
|
|
|
if (m_vScreenPosition[iId].y > 1.0f-OFFSET_FROM_EDGE_OF_SCREEN)
|
|
m_vScreenPosition[iId].y = 1.0f-OFFSET_FROM_EDGE_OF_SCREEN;
|
|
|
|
if (m_vScreenPosition[iId].y < OFFSET_FROM_EDGE_OF_SCREEN)
|
|
m_vScreenPosition[iId].y = OFFSET_FROM_EDGE_OF_SCREEN;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetWorldPosition
|
|
// PURPOSE: sets the position of the help text in the world and onto the screen
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetWorldPosition(s32 iId, Vector3::Vector3Param vWorldPositon)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
Vector3 vWorldPositionV(vWorldPositon);
|
|
|
|
eTEXT_ARROW_ORIENTATION iArrowDir;
|
|
|
|
SetScreenPosition(iId, CHudTools::GetHudPosFromWorldPos(vWorldPositionV, &iArrowDir));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetWorldPositionFromEntity
|
|
// PURPOSE: sets the position of the help text in the world from an entity pos and
|
|
// to a screen pos
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetWorldPositionFromEntity(s32 iId, CPhysical* pEntity, Vector2 vOffset)
|
|
{
|
|
if (pEntity)
|
|
{
|
|
if (pEntity->GetIsTypePed())
|
|
{
|
|
SetWorldPosition(iId, VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()));
|
|
}
|
|
else if (pEntity->GetIsTypeVehicle())
|
|
{
|
|
CPed *pDriverPed = static_cast<CVehicle*>(pEntity)->GetDriver();
|
|
|
|
if (pDriverPed)
|
|
{
|
|
SetWorldPosition(iId, VEC3V_TO_VECTOR3(pDriverPed->GetTransform().GetPosition()));
|
|
}
|
|
else
|
|
{
|
|
SetWorldPosition(iId, VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()));
|
|
}
|
|
}
|
|
else if (pEntity->GetIsTypeObject())
|
|
{
|
|
SetWorldPosition(iId, VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()));
|
|
}
|
|
|
|
// apply the offset and pass it to be the screen pos
|
|
Vector2 vScreenPosWithOffset = (GetScreenPosition(iId) + vOffset);
|
|
SetScreenPosition(iId, vScreenPosWithOffset);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetStyle
|
|
// PURPOSE: sets the style of this help text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetStyle(s32 iId, s32 iNewStyle, s32 iNewArrowDirection, s32 iFloatingTextOffset, s32 iHudColour, s32 iAlpha)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
m_iStyle[iId] = (eTEXT_STYLE)iNewStyle;
|
|
m_iArrowDirection[iId] = (eTEXT_ARROW_ORIENTATION)iNewArrowDirection;
|
|
m_iFloatingTextOffset[iId] = iFloatingTextOffset;
|
|
|
|
if (iAlpha >= 0 && iAlpha <= 255)
|
|
{
|
|
m_iColour[iId] = CHudColour::GetRGB((eHUD_COLOURS)iHudColour, (u8)iAlpha);
|
|
}
|
|
else
|
|
{
|
|
m_iColour[iId] = CHudColour::GetRGBA((eHUD_COLOURS)iHudColour);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::Clear
|
|
// PURPOSE: flags to clear the help message
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::Clear(s32 iId, bool bClearNow)
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CHelpMessage::Clear only allowed to be called on UPDATE thread");
|
|
return;
|
|
}
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
if (m_HelpMessageText[iId][0] != 0)
|
|
{
|
|
m_bTriggerSound[iId] = false;
|
|
m_bHelpMessagesFading[iId] = !bClearNow;
|
|
CNewHud::ClearHelpText(iId, bClearNow);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::ClearAll
|
|
// PURPOSE: clears all help messages
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::ClearAll()
|
|
{
|
|
for (s32 i = 0; i < MAX_HELP_TEXT_SLOTS; i++)
|
|
{
|
|
Clear(i, true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::ClearMessage
|
|
// PURPOSE: clears the actual help message content
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::ClearMessage(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
m_HelpMessageText[iId][0] = 0;
|
|
m_bHelpMessagesFading[iId] = false;
|
|
m_iNewText[iId] = HELP_TEXT_STATE_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::GetMessageText
|
|
// PURPOSE: returns the content of the help message text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
char *CHelpMessage::GetMessageText(s32 iId)
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CHelpMessage::GetMessageText only allowed to be called on UPDATE thread");
|
|
return NULL;
|
|
}
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return &m_HelpMessageText[iId][0];
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::DoesMessageTextExist
|
|
// PURPOSE: returns whether a help message is currently set or not
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CHelpMessage::DoesMessageTextExist(s32 iId)
|
|
{
|
|
if (!CSystem::IsThisThreadId(SYS_THREAD_UPDATE))
|
|
{
|
|
AssertMsg(0, "CHelpMessage::GetMessageText only allowed to be called on UPDATE thread");
|
|
return false;
|
|
}
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_HelpMessageText[iId][0] != 0);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::IsMessageFadingOut
|
|
// PURPOSE: returns whether a help message is currently fading out
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CHelpMessage::IsMessageFadingOut(s32 iId)
|
|
{
|
|
if (!CSystem::IsThisThreadId(SYS_THREAD_UPDATE))
|
|
{
|
|
AssertMsg(0, "CHelpMessage::GetMessageText only allowed to be called on UPDATE thread");
|
|
return false;
|
|
}
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_HelpMessageText[iId][0] != 0) && m_bHelpMessagesFading[iId];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::IsHelpTextNew
|
|
// PURPOSE: returns whether the help text is new
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CHelpMessage::IsHelpTextNew(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_iNewText[iId] == HELP_TEXT_STATE_NEW);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::IsHelpTextSame
|
|
// PURPOSE: returns whether the help text is the same as the previous frame
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CHelpMessage::IsHelpTextSame(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_iNewText[iId] == HELP_TEXT_STATE_SAME);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::IsHelpTextForever
|
|
// PURPOSE: returns whether the help text is to display forever
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CHelpMessage::IsHelpTextForever(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_iNewText[iId] == HELP_TEXT_STATE_FOREVER);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CHelpMessage::WasHelpTextForever(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
return (m_iNewText[iId] == HELP_TEXT_STATE_FOREVER_IDLE);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::SetHelpMessageAsProcessed
|
|
// PURPOSE: flags the help message as processed - called after the scaleform movie
|
|
// has been updated with the content
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CHelpMessage::SetHelpMessageAsProcessed(s32 iId)
|
|
{
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
if(IsHelpTextForever(iId))
|
|
{
|
|
m_iNewText[iId] = HELP_TEXT_STATE_FOREVER_IDLE;
|
|
}
|
|
else
|
|
{
|
|
m_iNewText[iId] = HELP_TEXT_STATE_IDLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CHelpMessage::GetDirectionHelpText
|
|
// PURPOSE: returns the direction of the help text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
eTEXT_ARROW_ORIENTATION CHelpMessage::GetDirectionHelpText(s32 iId, Vector2 vNewPosition)
|
|
{
|
|
eTEXT_ARROW_ORIENTATION iDirectionOffScreen = HELP_TEXT_ARROW_NORMAL;
|
|
|
|
if (uiVerifyf(iId < MAX_HELP_TEXT_SLOTS, "CHelpMessage: Invalid Help Message Slot Passed: %d", iId))
|
|
{
|
|
// work out whether the new position of the help text is offscreen
|
|
if (vNewPosition.x >= 1.0f-OFFSET_FROM_EDGE_OF_SCREEN)
|
|
iDirectionOffScreen = HELP_TEXT_ARROW_EAST;
|
|
else if (vNewPosition.x <= OFFSET_FROM_EDGE_OF_SCREEN)
|
|
iDirectionOffScreen = HELP_TEXT_ARROW_WEST;
|
|
else if (vNewPosition.y >= 1.0f-OFFSET_FROM_EDGE_OF_SCREEN)
|
|
iDirectionOffScreen = HELP_TEXT_ARROW_SOUTH;
|
|
else if (vNewPosition.y <= OFFSET_FROM_EDGE_OF_SCREEN)
|
|
iDirectionOffScreen = HELP_TEXT_ARROW_NORTH;
|
|
}
|
|
|
|
return iDirectionOffScreen;
|
|
}
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CSubtitleText
|
|
// ******************************************************************************************************
|
|
|
|
char CSubtitleText::m_SubtitleMessageText[MAX_CHARS_IN_EXTENDED_MESSAGE];
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSubtitleText::Init
|
|
// PURPOSE: initialises some variables
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSubtitleText::Init()
|
|
{
|
|
m_SubtitleMessageText[0] = 0;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSubtitleText::SetMessageText
|
|
// PURPOSE: stores the new text and sets it up in Scaleform if its different
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSubtitleText::SetMessageText(char *pText)
|
|
{
|
|
if ( strcmp(m_SubtitleMessageText, pText) != 0 )
|
|
{
|
|
safecpy(m_SubtitleMessageText, pText);
|
|
|
|
CNewHud::SetSubtitleText(pText);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSubtitleText::ClearMessage
|
|
// PURPOSE: clears the text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSubtitleText::ClearMessage()
|
|
{
|
|
if (m_SubtitleMessageText[0] != 0)
|
|
{
|
|
m_SubtitleMessageText[0] = 0;
|
|
CNewHud::ClearSubtitleText();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSubtitleText::GetMessageText
|
|
// PURPOSE: returns the content of the subtitle text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
char *CSubtitleText::GetMessageText()
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CSubtitleText::GetMessageText only allowed to be called on UPDATE thread");
|
|
return NULL;
|
|
}
|
|
|
|
return &m_SubtitleMessageText[0];
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CSavingGameMessage
|
|
// ******************************************************************************************************
|
|
|
|
|
|
char CSavingGameMessage::m_cMessageText[MAX_CHARS_IN_MESSAGE];
|
|
CSavingGameMessage::eICON_STYLE CSavingGameMessage::m_iIconStyle;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::Init
|
|
// PURPOSE: initialises some variables
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSavingGameMessage::Init()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::SetMessageText
|
|
// PURPOSE: stores the new text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSavingGameMessage::SetMessageText(const char *pText, eICON_STYLE iconStyle)
|
|
{
|
|
safecpy(m_cMessageText, pText);
|
|
|
|
m_iIconStyle = iconStyle;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::GetMessageText
|
|
// PURPOSE: returns the content of the subtitle text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
char *CSavingGameMessage::GetMessageText()
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CSavingGameMessage::GetMessageText only allowed to be called on UPDATE thread");
|
|
return NULL;
|
|
}
|
|
|
|
return &m_cMessageText[0];
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::GetIconStyle
|
|
// PURPOSE: returns the icon style as an integer
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
s32 CSavingGameMessage::GetIconStyle()
|
|
{
|
|
if (CSystem::IsThisThreadId(SYS_THREAD_RENDER))
|
|
{
|
|
AssertMsg(0, "CSavingGameMessage::GetMessageText only allowed to be called on UPDATE thread");
|
|
return 0;
|
|
}
|
|
|
|
return s32( m_iIconStyle == SAVEGAME_ICON_STYLE_SAVING_NO_MESSAGE ? SAVEGAME_ICON_STYLE_SAVING : m_iIconStyle);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::IsSavingMessageActive
|
|
// PURPOSE: returns whether the saving message is active or not
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CSavingGameMessage::IsSavingMessageActive()
|
|
{
|
|
return (m_cMessageText[0] != 0) || m_iIconStyle == SAVEGAME_ICON_STYLE_SAVING_NO_MESSAGE;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CSavingGameMessage::Clear
|
|
// PURPOSE: clears the message
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CSavingGameMessage::Clear()
|
|
{
|
|
m_cMessageText[0] = 0;
|
|
m_iIconStyle = SAVEGAME_ICON_STYLE_NONE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ******************************************************************************************************
|
|
// ** CLoadingText
|
|
// ******************************************************************************************************
|
|
|
|
|
|
char CLoadingText::m_cText[MAX_CHARS_IN_MESSAGE];
|
|
bool CLoadingText::bActive = false;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::Init
|
|
// PURPOSE: initialises some variables
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CLoadingText::Init()
|
|
{
|
|
bActive = false;
|
|
|
|
Clear();
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::Shutdown
|
|
// PURPOSE: clears and shuts down the scaleform movie for the Loading Text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CLoadingText::Shutdown()
|
|
{
|
|
Clear();
|
|
SetActive(false);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::SetActive
|
|
// PURPOSE: sets loading text on/off
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CLoadingText::SetActive(bool bSet)
|
|
{
|
|
if (bSet)
|
|
{
|
|
if (!bActive)
|
|
{
|
|
bActive = true;
|
|
Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bActive)
|
|
{
|
|
bActive = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::SetText
|
|
// PURPOSE: stores the new text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CLoadingText::SetText(const char *pText)
|
|
{
|
|
safecpy(m_cText, pText);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::GetText
|
|
// PURPOSE: returns the content of the loading text
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
char *CLoadingText::GetText()
|
|
{
|
|
return &m_cText[0];
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::IsActive
|
|
// PURPOSE: returns whether the loading text is active or not
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
bool CLoadingText::IsActive()
|
|
{
|
|
return (m_cText[0] != 0);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// NAME: CLoadingText::Clear
|
|
// PURPOSE: clears the message
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
void CLoadingText::Clear()
|
|
{
|
|
m_cText[0] = 0;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// name: CLoadingText::UpdateAndRenderLoadingText
|
|
// description: update and render the loading text
|
|
void CLoadingText::UpdateAndRenderLoadingText(float fCurrentTimer)
|
|
{
|
|
if (!bActive)
|
|
return;
|
|
|
|
#define FADEOUT_WITHOUT_LOADING_TEXT (4.0f)
|
|
|
|
CMessages::ClearMessages();
|
|
|
|
if(CWarningScreen::IsActive())
|
|
{
|
|
if (TheText.IsBasicTextLoaded()) // only render text if the text file is loaded
|
|
{
|
|
if(CControlMgr::GetMainFrontendControl(false).GetFrontendAccept().IsPressed())
|
|
{
|
|
CWarningScreen::Remove();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (fCurrentTimer > FADEOUT_WITHOUT_LOADING_TEXT) || CSavingMessage::ShouldDisplaySavingSpinner() )
|
|
{
|
|
if (CBusySpinner::CanRender() && CBusySpinner::HasBodyText()) // if we have text then we need to display the real busy spinner now
|
|
{
|
|
CBusySpinner::Render();
|
|
}
|
|
else
|
|
{
|
|
// if we have no text, lets just render same way as we have done (this limits the change of using the busy spinner here to just a few places)
|
|
Vector2 vSize(0.0073125f, 0.013f);
|
|
const Vector2 vPos = CHudTools::CalculateHudPosition(Vector2(0,0), vSize, 'R', 'B');
|
|
CPauseMenu::RenderAnimatedSpinner(vSize, vPos, true, false, (CSavingMessage::ShouldDisplaySavingSpinner() REPLAY_ONLY(|| CReplayMgr::IsSaving()))?true:false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// eof
|