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

531 lines
15 KiB
C++

#include "Frontend/Store/PauseStoreMenu.h"
#include "Frontend/Store/StoreUIChannel.h"
#include "Frontend/ButtonEnum.h"
#include "Frontend/PauseMenu.h"
#include "frontend/Store/StoreScreenMgr.h"
#include "network/Live/livemanager.h"
#include "network/NetworkInterface.h"
#include "Network/Sessions/NetworkSession.h"
#include "rline/rlgamerinfo.h"
#include "Stats/MoneyInterface.h"
#include "net/nethardware.h"
FRONTEND_STORE_OPTIMISATIONS()
XPARAM(leavempforstore);
const unsigned int STORE_UNAVAILABLE_DISPLAY_FRAME_THRESHOLD = 90;
CPauseStoreMenu::CPauseStoreMenu( CMenuScreen& owner ): CMenuBase(owner),
m_storeState(STATE_NONE),
m_StoreSigninFrameCount(0),
m_WaitingForLoginScreenReturn(false),
m_WaitingForLoginScreenEntrance(false),
m_bIsDisplayingNews(false),
m_bStoryTextPosted(false)
{
}
CPauseStoreMenu::~CPauseStoreMenu()
{
}
void CPauseStoreMenu::Init()
{
m_WaitingForLoginScreenReturn = false;
m_WaitingForLoginScreenEntrance = false;
m_StoreSigninFrameCount = 0;
}
void CPauseStoreMenu::Update()
{
CMenuBase::Update();
if ( m_WaitingForLoginScreenEntrance && CLiveManager::IsSystemUiShowing() )
{
m_WaitingForLoginScreenEntrance = false;
m_WaitingForLoginScreenReturn = true;
}
else if ( m_WaitingForLoginScreenReturn && !CLiveManager::IsSystemUiShowing())
{
m_WaitingForLoginScreenReturn = false;
if( CPauseMenu::IsStoreAvailable() && !CLiveManager::GetCommerceMgr().IsThinDataPopulationInProgress() )
{
//We close the pause menu immediately unless we are in an MP game AND the leave mp for store param is set
bool closePauseMenuImmediately = !(NetworkInterface::IsGameInProgress() && PARAM_leavempforstore.Get());
if (closePauseMenuImmediately)
{
CPauseMenu::TriggerStore();
}
else
{
cStoreScreenMgr::RequestMPStore();
//Lock the store screen early to avoid glitches when leaving the session.
CPauseMenu::TogglePauseRenderPhases(false, OWNER_STORE, __FUNCTION__ );
}
}
}
//We use this to stop flickers of unavailability, and a visible progression of states on sign in.
if ( CPauseMenu::IsStoreAvailable() )
{
m_StoreSigninFrameCount = 0;
// If we are displaying news, let's check for any updates
if( m_bIsDisplayingNews)
{
CNetworkPauseMenuNewsController& newsController = CNetworkPauseMenuNewsController::Get();
if(newsController.IsPendingStoryReady())
{
PostStoreStory(newsController.GetPendingStory(), true);
UpdateStoreColumnScroll(newsController.GetNumStories(), newsController.GetPendingStoryIndex());
CNetworkPauseMenuNewsController::Get().MarkPendingStoryAsShown();
m_bStoryTextPosted = false;
}
else if(!m_bStoryTextPosted && newsController.IsPendingStoryDataRcvd())
{
PostStoreStory(newsController.GetPendingStory(), false);
UpdateStoreColumnScroll(newsController.GetNumStories(), newsController.GetPendingStoryIndex());
m_bStoryTextPosted = true;
}
}
}
//We use this point to delay the DC message after sign in. Hopefully I can get a more reliable method to check if sign in is in progress.
if (m_StoreSigninFrameCount > 0)
{
m_StoreSigninFrameCount--;
}
if (IsNotConnected())
{
m_StoreSigninFrameCount = STORE_UNAVAILABLE_DISPLAY_FRAME_THRESHOLD;
}
if(m_storeState != GetStoreState())
{
Populate(MENU_UNIQUE_ID_STORE); // We don't want to do this every frame, only when the state has changed
CPauseMenu::RedrawInstructionalButtons();
}
if(CPauseMenu::DynamicMenuExists() &&
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().IsActive() &&
CLiveManager::GetInviteMgr().HasPendingAcceptedInvite())
{
// If we've started an invite let's kill the warning screen
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().Clear();
}
}
// void CPauseStoreMenu::Render(const PauseMenuRenderDataExtra* renderData)
// {
// CMenuBase::Render(renderData);
// }
bool CPauseStoreMenu::UpdateInput( s32 sInput )
{
if ( cStoreScreenMgr::IsPendingNetworkShutdownToOpen() )
{
//Dont allow the screen to exit if we are trying to leave a network session and open the store.
return true;
}
//BIG HONKING NOTE OF FUTURE PROTECTION-NESS! WHEN THIS MENU PANEL IS PRETTIED UP THE FUNCTIONALITY
//HERE NEEDS TO BE PRESERVED LEST WE ANGER THE QA ELDER ONES WITH TRC BREACHES.
//(That's right. Lovecraftian warnings.)
if( sInput == PAD_CROSS )
{
#if !RSG_ORBIS // B*1817634 - Cannot show Sign-in UI on PS4
if ( !CLiveManager::IsOnline() )
{
CLiveManager::ShowSigninUi();
m_WaitingForLoginScreenEntrance = true;
}
else
#endif
if (
#if !RSG_PC
(NetworkInterface::IsGameInProgress() && CNetwork::GetNetworkSession().IsActivitySession()) ||
#endif
!CPauseMenu::IsStoreAvailable())
{
//We do not open the store in this case
}
else if (CPauseMenu::IsStoreAvailable() && !CLiveManager::GetCommerceMgr().IsThinDataPopulationInProgress())
{
const rlGamerInfo* pGamerInfo = NetworkInterface::GetActiveGamerInfo();
if (!rlGamerInfo::HasStorePrivileges(pGamerInfo->GetLocalIndex()))
{
//Display the warning screen
DisplayUnderageWarning();
return true;
}
atString baseCat("TOP_DEFAULT");
cStoreScreenMgr::SetBaseCategory(baseCat);
//We no longer leave the game to access the store in NG.
//We close the pause menu immediately unless we are in an MP game AND the leave mp for store param is set
bool closePauseMenuImmediately = !(NetworkInterface::IsGameInProgress() && PARAM_leavempforstore.Get());
if (closePauseMenuImmediately)
{
CPauseMenu::TriggerStore();
}
else
{
//We populate here for MP since a lot of data is lost during the session exit.
cStoreScreenMgr::PopulateDisplayValues();
DisplayLeaveSessionWarning();
}
CPauseMenu::PlaySound("SELECT");
return true;
}
else if(!CPauseMenu::IsStoreAvailable())
{
// Display Warning
DisplayStoreUnavailableWarning();
}
}
if ( sInput == PAD_CIRCLE && cStoreScreenMgr::IsPendingNetworkShutdownToOpen() )
{
//Dont allow the screen to exit if we are trying to leave a network session and open the store.
return true;
}
if (SUIContexts::IsActive("STORE_CanScroll") && m_bIsDisplayingNews)
{
if( CPauseMenu::CheckInput(FRONTEND_INPUT_RUP, true))
{
CNetworkPauseMenuNewsController::Get().RequestStory(-1);
ShowColumnWarning(PM_COLUMN_LEFT, 3, "");
CScaleformMenuHelper::SET_BUSY_SPINNER(PM_COLUMN_MIDDLE, true);
}
else if( CPauseMenu::CheckInput(FRONTEND_INPUT_RDOWN, true))
{
CNetworkPauseMenuNewsController::Get().RequestStory(1);
ShowColumnWarning(PM_COLUMN_LEFT, 3, "");
CScaleformMenuHelper::SET_BUSY_SPINNER(PM_COLUMN_MIDDLE, true);
}
}
return CMenuBase::UpdateInput( sInput );
}
bool CPauseStoreMenu::Populate(MenuScreenId newScreenId)
{
bool bResult = false;
bool showAcceptButton = true;
if(newScreenId.GetValue() == MENU_UNIQUE_ID_STORE)
{
m_storeState = GetStoreState();
switch(m_storeState)
{
case STATE_DISCONNECTED:
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "NOT_CONNECTED", "WARNING_NOT_CONNECTED_TITLE");
#if !__XENON
showAcceptButton = false;
#endif
break;
case STATE_UNDERAGE:
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "HUD_PERM", "CWS_WARNING");
break;
case STATE_NOCONNECTION:
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "HUD_NONETWORK", "CWS_WARNING");
#if !RSG_PC // if no connection, we'll bring up the sign-in page that will say "retry connection" ...so need accept for that
showAcceptButton = false;
#endif
break;
#if RSG_ORBIS
case STATE_NP_UNAVAILABLE:
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, CPauseMenu::GetOfflineReasonAsLocKey(), "CWS_WARNING");
break;
#endif // RSG_ORBIS
case STATE_UNAVAILABLE_MP:
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "STORE_UNAVAIL_MP", "WARNING_STORE_UNAVAIL_TITLE");
showAcceptButton = false;
break;
case STATE_UNAVAILABLE:
#if __XENON
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "SCLB_NO_ROS", "WARNING_STORE_UNAVAIL_TITLE");
#else
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "STORE_UNAVAIL", "WARNING_STORE_UNAVAIL_TITLE");
#endif
showAcceptButton = false;
break;
case STATE_NONE:
default:
bResult = PopulateStoreNews();
break;
}
if (m_storeState != STATE_NONE)
{
bResult = false;
m_bIsDisplayingNews = false;
#if RSG_ORBIS
showAcceptButton = false; // Orbis only shows accept button when everything is fine. so override any other prev logic
#endif
}
}
if (showAcceptButton)
{
SUIContexts::Deactivate("HIDE_ACCEPTBUTTON");
}
else
{
SUIContexts::Activate("HIDE_ACCEPTBUTTON");
}
return bResult;
}
eStoreState CPauseStoreMenu::GetStoreState()
{
eStoreState storeState = STATE_NONE;
if (IsLinkNotConnected())
{
storeState = STATE_NOCONNECTION;
}
else if(IsNotConnected())
{
#if RSG_ORBIS
const rlGamerInfo* pGamerInfo = NetworkInterface::GetActiveGamerInfo();
if( !g_rlNp.GetNpAuth().IsNpAvailable( pGamerInfo->GetLocalIndex() ) )
{
storeState = STATE_NP_UNAVAILABLE;
}
else
#endif
{
storeState = STATE_DISCONNECTED;
}
}
else if(IsStoreUnavailableMP())
{
storeState = STATE_UNAVAILABLE_MP;
}
else if(IsStoreUnavailable())
{
//We do this to account for log in time.
if (m_StoreSigninFrameCount > 0)
{
storeState = STATE_DISCONNECTED;
}
else
{
storeState = STATE_UNAVAILABLE;
}
}
else if(CLiveManager::CheckIsAgeRestricted())
{
storeState = STATE_UNDERAGE;
}
return storeState;
}
bool CPauseStoreMenu::IsLinkNotConnected()
{
#if RSG_PC
// just check against not connected... can't be running without a SC account on PC
// this function overrides IsNotConnected in conditions so use here so rest of code works
return IsNotConnected();
#else
return !netHardware::IsLinkConnected();
#endif
}
bool CPauseStoreMenu::IsNotConnected()
{
return !CLiveManager::IsOnline();
}
bool CPauseStoreMenu::IsStoreUnavailableMP()
{
// we're happy opening the store on PC, as it's on the SCUI menu
#if RSG_PC
return false;
#else
return NetworkInterface::IsGameInProgress() && CNetwork::GetNetworkSession().IsActivitySession();
#endif
}
bool CPauseStoreMenu::IsStoreUnavailable()
{
#if !RSG_PC
// B*2413863 - moved this cloud save check to general unavailable, to display the correct generic "try again later" string
if (NetworkInterface::IsNetworkOpen())
{
if (!MoneyInterface::SavegameCanBuyCashProducts())
return true;
}
#endif
return !CPauseMenu::IsStoreAvailable();
}
bool CPauseStoreMenu::PopulateStoreNews()
{
CNetworkPauseMenuNewsController& newsController = CNetworkPauseMenuNewsController::Get();
if(!m_bIsDisplayingNews && newsController.InitControl(NEWS_TYPE_STORE_HASH))
{
int iNumStories = newsController.GetNumStories();
if(iNumStories > 1)
{
if(!SUIContexts::IsActive("STORE_CanScroll"))
{
SUIContexts::Activate("STORE_CanScroll");
CPauseMenu::RedrawInstructionalButtons();
}
UpdateStoreColumnScroll(iNumStories, newsController.GetPendingStoryIndex());
}
m_bIsDisplayingNews = true;
ShowColumnWarning(PM_COLUMN_LEFT, 3, "");
CScaleformMenuHelper::SET_BUSY_SPINNER(PM_COLUMN_MIDDLE, true);
}
else
{
ShowColumnWarning_Loc(PM_COLUMN_LEFT, 3, "", "CMRC_STORE_OPEN");
}
return true;
}
void CPauseStoreMenu::PostStoreStory(CNetworkSCNewsStoryRequest* pNewsItem, bool bUpdateTexture)
{
CScaleformMenuHelper::SET_BUSY_SPINNER(PM_COLUMN_MIDDLE_RIGHT, false);
if(bUpdateTexture)
{
ShowColumnWarning(PM_COLUMN_LEFT, 3, pNewsItem->GetContent(), pNewsItem->GetHeadline(), 0, pNewsItem->GetTxdName(), pNewsItem->GetTxdName(), IMG_ALIGN_RIGHT);
}
else
{
ShowColumnWarning(PM_COLUMN_LEFT, 3, pNewsItem->GetContent(), pNewsItem->GetHeadline(), 0, "", "", IMG_ALIGN_RIGHT);
}
CScaleformMenuHelper::SET_BUSY_SPINNER(PM_COLUMN_MIDDLE, false);
}
void CPauseStoreMenu::UpdateStoreColumnScroll(int iNumStories, int iSelectedStory)
{
char pszCaption[64] = {0};
CNumberWithinMessage pArrayOfNumbers[2];
pArrayOfNumbers[0].Set(iSelectedStory+1);
pArrayOfNumbers[1].Set(iNumStories);
if ( iNumStories > 1 )
{
CMessages::InsertNumbersAndSubStringsIntoString(TheText.Get("NEWS_NUM_STORIES"),pArrayOfNumbers,2,NULL,0,pszCaption,64);
CScaleformMenuHelper::SET_COLUMN_SCROLL(PM_COLUMN_LEFT, pszCaption);
}
}
void CPauseStoreMenu::LoseFocus()
{
uiDebugf3("CPauseStoreMenu::LoseFocus");
SUIContexts::Deactivate("STORE_CanScroll");
m_bIsDisplayingNews = false;
// Cleanup Newswire
CNetworkPauseMenuNewsController::Get().Reset();
}
void CPauseStoreMenu::DisplayUnderageWarning()
{
#if RSG_DURANGO
// Display the Xbox system UI telling the user they can't access the store.
uiVerifyf(
CLiveManager::ResolvePlatformPrivilege(
NetworkInterface::GetLocalGamerIndex(),
rlPrivileges::PrivilegeTypes::PRIVILEGE_PURCHASE_CONTENT,
true),
"CLiveManager::ResolvePlatformPrivilege with attemptResolution = true failed, so the system UI won't be shown");
#else
CWarningMessage& rMessage = CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage();
CWarningMessage::Data messageData;
messageData.m_TextLabelBody = "HUD_AGERES";
messageData.m_iFlags = FE_WARNING_OK;
messageData.m_acceptPressed = datCallback(MFA(CMenuScreen::BackOutOfWarningMessageToContextMenu), (datBase*)&m_Owner);
rMessage.SetMessage(messageData);
#endif
}
void CPauseStoreMenu::DisplayLeaveSessionWarning()
{
CWarningMessage& rMessage = CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage();
CWarningMessage::Data messageData;
messageData.m_TextLabelBody = "GOTO_STORE_CONFIRM";
messageData.m_TextLabelSubtext = "PM_QUIT_WARN11";
messageData.m_iFlags = FE_WARNING_YES_NO;
messageData.m_acceptPressed = datCallback(MFA(CPauseStoreMenu::ConfirmDestructiveActionCallback), (datBase*)&m_Owner);
messageData.m_declinePressed = datCallback(MFA(CMenuScreen::BackOutOfWarningMessageToContextMenu), (datBase*)&m_Owner);
rMessage.SetMessage(messageData);
}
void CPauseStoreMenu::DisplayStoreUnavailableWarning()
{
CWarningMessage& rMessage = CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage();
CWarningMessage::Data messageData;
#if __XENON || RSG_DURANGO
if ( !CLiveManager::IsSignedIn() || CLiveManager::IsOnline() )
{
messageData.m_TextLabelBody = "STORE_UNAVAIL";
}
else
{
messageData.m_TextLabelBody = "SCLB_NO_ROS";
}
#else
messageData.m_TextLabelBody = "STORE_UNAVAIL";
#endif
messageData.m_iFlags = FE_WARNING_OK;
messageData.m_acceptPressed = datCallback(MFA(CWarningMessage::Clear), &CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage());
rMessage.SetMessage(messageData);
}
void CPauseStoreMenu::ConfirmDestructiveActionCallback()
{
// remove the warning message
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().Clear();
CPauseMenu::TriggerStore(true);
//cStoreScreenMgr::RequestMPStore();
}