Files
GTASource/game/frontend/Credits.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1169 lines
32 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : Credits.cpp
// PURPOSE : manages the processing of end-game-credits
// gets the data from a file in the data directory
// AUTHOR : Derek Payne
// STARTED : 14/06/2006
//
/////////////////////////////////////////////////////////////////////////////////
// Rage headers
#include "grcore/allocscope.h"
#include "parser/manager.h"
#include "fwsys/fileExts.h"
#include "fwscene/stores/psostore.h"
#if RSG_PC
#include "rline/rlpc.h"
#endif // RSG_PC
// game:
#include "camera/viewports/Viewport.h"
#include "camera/viewports/ViewportManager.h"
#include "core/game.h"
#include "frontend\Credits.h"
#include "frontend\Credits_parser.h"
#include "frontend/VideoEditor/ui/InstructionalButtons.h"
#include "frontend\NewHud.h"
#include "frontend\Hud_Colour.h"
#include "renderer/sprite2d.h"
#include "streaming/streaming.h"
#include "system\ControlMgr.h"
#include "Text\Text.h"
#include "fwsys/timer.h"
// bank:
#include "bank\bank.h"
#include "bank\bkmgr.h"
#define CREDITS_FASTER 1
#define CREDITS_SLOWER -1
#define SPACE_SCALER_SLOWEST (0.0f)
#define SPACE_SCALER (0.0000856f)
#define SPACE_SCALER_FASTEST (0.000642f)
#define SPACE_SCALER_INTERVAL (0.0000214f)
#define MARGIN (0.15f)
#define NUMBER_OF_LINES_DOWN_TO_PROCESS (60) // we need to cater for all the text on-screen and enough "down" when final text has scrolled off the toip of the screen
static sysCriticalSectionToken s_creditsCritSecToken;
static const strStreamingObjectName CREDITS_TXD_PATH("platform:/textures/credits", 0xdc6683fa);
bool CCredits::bCreditsRunning;
bool CCredits::bShutdownPending = false;
u32 CCredits::CreditsStartTime;
float CCredits::PixelsScrolled;
bool CCredits::sm_bReachedEnd = false;
bool CCredits::sm_bUsingLimitedFont = false;
CCreditArray CCredits::m_CreditArray;
bool CCredits::sm_launchedFromPauseMenu = false;
float CCredits::sm_currentSpeed = SPACE_SCALER;
CSprite2d CCredits::sm_Sprite;
strLocalIndex CCredits::sm_iTxdId = strLocalIndex(-1);
grcBlendStateHandle CCredits::ms_StandardSpriteBlendStateHandle = grcStateBlock::BS_Invalid;
bool CCredits::bScriptWantsCreditsBeforeFade;
u32 CCredits::iStartLineRenderedThisFrame = 0;
float CCredits::fStartPixelsDown;
bool CCredits::sm_usingCloudCredits = false;
CCreditsDownloadHelper* CCredits::sm_creditsDownloadHelper;
#if __BANK
s32 CCredits::sm_iDebugPercentage = 0;
#endif
CTextLayout CCredits::CreditTextLayout;
CCreditsDownloadHelper::CCreditsDownloadHelper(CCreditArray& creditsArray)
: m_creditsArray(creditsArray)
{
Clear();
}
CCreditsDownloadHelper::~CCreditsDownloadHelper()
{
Clear();
}
CloudRequestID CCreditsDownloadHelper::StartDownload(const char* contentName, unsigned nCloudRequestFlags)
{
Clear();
SetupParserConversionDictionary();
m_fileRequestId = UserContentManager::GetInstance().RequestCdnContentData(contentName, nCloudRequestFlags);
if (m_fileRequestId == INVALID_CLOUD_REQUEST_ID)
{
Clear();
}
return m_fileRequestId;
}
void CCreditsDownloadHelper::Clear()
{
m_fileRequestId = INVALID_CLOUD_REQUEST_ID;
m_resultCode = RLSC_ERROR_NONE;
m_hasError = false;
m_downloadSuccess = false;
m_fileParseSuccess = false;
m_useLocal = false;
m_creditsArray.CreditItems.Reset();
m_parserConversionMap.Reset();
}
void CCreditsDownloadHelper::OnCloudEvent(const CloudEvent* pEvent)
{
switch (pEvent->GetType())
{
case CloudEvent::EVENT_REQUEST_FINISHED:
{
// grab event data
const CloudEvent::sRequestFinishedEvent* pEventData = pEvent->GetRequestFinishedData();
// check if we're interested
if (pEventData->nRequestID != m_fileRequestId)
{
return;
}
m_downloadSuccess = pEventData->bDidSucceed;
m_resultCode = pEventData->nResultCode;
m_fileRequestId = INVALID_CLOUD_REQUEST_ID;
if (m_downloadSuccess)
{
gnetDisplay("CCreditsDownloadHelper::OnCloudEvent - Download Success - %s", m_filename.c_str());
char bufferFName[RAGE_MAX_PATH];
fiDevice::MakeMemoryFileName(bufferFName, sizeof(bufferFName), pEventData->pData, pEventData->nDataSize, false, m_filename.c_str());
m_fileParseSuccess = ParseFile(bufferFName);
if (!m_fileParseSuccess)
{
gnetDisplay("CCreditsDownloadHelper::OnCloudEvent - Parsing failed, marking as 404 - %s", m_filename.c_str());
m_resultCode = NET_HTTPSTATUS_NOT_FOUND;
m_hasError = true;
m_useLocal = true;
}
else
{
CVideoEditorInstructionalButtons::Refresh();
}
}
else
{
// We give precedence to the cloud version but if the download fails we'll then fall back to the local version.
gnetDisplay("CCreditsDownloadHelper::OnCloudEvent - Download Failed - %s, resultCode=%d", m_filename.c_str(), m_resultCode);
m_hasError = true;
m_useLocal = true;
}
}
default:
break;
}
}
bool CCreditsDownloadHelper::ParseFile(const char* pFilename)
{
bool fileParseSuccess = false;
INIT_PARSER;
{
parTree* pDataXML = PARSER.LoadTree(pFilename, "");
if (pDataXML)
{
const parTreeNode* pRootNode = pDataXML->GetRoot();
if (pRootNode)
{
const parTreeNode* pArrayNode = pRootNode->GetChild();
parTreeNode::ChildNodeIterator pChildrenStart = pArrayNode->BeginChildren();
parTreeNode::ChildNodeIterator pChildrenEnd = pArrayNode->EndChildren();
int creditCount = 0;
for (parTreeNode::ChildNodeIterator ci = pChildrenStart; ci != pChildrenEnd; ++ci)
{
creditCount++;
}
m_creditsArray.CreditItems.Reset();
m_creditsArray.CreditItems.Reserve(creditCount);
for (parTreeNode::ChildNodeIterator ci = pChildrenStart; ci != pChildrenEnd; ++ci)
{
parTreeNode* curNode = *ci;
CCreditItem curCredit;
atHashString lineType = curNode->FindChildWithIndex(0)->GetData();
curCredit.LineType = GetLineType(lineType.GetHash());
const parTreeNode* c_node1 = curNode->FindChildWithIndex(1);
const parTreeNode* c_node2 = curNode->FindChildWithIndex(2);
const parTreeNode* c_node3 = curNode->FindChildWithIndex(3);
const char* line1 = c_node1 ? c_node1->GetData() : nullptr;
const char* line2 = c_node2 ? c_node2->GetData() : nullptr;
const char* line3 = c_node3 ? c_node3->GetData() : nullptr;
if (line1)
{
curCredit.cTextId1.Set(line1, (int)strlen(line1), 0);
}
if (line2)
{
curCredit.cTextId2.Set(line2, (int)strlen(line2), 0);
}
if (line3)
{
curCredit.cTextId3.Set(line3, (int)strlen(line3), 0);
}
const parTreeNode* c_literalString = curNode->FindChildWithIndex(4);
curCredit.bLiteral = c_literalString ? c_literalString->GetElement().FindAttributeBoolValue("value", false) : false;
m_creditsArray.CreditItems.PushAndGrow(curCredit);
}
}
fileParseSuccess = m_creditsArray.CreditItems.GetCount() > 0;
}
delete pDataXML;
}
SHUTDOWN_PARSER;
return fileParseSuccess;
}
eCreditType CCreditsDownloadHelper::GetLineType(u32 creditHash)
{
eCreditType returnType = JOB_BIG;
eCreditType* lookupVal = m_parserConversionMap.Access(creditHash);
if (lookupVal)
{
returnType = *lookupVal;
}
return returnType;
}
void CCreditsDownloadHelper::SetupParserConversionDictionary()
{
m_parserConversionMap.Insert(0x969D857B, JOB_BIG);
m_parserConversionMap.Insert(0x2C3E17BC, JOB_MED);
m_parserConversionMap.Insert(0x9699C7AD, JOB_SMALL);
m_parserConversionMap.Insert(0x7EB04BDA, NAME_BIG);
m_parserConversionMap.Insert(0xE6A96DB1, NAME_MED);
m_parserConversionMap.Insert(0x643F3E6D, NAME_SMALL);
m_parserConversionMap.Insert(0xD55FBCCA, SPACE_BIG);
m_parserConversionMap.Insert(0x52D63917, SPACE_MED);
m_parserConversionMap.Insert(0xAC816538, SPACE_SMALL);
m_parserConversionMap.Insert(0x3B486986, SPACE_END);
m_parserConversionMap.Insert(0xF35FD8B0, SPRITE_1);
m_parserConversionMap.Insert(0x7A76BDC, LEGALS);
m_parserConversionMap.Insert(0x9292BB6D, AUDIO_NAME);
m_parserConversionMap.Insert(0x669E929, AUDIO_LEGALS);
m_parserConversionMap.Insert(0xE9223F47, JOB_AND_NAME_MED);
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::Init()
// PURPOSE: main init function
/////////////////////////////////////////////////////////////////////////////////////
void CCredits::Init(unsigned initMode)
{
if (initMode == INIT_AFTER_MAP_LOADED)
{
Assertf(!bCreditsRunning, "Credits are already running!");
sm_iTxdId = -1;
sm_creditsDownloadHelper = rage_new CCreditsDownloadHelper(m_CreditArray);
sm_usingCloudCredits = false;
sm_currentSpeed = SPACE_SCALER;
Load();
// some languages require a little extra spacing 1535969
switch (CPauseMenu::GetMenuPreference(PREF_CURRENT_LANGUAGE))
{
case LANGUAGE_RUSSIAN:
case LANGUAGE_CHINESE_TRADITIONAL:
case LANGUAGE_CHINESE_SIMPLIFIED:
case LANGUAGE_KOREAN:
case LANGUAGE_JAPANESE:
sm_bUsingLimitedFont = true;
break;
default:
sm_bUsingLimitedFont = false;
}
sm_bReachedEnd = false;
bCreditsRunning = true;
bShutdownPending = false;
CreditsStartTime = fwTimer::GetSystemTimeInMilliseconds();
PixelsScrolled = 0.0f;
iStartLineRenderedThisFrame = 0;
fStartPixelsDown = 0.0f;
bScriptWantsCreditsBeforeFade = false; // reset the flag so that credits appear over the fades until Script says otherwise (when skipped)
CreditTextLayout.Default();
CreditTextLayout.SetDropShadow(true);
CreditTextLayout.SetAdjustForNonWidescreen(true);
if (sm_launchedFromPauseMenu)
{
CVideoEditorInstructionalButtons::Init();
CVideoEditorInstructionalButtons::Refresh();
}
if (sm_bUsingLimitedFont)
{
CreditTextLayout.SetLeading(2);
}
// stateblocks
grcBlendStateDesc bsd;
// The remaining BlendStates should all have these two flags set
bsd.BlendRTDesc[0].BlendEnable = true;
bsd.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_SRCALPHA;
bsd.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
bsd.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
bsd.BlendRTDesc[0].SrcBlendAlpha = grcRSV::BLEND_SRCALPHA;
bsd.BlendRTDesc[0].DestBlendAlpha = grcRSV::BLEND_INVSRCALPHA;
bsd.BlendRTDesc[0].BlendOpAlpha = grcRSV::BLENDOP_ADD;
bsd.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
ms_StandardSpriteBlendStateHandle = grcStateBlock::CreateBlendState(bsd);
#if __BANK
sm_iDebugPercentage = 0;
#endif // __BANK
}
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::Shutdown()
// PURPOSE: shuts down the credit items
/////////////////////////////////////////////////////////////////////////////////////
void CCredits::Shutdown(unsigned shutdownMode)
{
SYS_CS_SYNC(s_creditsCritSecToken);
if (shutdownMode == SHUTDOWN_WITH_MAP_LOADED || shutdownMode == SHUTDOWN_CORE || shutdownMode == SHUTDOWN_SESSION)
{
if (sm_creditsDownloadHelper != nullptr)
{
sm_usingCloudCredits = false;
sm_creditsDownloadHelper->Clear();
delete sm_creditsDownloadHelper;
sm_creditsDownloadHelper = nullptr;
}
bCreditsRunning = false;
m_CreditArray.CreditItems.Reset();
if (sm_Sprite.GetTexture())
{
sm_Sprite.Delete();
if (sm_iTxdId != -1)
{
g_TxdStore.RemoveRef(sm_iTxdId, REF_OTHER);
}
}
CVideoEditorInstructionalButtons::Shutdown();
}
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::Load()
// PURPOSE: loads from the credits.dat data file
/////////////////////////////////////////////////////////////////////////////////////
void CCredits::Load()
{
const char* c_creditsFileName = "Credits";
if (sm_launchedFromPauseMenu)
{
if (sm_creditsDownloadHelper->StartDownload(c_creditsFileName) != INVALID_CLOUD_REQUEST_ID)
{
sm_usingCloudCredits = true;
}
else
{
// LoadLocalCreditsFile();
// To avoid potential issues we're just going to bail for now.
// Please contact Alex Hadjadj for further questions.
bShutdownPending = true;
}
}
else
{
LoadLocalCreditsFile();
}
Assertf(sm_iTxdId == -1, "Credit sprite already active");
sm_iTxdId = g_TxdStore.FindSlot(CREDITS_TXD_PATH);
if (sm_iTxdId != -1)
{
g_TxdStore.StreamingRequest(sm_iTxdId, (STRFLAG_FORCE_LOAD | STRFLAG_PRIORITY_LOAD | STRFLAG_DONTDELETE));
CStreaming::SetDoNotDefrag(sm_iTxdId, g_TxdStore.GetStreamingModuleId());
}
}
void CCredits::LoadLocalCreditsFile()
{
sm_usingCloudCredits = false;
fwPsoStoreLoader::LoadDataIntoObject("platform:/data/credits", META_FILE_EXT, CCredits::m_CreditArray);
Assertf(m_CreditArray.CreditItems.GetCount() != 0, "No credit items to display!");
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::Update()
// PURPOSE: checks for quitting the credits
/////////////////////////////////////////////////////////////////////////////////////
void CCredits::Update()
{
if (!bCreditsRunning)
return;
SYS_CS_SYNC(s_creditsCritSecToken);
if (bShutdownPending)
{
Shutdown(SHUTDOWN_WITH_MAP_LOADED);
}
if (sm_launchedFromPauseMenu)
{
CVideoEditorInstructionalButtons::Update();
}
if (sm_usingCloudCredits && sm_creditsDownloadHelper->UseLocalFile())
{
// Use local files if we timeout, error, or fail to prase the cloud XML
// LoadLocalCreditsFile();
// To avoid potential issues we're just going to bail for now.
// Please contact Alex Hadjadj for further questions.
bShutdownPending = true;
}
//
// check and setup the credits texture
//
if ((sm_iTxdId != -1) && (g_TxdStore.HasObjectLoaded(sm_iTxdId)))
{
if (!sm_Sprite.GetTexture())
{
g_TxdStore.ClearRequiredFlag(sm_iTxdId.Get(), STRFLAG_DONTDELETE);
g_TxdStore.AddRef(sm_iTxdId, REF_OTHER);
g_TxdStore.PushCurrentTxd();
g_TxdStore.SetCurrentTxd(sm_iTxdId);
if (Verifyf(fwTxd::GetCurrent(), "Current Texture Dictionary (id %u) is NULL ", sm_iTxdId.Get()))
{
sm_Sprite.SetTexture("Credit_Logos");
}
g_TxdStore.PopCurrentTxd();
}
}
}
void CCredits::UpdateInput()
{
if (sm_launchedFromPauseMenu)
{
#if !RSG_PC
if (CControlMgr::GetPlayerPad() && !CControlMgr::GetPlayerPad()->IsConnected(PPU_ONLY(true)))
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "SG_HDNG", "NO_PAD", FE_WARNING_NONE);
return;
}
#endif
if (CPauseMenu::CheckInput(FRONTEND_INPUT_LEFT))
{
AdjustCreditScrollSpeed(CREDITS_SLOWER);
}
else if (CPauseMenu::CheckInput(FRONTEND_INPUT_RIGHT))
{
AdjustCreditScrollSpeed(CREDITS_FASTER);
}
else if (CPauseMenu::CheckInput(FRONTEND_INPUT_BACK))
{
bShutdownPending = true;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::Render()
// PURPOSE: renders the credits
/////////////////////////////////////////////////////////////////////////////////////
void CCredits::Render(bool bBeforeFade)
{
SYS_CS_SYNC(s_creditsCritSecToken);
if (!bCreditsRunning || bShutdownPending)
return;
#if RSG_PC
if (g_rlPc.IsUiShowing())
return;
#endif // RSG_PC
if (bBeforeFade != bScriptWantsCreditsBeforeFade)
return;
if (sm_usingCloudCredits)
{
const bool c_downloadingCredits = sm_creditsDownloadHelper->IsDownloading();
const bool c_fileParsed = sm_creditsDownloadHelper->IsFileParseSuccessful();
const bool c_hasError = sm_creditsDownloadHelper->HasError();
if ((c_downloadingCredits || !c_fileParsed) && !c_hasError)
{
// Still downloading/processing, just wait.
CVideoEditorInstructionalButtons::Render();
return;
}
}
float PixelsDown = 0;
PixelsDown += fStartPixelsDown; // apply the pixels down from what we rendered last frame
if (!fwTimer::IsGamePaused() ||
sm_launchedFromPauseMenu)
{
float offset = SPACE_SCALER;
if (sm_launchedFromPauseMenu)
{
offset = sm_currentSpeed;
}
PixelsScrolled += fwTimer::GetTimeStepInMilliseconds() * offset;
}
CreditTextLayout.SetWrap(GetWrapLeftRight());
CreditTextLayout.SetOrientation(FONT_CENTRE);
bool bCheckForStartLine = true;
int iEndLineRenderedThisFrame = (iStartLineRenderedThisFrame + NUMBER_OF_LINES_DOWN_TO_PROCESS);
// go through each credit item and display it
for (u32 i = iStartLineRenderedThisFrame; i < iEndLineRenderedThisFrame; i++)
{
Vector2 vSize(0, 0);
bool bSprite = false;
bool bSpace = false;
bool bTranslatedText = false;
if (i < m_CreditArray.CreditItems.GetCount())
{
const bool c_literal = m_CreditArray.CreditItems[i].bLiteral;
switch (m_CreditArray.CreditItems[i].LineType)
{
case JOB_BIG:
{
vSize = Vector2(0.600f, 0.795f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_STANDARD);
bTranslatedText = !c_literal;
break;
}
case JOB_MED:
{
vSize = Vector2(0.568f, 0.69f); // made this one a bit smaller, and we will add the difference as extra padding after the render below
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_CURSIVE);
bTranslatedText = !c_literal;
break;
}
case JOB_SMALL:
{
vSize = Vector2(0.418f, 0.595f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_STANDARD);
bTranslatedText = !c_literal;
break;
}
case NAME_BIG:
{
vSize = Vector2(0.478f, 0.575f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
break;
}
case NAME_MED:
{
vSize = Vector2(0.458f, 0.555f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
break;
}
case NAME_SMALL:
{
vSize = Vector2(0.418f, 0.595f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
break;
}
case LEGALS:
{
vSize = Vector2(0.458f, 0.555f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
if (!sm_bUsingLimitedFont)
{
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
}
else
{
CreditTextLayout.SetStyle(FONT_STYLE_STANDARD);
}
bTranslatedText = !c_literal;
break;
}
case AUDIO_NAME:
{
vSize = Vector2(0.2f, 0.54f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
break;
}
case AUDIO_LEGALS:
{
vSize = Vector2(0.2f, 0.42f);
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
CreditTextLayout.SetStyle(FONT_STYLE_STANDARD);
bTranslatedText = !c_literal;
break;
}
case JOB_AND_NAME_MED:
{
// the size, font & whether translated will get set for this one later on... as we are in submission & this new feature wants added so this is the quickest way to add it without too much rework
CreditTextLayout.SetColor(CHudColour::GetRGBA(HUD_COLOUR_WHITE));
break;
}
case SPACE_BIG:
{
bSpace = true;
vSize = Vector2(0.0f, 0.15f);
break;
}
case SPACE_MED:
{
bSpace = true;
vSize = Vector2(0.0f, 0.10f);
break;
}
case SPACE_SMALL:
{
bSpace = true;
vSize = Vector2(0.0f, 0.05f);
break;
}
case SPACE_END:
{
bSpace = true;
vSize = Vector2(0.0f, 1.0f); // very large amount so everything is scrolled off the screen
break;
}
case SPRITE_1:
{
bSpace = true;
bSprite = true;
vSize = Vector2(0.8f, 0.266f);
break;
}
default:
{
Assertf(0, "Invalid Credit line type");
}
}
}
else
{
sm_bReachedEnd = true;
bSpace = true;
vSize = Vector2(0.0f, 2.0f); // very large amount so everything is scrolled off the screen
}
CHudTools::AdjustForWidescreen(WIDESCREEN_FORMAT_SIZE_ONLY, NULL, &vSize, NULL);
if (!bSprite)
{
if (!bSpace)
{
bool bPrintedThisLineThisFrame = false;
const char* pTextToDisplay = NULL;
if (m_CreditArray.CreditItems[i].cTextId2.GetLength() > 0)
{
// 2 items so have one left justified, one right:
Vector2 vExtreems;
float fDivisor = 1.0f;
#if SUPPORT_MULTI_MONITOR
if (GRCDEVICE.GetMonitorConfig().isMultihead())
fDivisor = 3.0f;
#endif // SUPPORT_MULTI_MONITOR
if (gVpMan.IsUsersMonitorWideScreen()) // bring these in a little to fix 1589960
{
Vector2 vWrap = GetWrapLeftRight();
float fOffset = 0.17f;
fOffset /= fDivisor;
vExtreems = Vector2(vWrap.x + fOffset, vWrap.y - fOffset);
}
else
{
Vector2 vWrap = GetWrapLeftRight();
float fOffset = 0.12f;
fOffset /= fDivisor;
vExtreems = Vector2(vWrap.x + fOffset, vWrap.y - fOffset);
}
CreditTextLayout.SetWrap(&vExtreems);
bool hasCenterColumn = ((m_CreditArray.CreditItems[i].cTextId3.GetLength() > 0) && (stricmp(m_CreditArray.CreditItems[i].cTextId3.c_str(), "BLANK")));
if (hasCenterColumn)
{
CreditTextLayout.SetOrientation(FONT_RIGHT);
if (bTranslatedText)
{
pTextToDisplay = TheText.Get(m_CreditArray.CreditItems[i].cTextId3.c_str());
}
else
{
pTextToDisplay = m_CreditArray.CreditItems[i].cTextId3.c_str();
}
bPrintedThisLineThisFrame = PrintCreditText(0.0f, vSize.x, vSize.y, pTextToDisplay, PixelsDown, PixelsScrolled, false);
}
if ((m_CreditArray.CreditItems[i].cTextId2.GetLength() > 0) && (stricmp(m_CreditArray.CreditItems[i].cTextId2.c_str(), "BLANK")))
{
CreditTextLayout.SetOrientation(hasCenterColumn ? (u8)FONT_CENTRE : (u8)FONT_RIGHT);
float posX = hasCenterColumn ? 0.5f : 0.0f;
if (m_CreditArray.CreditItems[i].LineType == JOB_AND_NAME_MED)
{
vSize = Vector2(0.458f, 0.555f);
CreditTextLayout.SetStyle(FONT_STYLE_CONDENSED);
bTranslatedText = false;
}
if (bTranslatedText)
{
pTextToDisplay = TheText.Get(m_CreditArray.CreditItems[i].cTextId2.c_str());
}
else
{
pTextToDisplay = m_CreditArray.CreditItems[i].cTextId2.c_str();
}
bPrintedThisLineThisFrame = PrintCreditText(posX, vSize.x, vSize.y, pTextToDisplay, PixelsDown, PixelsScrolled, false);
}
if ((m_CreditArray.CreditItems[i].cTextId1.GetLength() > 0) && (stricmp(m_CreditArray.CreditItems[i].cTextId1.c_str(), "BLANK")))
{
CreditTextLayout.SetOrientation(FONT_LEFT);
if (m_CreditArray.CreditItems[i].LineType == JOB_AND_NAME_MED)
{
vSize = Vector2(0.568f, 0.69f); // made this one a bit smaller, and we will add the difference as extra padding after the render below
CreditTextLayout.SetStyle(FONT_STYLE_CURSIVE);
const bool c_literal = m_CreditArray.CreditItems[i].bLiteral;
bTranslatedText = !c_literal;
}
if (bTranslatedText)
{
pTextToDisplay = TheText.Get(m_CreditArray.CreditItems[i].cTextId1.c_str());
}
else
{
pTextToDisplay = m_CreditArray.CreditItems[i].cTextId1.c_str();
}
bPrintedThisLineThisFrame = PrintCreditText(vExtreems.x, vSize.x, vSize.y, pTextToDisplay, PixelsDown, PixelsScrolled, true);
}
if (m_CreditArray.CreditItems[i].LineType == JOB_AND_NAME_MED) // add some padding as we changed the font size
{
PixelsDown += 0.005f;
}
}
else
{
if (m_CreditArray.CreditItems[i].cTextId1.GetLength() > 0)
{
// one item, so CENTRE it:
CreditTextLayout.SetWrap(GetWrapLeftRight());
CreditTextLayout.SetOrientation(FONT_CENTRE);
if (bTranslatedText)
{
pTextToDisplay = TheText.Get(m_CreditArray.CreditItems[i].cTextId1.c_str());
}
else
{
pTextToDisplay = m_CreditArray.CreditItems[i].cTextId1.c_str();
}
bPrintedThisLineThisFrame = PrintCreditText(0.5f, vSize.x, vSize.y, pTextToDisplay, PixelsDown, PixelsScrolled, true);
if (m_CreditArray.CreditItems[i].LineType == JOB_MED) // add some padding as we changed the font size
{
PixelsDown += 0.005f;
}
}
}
// check and store the 1st line we printed this frame so we know we dont need to bother testing this one again next frame
if (bCheckForStartLine)
{
if (bPrintedThisLineThisFrame)
{
if (iStartLineRenderedThisFrame != i)
{
// Displayf("\nNEW START LINE: %d\n", i);
iStartLineRenderedThisFrame = i + 1; // store this credit as the 1st one printed this frame
fStartPixelsDown = PixelsDown;
}
bCheckForStartLine = false;
}
}
}
else
{
if (PrintCreditSpace(vSize.y, PixelsDown, PixelsScrolled))
{
if (bCheckForStartLine)
{
if (iStartLineRenderedThisFrame != i)
{
// Displayf("\nNEW START SPACE: %d\n", i);
iStartLineRenderedThisFrame = i + 1; // store this credit as the 1st one printed this frame
fStartPixelsDown = PixelsDown;
}
bCheckForStartLine = false;
}
}
}
}
else
//
// sprite:
//
{
if (DrawSprite(vSize.x, vSize.y, PixelsDown, PixelsScrolled))
{
if (bCheckForStartLine)
{
if (iStartLineRenderedThisFrame != i)
{
iStartLineRenderedThisFrame = i + 1; // store this credit as the 1st one printed this frame
fStartPixelsDown = PixelsDown;
}
bCheckForStartLine = false;
}
}
}
}
CText::Flush();
// Do wide screen (again) to go on top of the text
/*if (camInterface::IsWidescreen())
{
camInterface::DrawBordersForWideScreen();
}*/
// Make sure credits get switched off at the end.
if (!bShutdownPending && ((-PixelsScrolled) + PixelsDown + 1.0f) < MARGIN)
{
bShutdownPending = true;
}
if (sm_launchedFromPauseMenu)
{
CVideoEditorInstructionalButtons::Render();
}
}
void CCredits::AdjustCreditScrollSpeed(int fasterSlower)
{
sm_currentSpeed += fasterSlower > 0 ? SPACE_SCALER_INTERVAL : -SPACE_SCALER_INTERVAL;
sm_currentSpeed = Clamp<float>(sm_currentSpeed, SPACE_SCALER_SLOWEST, SPACE_SCALER_FASTEST);
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::PrintCreditText()
// PURPOSE: renders one line of credits
/////////////////////////////////////////////////////////////////////////////////////
bool CCredits::PrintCreditText(float PosX, float ScaleX, float ScaleY, const char* pString, float& PixelsDown, float PixelsScrolled, bool bMoveOn)
{
bool bRenderedThisFrame = false;
Vector2 vOnScreenPos(PosX, MARGIN + 1.0f + PixelsDown - PixelsScrolled);
s32 iNumLines = 1;
CreditTextLayout.SetScale(Vector2(ScaleX, ScaleY));
#if RSG_PC
CreditTextLayout.SetEnableMultiheadBoxWidthAdjustment(false);
#endif // RSG_PC
if (vOnScreenPos.y > -MARGIN && vOnScreenPos.y < 1.0f + MARGIN)
{
CreditTextLayout.Render(&vOnScreenPos, pString);
bRenderedThisFrame = true;
}
if (bMoveOn)
{
iNumLines = rage::Max(CreditTextLayout.GetNumberOfLines(&vOnScreenPos, pString), 1);
float fCharHeight = CreditTextLayout.GetCharacterHeight();
if (sm_bUsingLimitedFont)
{
fCharHeight += 0.004f; // a little extra spacing
}
PixelsDown += (fCharHeight * iNumLines);
}
return bRenderedThisFrame;
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::DrawSprite()
// PURPOSE: renders the sprite
/////////////////////////////////////////////////////////////////////////////////////
bool CCredits::DrawSprite(float ScaleX, float ScaleY, float& PixelsDown, float PixelsScrolled)
{
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
bool bRenderedThisFrame = false;
Vector2 vOnScreenPos(0.5f - (ScaleX * 0.5f), (MARGIN + 1.0f + PixelsDown - PixelsScrolled) - ScaleY);
if ((vOnScreenPos.y > -ScaleY) && (vOnScreenPos.y < 1.0f + ScaleY))
{
if (sm_Sprite.GetTexture() && sm_Sprite.GetShader())
{
grcStateBlock::SetRasterizerState(grcStateBlock::RS_NoBackfaceCull);
grcStateBlock::SetDepthStencilState(grcStateBlock::DSS_IgnoreDepth);
grcStateBlock::SetBlendState(ms_StandardSpriteBlendStateHandle);
sm_Sprite.SetRenderState();
Vector2& vPos = vOnScreenPos;
Vector2 vSize = Vector2(ScaleX, ScaleY);
#if SUPPORT_MULTI_MONITOR
if (GRCDEVICE.GetMonitorConfig().isMultihead())
{
const fwRect area = GRCDEVICE.GetMonitorConfig().getLandscapeMonitor().getArea();
vPos.x = (vPos.x / 3.0f) + area.left;
vSize.x *= 0.33333f;
}
#endif // SUPPORT_MULTI_MONITOR
Vector2 v[4], uv[4];
v[0].Set(vPos.x, vPos.y + vSize.y);
v[1].Set(vPos.x, vPos.y);
v[2].Set(vPos.x + vSize.x, vPos.y + vSize.y);
v[3].Set(vPos.x + vSize.x, vPos.y);
#define __UV_OFFSET (0.002f)
uv[0].Set(__UV_OFFSET, 1.0f - __UV_OFFSET);
uv[1].Set(__UV_OFFSET, __UV_OFFSET);
uv[2].Set(1.0f - __UV_OFFSET, 1.0f - __UV_OFFSET);
uv[3].Set(1.0f - __UV_OFFSET, __UV_OFFSET);
sm_Sprite.Draw(v[0], v[1], v[2], v[3], uv[0], uv[1], uv[2], uv[3], CRGBA(255, 255, 255, 255));
}
}
else if (vOnScreenPos.y < (-ScaleY))
{
bRenderedThisFrame = true;
}
//PixelsDown += ScaleY;
return bRenderedThisFrame;
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CCredits::PrintCreditSpace()
// PURPOSE: moves down one space
/////////////////////////////////////////////////////////////////////////////////////
bool CCredits::PrintCreditSpace(float ScaleY, float& PixelsDown, float PixelsScrolled)
{
bool bRenderedThisFrame = false;
float fOnScreenY = MARGIN + 1.0f + PixelsDown - PixelsScrolled;
if (fOnScreenY > -MARGIN && fOnScreenY < 1.0f + MARGIN)
{
bRenderedThisFrame = true;
}
PixelsDown += (ScaleY);
return bRenderedThisFrame;
}
Vector2 CCredits::GetWrapLeftRight()
{
Vector2 vWrap(0.08f, 0.92f);
#if SUPPORT_MULTI_MONITOR
if (GRCDEVICE.GetMonitorConfig().isMultihead())
{
const fwRect area = GRCDEVICE.GetMonitorConfig().getLandscapeMonitor().getArea();
float fLeft = area.left + 0.027f;
float fRight = area.right - 0.027f;
vWrap.x = fLeft;
vWrap.y = fRight;
}
#endif // SUPPORT_MULTI_MONITOR
return vWrap;
}
void CCredits::UpdateInstructionalButtons()
{
if (!sm_launchedFromPauseMenu)
{
return;
}
if (sm_usingCloudCredits)
{
const bool c_downloadingCredits = sm_creditsDownloadHelper->IsDownloading();
const bool c_fileParsed = sm_creditsDownloadHelper->IsFileParseSuccessful();
const bool c_hasError = sm_creditsDownloadHelper->HasError();
if ((c_downloadingCredits || !c_fileParsed) && !c_hasError)
{
char const* const c_downloadingLabel = TheText.Get(atHashString("INSTALL_SCREEN_DOWNLOAD", 0x2A81BA7E));
CVideoEditorInstructionalButtons::AddButton(ICON_SPINNER, c_downloadingLabel);
}
else
{
char const* const c_backLabel = TheText.Get(atHashString("IB_BACK", 0xE5FC11CD));
CVideoEditorInstructionalButtons::AddButton(rage::INPUT_FRONTEND_CANCEL, c_backLabel, true);
char const* const c_fasterLabel = TheText.Get(atHashString("IB_FASTER", 0x7015C9E3));
CVideoEditorInstructionalButtons::AddButton(rage::INPUT_FRONTEND_RIGHT, c_fasterLabel, true);
char const* const c_slowerLabel = TheText.Get(atHashString("IB_SLOWER", 0x684A58B8));
CVideoEditorInstructionalButtons::AddButton(rage::INPUT_FRONTEND_LEFT, c_slowerLabel, true);
}
}
}
//
// DEBUG CODE CAN GO HERE AT THE END:
//
#if __BANK
//
// name: CCredits::InitWidgets
// description: Add debug widgets
void CCredits::InitWidgets()
{
bkBank& bank = BANKMGR.CreateBank("Credits");
bank.AddButton("Start/End Credits", &CCredits::StartEndCreditsButton);
bank.AddSlider("Jump to %", &sm_iDebugPercentage, 0, 100, 1, &CCredits::JumpToPosition);
}
//
// name: CCredits::StartEndCreditsButton()
// description: starts or finishes the credits
void CCredits::StartEndCreditsButton()
{
// start a cutscene:
if (!IsRunning())
{
Init(INIT_AFTER_MAP_LOADED);
}
else
{
Shutdown(SHUTDOWN_WITH_MAP_LOADED);
}
}
//
// name: CCredits::JumpToPosition()
// description: jump to % position in credits
void CCredits::JumpToPosition()
{
iStartLineRenderedThisFrame = (u32)(((float)m_CreditArray.CreditItems.GetCount() / 100.0f) * (float)sm_iDebugPercentage);
Displayf("CREDITS: JUMPED TO %d%% (%d)", sm_iDebugPercentage, iStartLineRenderedThisFrame);
}
#endif //#if __BANK
// eof