#include "Frontend/Store/StoreMainScreen.h" #include "frontend/hud_colour.h" #include "frontend/PauseMenu.h" #include "frontend/BusySpinner.h" #include "Frontend/FrontendStatsMgr.h" #include "frontend/HudTools.h" #include "frontend/NewHud.h" #include "frontend/Scaleform/ScaleFormMgr.h" #include "Frontend/Store/StoreScreenMgr.h" #include "Frontend/Store/StoreUIChannel.h" #include "Frontend/Store/StoreTextureManager.h" #include "FrontEnd/WarningScreen.h" #include "game/Clock.h" #include "Network/Cloud/Tunables.h" #include "Network/Live/livemanager.h" #include "Network/Live/NetworkTransactionTelemetry.h" #include "Network/Live/NetworkTelemetry.h" #include "Network/commerce/CommerceManager.h" #include "Network/Stats//NetworkLeaderboardSessionMgr.h" #include "Peds/rendering/PedHeadshotManager.h" #include "SaveLoad/savegame_users_and_devices.h" #include "scene/ExtraContent.h" #include "stats/StatsInterface.h" #include "system/controlMgr.h" #include "system/pad.h" #include "Stats/MoneyInterface.h" #include "text/TextConversion.h" #include "fwcommerce/CommerceUtil.h" #include "fwcommerce/CommerceChannel.h" #include "fwcommerce/CommerceConsumable.h" #include "fwnet/netscgameconfigparser.h" #include "rline/rlgamerinfo.h" #include "rline/rlpresence.h" #include "rline/scmembership/rlscmembership.h" #if RSG_PC #include "rline/rlpc.h" #endif // RSG_PC FRONTEND_STORE_OPTIMISATIONS() PARAM(commerceAlwaysShowUpsell, "[commerce] For products with an upsell, always run the upsell flow"); #define STOREMENU_FILENAME "pause_menu_store_content" #define BUTTONMENU_FILENAME "store_instructional_buttons" const char* BLACKFADE_FILENAME = "STORE_BLACKOUT"; const char* BACKGROUNDMOVIE_FILENAME = "store_background"; const char* CONSUMABLE_AMOUNT_SUB_TAG = "[cash pack amount]"; const int CONSUMABLE_AMOUNT_STR_LEN = 32; #if __XENON const char* PRICE_STRING_SUFFIX = ""; #else const char* PRICE_STRING_SUFFIX = ""; #endif const s32 DEFAULT_TOP_CATAGORY = 0; const s32 NUM_COLUMNS = 3; const s32 CAT_COLUMN_INDEX = 0; const s32 ITEM_COLUMN_INDEX = 1; const s32 DETAIL_COLUMN_INDEX = 2; const int MAX_NUM_ITEMS_IN_MIDDLE_COLUMN = 4; const int MAX_REQUESTED_IMAGES = 6; const int INVALID_COLUMN_INDEX = -1; const int INVALID_ITEM_INDEX = -1; const int RIGHT_STICK_DEADZONE = 64; const int SIZE_OF_PRICE_BUFFER = 64; const int STORE_FADE_TIME = 1000; const int OFFLINE_FRAMES_TO_WAIT_FOR_SIGNOUT_MESSAGE = 12; bool cStoreMainScreen::sm_bPlayerHasJustSignedOut = false; static rlPresence::Delegate sPresenceDlgt; cStoreMainScreen::cStoreMainScreen() : m_LastSelectionTransition(NO_SELECTION_TRANSITION), m_StoreMovieId(INVALID_MOVIE_ID), m_ButtonMovieId(INVALID_MOVIE_ID), m_StoreBgMovieId(INVALID_MOVIE_ID), m_BlackFadeMovieId(INVALID_MOVIE_ID), m_Initialised(false), m_PendingListSetup(false), m_ColumnReturnId(0), m_ItemReturnId(0), m_LastColumnIndex(CAT_COLUMN_INDEX), m_LastItemIndex(INVALID_ITEM_INDEX), m_LastCenterItemIndex(INVALID_ITEM_INDEX), m_PendingSelection(false), m_PendingHighlightChange(false), m_PendingLongDescriptionItem(NULL), m_PendingCheckoutProduct(NULL), m_CurrentlySelectedCategory(INVALID_ITEM_INDEX), m_WasInCheckout(false), m_RefetchingPlatformData(false), m_CurrentStartReturnId(0), m_CurrentEndReturnId(0), m_LastStartIndex(INVALID_ITEM_INDEX), m_LastEndIndex(INVALID_ITEM_INDEX), m_bLoadingAssets(false), m_ConsumableProductCheckLevel(0), mp_CurrentLeaderboardRead(NULL), mp_PendingNumFriendsWhoOwnProduct(NULL), m_CurrentNonCriticalWarningScreen(STORE_NON_CRIT_WARNING_NONE), m_PendingNonCriticalWarningScreen(STORE_NON_CRIT_WARNING_NONE), m_CurrentDCWarningScreen(STORE_WARNING_NONE), m_FramesOffline(0), m_InNoProductsMode(false), m_PedHeadShotHandle(0), m_PendingCash(0), m_WasConsumableDataDirtyLastFrame(false), m_StoreListsSetup(false), m_QuickCheckout(false), m_HasBeenInCheckout(false), m_WasSignedOnlineOnEnter(false) { m_CurrentState = STATE_INVALID; m_ProductIndexArray.Reset(); m_CategoryIndexArray.Reset(); m_RequestedImageIndices.Reset(); m_RequestedImageIndices.Reserve( MAX_REQUESTED_IMAGES ); m_LoadedImageIndices.Reset(); m_LoadedImageIndices.Reserve( MAX_REQUESTED_IMAGES ); m_NumFriendsWhoOwnProductMap.Reset(); sm_bPlayerHasJustSignedOut = false; if (!sPresenceDlgt.IsBound()) { sPresenceDlgt.Bind(cStoreMainScreen::OnPresenceEvent); rlPresence::AddDelegate(&sPresenceDlgt); } #if RSG_NP m_shouldCommerceLogoDisplay = false; m_isCommerceLogoDisplaying = false; #endif } cStoreMainScreen::~cStoreMainScreen() { Shutdown(); if (sPresenceDlgt.IsBound()) sPresenceDlgt.Unbind(); rlPresence::RemoveDelegate(&sPresenceDlgt); } void cStoreMainScreen::Init( int a_StoreMovieId, int a_ButtonMovieId ) { m_StoreMovieId = a_StoreMovieId; m_ButtonMovieId = a_ButtonMovieId; InitInternal(); m_CurrentState = STATE_PENDING_INITIAL_DATA; m_PurchaseCheckProductID.Reset(); m_WasConsumableDataDirtyLastFrame = false; m_TimerSinceInputEnabled.Zero(); } void cStoreMainScreen::Init() { InitInternal(); RequestScaleformMovies(); } void cStoreMainScreen::InitInternal() { m_LastSelectionTransition = NO_SELECTION_TRANSITION; m_Initialised = true; m_ColumnReturnId = 0; m_ItemReturnId = 0; m_LastColumnIndex = CAT_COLUMN_INDEX; m_LastItemIndex = INVALID_ITEM_INDEX; m_LastCenterItemIndex = INVALID_ITEM_INDEX; m_PendingSelection = false; m_PendingHighlightChange = false; m_PendingLongDescriptionItem = NULL; m_PendingCheckoutProduct = NULL; m_CurrentlySelectedCategory = INVALID_ITEM_INDEX; m_WasInCheckout = false; m_RefetchingPlatformData = false; m_WasSignedOnlineOnEnter = CLiveManager::IsOnline(); m_ProductIndexArray.Reset(); m_CategoryIndexArray.Reset(); m_RequestedImageIndices.Reset(); m_RequestedImageIndices.Reserve( MAX_REQUESTED_IMAGES ); m_LoadedImageIndices.Reset(); m_LoadedImageIndices.Reserve( MAX_REQUESTED_IMAGES ); m_CurrentStartReturnId = 0; m_CurrentEndReturnId = 0; m_NumFriendsWhoOwnProductMap.Reset(); m_CurrentLeaderboardDataFetchProductId.Reset(); mp_PendingNumFriendsWhoOwnProduct = NULL; m_CurrentDCWarningScreen = STORE_WARNING_NONE; m_bLoadingAssets = false; sm_bPlayerHasJustSignedOut = false; m_ConsumableProductCheckId.Reset(); m_ConsumableProductCheckLevel = 0; m_FramesOffline = 0; m_InNoProductsMode = false; m_CurrentNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; m_PendingNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; m_WasConsumableDataDirtyLastFrame = false; m_PendingCash = 0; m_StoreListsSetup = false; m_QuickCheckout = false; m_SubscriptionUpsellItemId.Clear(); m_SubscriptionUpsellTargetItemId.Clear(); m_PendingImageRequest = false; InitialiseConsumablesLevel(); // if ( m_PedHeadShotHandle == 0 ) // { // m_PedHeadShotHandle = (s32)PEDHEADSHOTMANAGER.RegisterPed(CGameWorld::FindLocalPlayer()); // } } void cStoreMainScreen::Shutdown() { #if RSG_NP g_rlNp.GetCommonDialog().HideCommerceLogo(); m_shouldCommerceLogoDisplay = false; m_isCommerceLogoDisplaying = false; #endif m_SubscriptionUpsellItemId.Clear(); m_SubscriptionUpsellTargetItemId.Clear(); m_PendingImageRequest = false; // switch this off when we exit the store MEMBERSHIP_ONLY(CLiveManager::SetSuppressMembershipForScript(false)); if ( m_StoreMovieId != INVALID_MOVIE_ID ) { CScaleformMgr::RequestRemoveMovie(m_StoreMovieId); m_StoreMovieId = INVALID_MOVIE_ID; } if ( m_ButtonMovieId != INVALID_MOVIE_ID ) { CBusySpinner::UnregisterInstructionalButtonMovie(m_ButtonMovieId); // register this "instructional button" movie with the spinner system CScaleformMgr::RequestRemoveMovie(m_ButtonMovieId); m_ButtonMovieId = INVALID_MOVIE_ID; } if ( m_StoreBgMovieId != INVALID_MOVIE_ID ) { CScaleformMgr::RequestRemoveMovie(m_StoreBgMovieId); m_StoreBgMovieId = INVALID_MOVIE_ID; } if ( m_BlackFadeMovieId != INVALID_MOVIE_ID ) { CScaleformMgr::RequestRemoveMovie(m_BlackFadeMovieId); m_BlackFadeMovieId = INVALID_MOVIE_ID; } const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); for ( s32 i = 0; i < m_LoadedImageIndices.GetCount(); i++ ) { const cCommerceItemData *itemData = pCommerceUtil->GetItemData(m_LoadedImageIndices[i]); if ( itemData && itemData->GetImagePaths().GetCount() > 0 ) { const atString path(itemData->GetImagePaths()[0]); cStoreScreenMgr::GetTextureManager()->ReleaseTexture( path ); } } m_LoadedImageIndices.Reset(); CLiveManager::GetCommerceMgr().SetAutoConsumePaused(false); if (mp_CurrentLeaderboardRead != NULL) { CNetwork::GetLeaderboardMgr().GetLeaderboardReadMgr().ClearLeaderboardRead(mp_CurrentLeaderboardRead); mp_CurrentLeaderboardRead = NULL; m_CurrentLeaderboardDataFetchProductId.Reset(); } if (m_PedHeadShotHandle != 0) { PEDHEADSHOTMANAGER.UnregisterPed(m_PedHeadShotHandle); m_PedHeadShotHandle = 0; } mp_PendingNumFriendsWhoOwnProduct = NULL; m_Initialised = false; m_CurrentState = STATE_INVALID; //Take this opportunity to kick off an data enumeration. if (!CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataDirty()) { CLiveManager::GetCommerceMgr().GetConsumableManager()->SetOwnershipDataDirty(); } #if NET_ENABLE_MEMBERSHIP_FUNCTIONALITY // refresh membership status - defaulted to false if(Tunables::IsInstantiated() && Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_MEMBERSHIP_REFRESH_ON_STORE_SHUTDOWN", 0x8f865fd1), false)) { storeUIDebugf1("Shutdown :: Requesting Membership Status"); rlScMembership::RequestMembershipStatus(NetworkInterface::GetLocalGamerIndex()); } #endif //We should redisplay cash limit warnings at this point. CLiveManager::GetCommerceMgr().ResetAutoConsumeMessage(); } void cStoreMainScreen::UpdateStoreLists() { if (!CLiveManager::GetCommerceMgr().HasValidData()) { return; } ClearImageRequests(); m_ProductIndexArray.Reset(); m_CategoryIndexArray.Reset(); m_StoreListsSetup = true; if (m_InNoProductsMode) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "MENU_STATE")) { CScaleformMgr::AddParamInt(1); CScaleformMgr::EndMethod(); } } //Clear up previous item column if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "REMOVE_COLUMN")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "ADD_COLUMN")) { //Plus one because this function seems to count the columns up from one. Charming. CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::AddParamString("contentImageList"); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_HEADER_COLOUR")) { CRGBA headerColour = CHudColour::GetRGBA(HUD_COLOUR_BLUE); CScaleformMgr::AddParamInt(headerColour.GetRed()); CScaleformMgr::AddParamInt(headerColour.GetGreen()); CScaleformMgr::AddParamInt(headerColour.GetBlue()); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_COLUMN_TITLE")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamString(TheText.Get("CMRC_CATEGS")); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_COLUMN_TITLE")) { CScaleformMgr::AddParamInt(1); CScaleformMgr::AddParamString(TheText.Get("CMRC_CONTENT")); CScaleformMgr::EndMethod(); } #if __PPU if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHOW_PLAYSTATION_LOGO")) { CScaleformMgr::AddParamBool(true); CScaleformMgr::EndMethod(); } #endif const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } //Build the category list const rage::cCommerceItemData* pTopLevelItemData = NULL; pTopLevelItemData = pCommerceUtil->GetItemData( cStoreScreenMgr::GetBaseCategory(), cCommerceItemData::ITEM_TYPE_CATEGORY ); const cCommerceCategoryData* pTopLevelCategoryData = NULL; if(pTopLevelItemData && pTopLevelItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY) { pTopLevelCategoryData = static_cast(pTopLevelItemData); } if (pTopLevelCategoryData==NULL) { //Handle the case where a top level category is not found for whatever reason pTopLevelItemData = pCommerceUtil->GetItemData( "TOP_DEFAULT", cCommerceItemData::ITEM_TYPE_CATEGORY ); if(pTopLevelItemData && pTopLevelItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY) { pTopLevelCategoryData = static_cast(pTopLevelItemData); } } int numberOfCategories = 0; bool customTopLevelCategory = false; if(pTopLevelCategoryData) { for(int i= 0; i < pTopLevelCategoryData->GetNumProducts(); i++) { int itemIndex = pTopLevelCategoryData->GetItemIndex(i); const cCommerceItemData* pCatItemData = pCommerceUtil->GetItemData(itemIndex); if (pCatItemData) { //Check if we are a category or an item. if (pCatItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY && (static_cast(pCatItemData))->GetNumProducts() > 0 ) { m_CategoryIndexArray.PushAndGrow(itemIndex); //Category, add to appropriate column if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { atString tempString = pCatItemData->GetName(); storeUIDebugf3("Setting the string name to %s",tempString.c_str()); CScaleformMgr::AddParamInt(CAT_COLUMN_INDEX); CScaleformMgr::AddParamInt(numberOfCategories); CScaleformMgr::AddParamString(tempString.c_str()); CScaleformMgr::EndMethod(); numberOfCategories++; } } } } } int initialCategoryIndex = INVALID_ITEM_INDEX; int currentCatagory = m_CurrentlySelectedCategory; if ( m_CurrentlySelectedCategory == INVALID_ITEM_INDEX ) { //We are in the initial state of the store. Display the first category found. if ( m_CategoryIndexArray.GetCount() == 0 ) { //Check if we are looking at an over-ridden base category and see if that category is valid if (pTopLevelCategoryData ) { //Special case, a custom category with items but no sub category //Category, add to appropriate column if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { atString tempString = pTopLevelCategoryData->GetName(); storeUIDebugf3("Setting the string name to %s",tempString.c_str()); CScaleformMgr::AddParamInt(CAT_COLUMN_INDEX); CScaleformMgr::AddParamInt(numberOfCategories); CScaleformMgr::AddParamString(tempString.c_str()); CScaleformMgr::EndMethod(); numberOfCategories++; } //Find the ID of this category for (int i=0; i < pCommerceUtil->GetNumItems(); i++) { if ( pCommerceUtil->GetItemData(i)->GetId() == pTopLevelCategoryData->GetId() ) { m_CategoryIndexArray.PushAndGrow(i); break; } } customTopLevelCategory = true; } else { //No categories. Call function to setup no data notification here. return; } } else { storeUIDebugf1("UpdateStoreLists :: InitialItemId: %s, SubscriptionUpsellItemId: %s, SubscriptionUpsellTargetItemId: %s", cStoreScreenMgr::GetInitialItemID().c_str(), m_SubscriptionUpsellItemId.c_str(), m_SubscriptionUpsellTargetItemId.c_str()); // tracking whether we have an initial item or not const cCommerceItemData* pInitialItemData = nullptr; int validInitialCategoryIndex = INVALID_ITEM_INDEX; if ( cStoreScreenMgr::GetInitialItemID().GetLength() > 0 ) { pInitialItemData = pCommerceUtil->GetItemDataByItemId(cStoreScreenMgr::GetInitialItemID().c_str()); } // if we have a target for post-upsell, use that else if(m_SubscriptionUpsellTargetItemId.GetLength() > 0) { pInitialItemData = pCommerceUtil->GetItemDataByItemId(m_SubscriptionUpsellTargetItemId.c_str()); // our subscription changed, request new images m_PendingImageRequest = true; } // tracking whether we have an initial item or not if ( pInitialItemData ) { //We found our initial item. Now check if there is a category that it is a member of in our category list storeUIDebugf1("UpdateStoreLists :: Found InitialItem: %s", pInitialItemData->GetName().c_str()); for ( int iItemsCats = 0; iItemsCats < pInitialItemData->GetCategoryMemberships().GetCount(); iItemsCats++ ) { for ( int iValidCats = 0; iValidCats < m_CategoryIndexArray.GetCount(); iValidCats++ ) { if ( pInitialItemData->GetCategoryMemberships()[iItemsCats] == pCommerceUtil->GetItemData( m_CategoryIndexArray[iValidCats] )->GetId() ) { //We have a found a category which //a) the item belongs to //b) is in our base category list validInitialCategoryIndex = iValidCats; } } } } if (validInitialCategoryIndex == INVALID_ITEM_INDEX ) { currentCatagory = m_CategoryIndexArray[0]; } else { currentCatagory = m_CategoryIndexArray[validInitialCategoryIndex]; initialCategoryIndex = validInitialCategoryIndex; } } } else if ( m_CurrentlySelectedCategory < 0 || m_CurrentlySelectedCategory >= m_CategoryIndexArray.GetCount() ) { commerceErrorf("We have hit the commerce list safety fall through with a category list size of %d and an index of %d", m_CategoryIndexArray.GetCount(), m_CurrentlySelectedCategory); DoEmptyStoreCheck(); return; } else { //We have a valid slot. storeUIAssert(m_CurrentlySelectedCategory >= 0 && m_CurrentlySelectedCategory < m_CategoryIndexArray.GetCount() ); currentCatagory = m_CategoryIndexArray[ m_CurrentlySelectedCategory ]; } const cCommerceCategoryData* pCategoryData = NULL; if (customTopLevelCategory) { pCategoryData = pTopLevelCategoryData; } else { //Get the list of items in this category to display them. const rage::cCommerceItemData* pItemData = NULL; pItemData = pCommerceUtil->GetItemData( currentCatagory ); if(storeUIVerifyf( pItemData && pItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY, "pItem not retrieved") ) { pCategoryData = static_cast(pItemData); } } storeUIDebugf1("UpdateStoreLists :: Category data - NumItems: %d, Name: %s", pCategoryData->GetNumProducts(), pCategoryData->GetName().c_str()); //Clear the existing center data slot of items. if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT_EMPTY")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::EndMethod(); } //Clear the columns. for (s32 iClearCols=1; iClearCols < NUM_COLUMNS; iClearCols++) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT_EMPTY")) { CScaleformMgr::AddParamInt(iClearCols); CScaleformMgr::EndMethod(); } } //Clear the existing center data slot of items. if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_DATA_SLOT")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::EndMethod(); } int initialProductIndex = INVALID_ITEM_INDEX; int numberOfItems = 0; int numberOfProducts = 0; if(pCategoryData) { numberOfItems = pCategoryData->GetNumProducts(); for(int i= 0; i < numberOfItems; i++) { int itemIndex = pCategoryData->GetItemIndex(i); const cCommerceItemData* pCatItemData = pCommerceUtil->GetItemData(itemIndex); if (pCatItemData) { //Check if we are a category or an item. if (pCatItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY && (static_cast(pCatItemData))->GetNumProducts() > 0 ) { m_CategoryIndexArray.PushAndGrow(itemIndex); //Category, add to appropriate column if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { atString tempString = pCatItemData->GetName(); storeUIDebugf3("Setting the string name to %s",tempString.c_str()); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(numberOfCategories); CScaleformMgr::AddParamString(tempString.c_str()); CScaleformMgr::EndMethod(); numberOfCategories++; } } if (pCatItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT) { const cCommerceProductData *pProductData = static_cast(pCatItemData); const bool showProduct = (pCommerceUtil->HasProductDetails(pProductData) || pCommerceUtil->ShouldAllowWithoutProduct(pProductData)) && !pCommerceUtil->ShouldHideProduct(pProductData); storeUIDebugf1("UpdateStoreLists :: %s: %s - %s, HasProduct: %s, AllowWithoutProduct: %s, Hide: %s", showProduct ? "Adding" : "Skipping", pProductData ? pProductData->GetId().c_str() : "[InvalidProduct]", pProductData ? pProductData->GetName().c_str() : "[InvalidProduct]", pCommerceUtil->HasProductDetails(pProductData) ? "True" : "False", pCommerceUtil->ShouldAllowWithoutProduct(pProductData) ? "True" : "False", pCommerceUtil->ShouldHideProduct(pProductData) ? "True" : "False"); // exit if we can't show this product if(!showProduct) continue; m_ProductIndexArray.PushAndGrow(itemIndex); //Item, add to appropriate column if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::AddParamInt(numberOfProducts); //No string, so that the images start off as a spinner. CScaleformMgr::AddParamString(""); CScaleformMgr::EndMethod(); //Show the first product. if ( numberOfProducts == 0 ) { storeUIDebugf1("UpdateStoreLists :: Displaying First Item"); DisplayItemDetails( pProductData ); } if (pProductData->GetId() == cStoreScreenMgr::GetInitialItemID() || pProductData->GetId() == m_SubscriptionUpsellTargetItemId) { storeUIDebugf1("UpdateStoreLists :: Displaying Initial Item (Index: %d, Product: %s)", numberOfProducts, pProductData->GetName().c_str()); initialProductIndex = numberOfProducts; //Set the initial string to empty so this does not occur each time. atString emptyString(""); cStoreScreenMgr::SetInitialItemID(emptyString); //We want these details displayed on initial store entry DisplayItemDetails( pProductData ); } numberOfProducts++; } } } } } if ( numberOfProducts == 0 ) { //We have no products in this category, so clear the details column. ClearItemDetails(); } if ( initialCategoryIndex != INVALID_ITEM_INDEX ) { //We have a non zero initial category set if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "HIGHLIGHT_ITEM")) { CScaleformMgr::AddParamInt(CAT_COLUMN_INDEX); CScaleformMgr::AddParamInt(initialCategoryIndex); CScaleformMgr::EndMethod(); } m_LastItemIndex = initialCategoryIndex; } if ( initialProductIndex != INVALID_ITEM_INDEX ) { //We have found a requested initial product m_LastColumnIndex = ITEM_COLUMN_INDEX; if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "HIGHLIGHT_ITEM")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::AddParamInt(initialProductIndex); CScaleformMgr::EndMethod(); } m_LastCenterItemIndex = initialProductIndex; m_LastItemIndex = initialProductIndex; } else { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "HIGHLIGHT_COLUMN")) { CScaleformMgr::AddParamInt(CAT_COLUMN_INDEX); CScaleformMgr::EndMethod(); m_LastCenterItemIndex = 0; } } for (s32 iCols=0; iCols < NUM_COLUMNS; iCols++) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_DATA_SLOT")) { CScaleformMgr::AddParamInt(iCols); CScaleformMgr::EndMethod(); } } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_START_INDEX")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); m_CurrentStartReturnId = CScaleformMgr::EndMethodReturnValue(m_CurrentStartReturnId); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_END_INDEX")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); m_CurrentEndReturnId = CScaleformMgr::EndMethodReturnValue(m_CurrentEndReturnId); } //Check for empty store DoEmptyStoreCheck(); } void cStoreMainScreen::Update() { rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if ( pCommerceUtil == NULL ) { return; } if (!m_Initialised) { return; } #if RSG_NP if (CWarningScreen::IsActive()) { if (m_isCommerceLogoDisplaying) { g_rlNp.GetCommonDialog().HideCommerceLogo(); m_isCommerceLogoDisplaying = false; } } else { if (!m_isCommerceLogoDisplaying && m_shouldCommerceLogoDisplay) { m_isCommerceLogoDisplaying = g_rlNp.GetCommonDialog().ShowCommerceLogo(rlNpCommonDialog::COMMERCE_LOGO_POS_LEFT); } } #endif if (!UpdateWarningScreen()) { return; } if ( !UpdateNonCriticalWarningScreen() ) { return; } if (m_PendingListSetup && !CLiveManager::GetCommerceMgr().GetCommerceUtil()->IsInErrorState() ) { UpdateStoreLists(); m_PendingListSetup = false; } if (m_PendingLongDescriptionItem != NULL && pCommerceUtil->GetItemLongDescription(m_PendingLongDescriptionItem) != NULL) { DisplayItemDetails(m_PendingLongDescriptionItem); m_PendingLongDescriptionItem = NULL; } switch( m_CurrentState ) { case(STATE_INITIALISING): UpdateInitialising(); break; case(STATE_NEUTRAL): UpdateInput(); break; case(STATE_PENDING_INITIAL_DATA): UpdatePendingInitialData(); break; case(STATE_LOADING_ASSETS): UpdateLoadingAssets(); break; case(STATE_IN_CHECKOUT): UpdateCheckout(); break; default: break; } if ( m_ColumnReturnId != 0 ) { if (CScaleformMgr::IsReturnValueSet(m_ColumnReturnId)) { m_LastColumnIndex = CScaleformMgr::GetReturnValueInt(m_ColumnReturnId); m_ColumnReturnId = 0; } } if ( m_ItemReturnId != 0 ) { if (CScaleformMgr::IsReturnValueSet(m_ItemReturnId)) { m_LastItemIndex = CScaleformMgr::GetReturnValueInt(m_ItemReturnId); if ( m_LastItemIndex < 0 ) { commerceErrorf("m_LastItemIndex set to %d. Almost certainly bad.", m_LastItemIndex); } m_ItemReturnId = 0; } } bool imageUpdateRequired = false; if ( m_CurrentStartReturnId != 0 ) { if (CScaleformMgr::IsReturnValueSet(m_CurrentStartReturnId)) { m_LastStartIndex = CScaleformMgr::GetReturnValueInt(m_CurrentStartReturnId); m_CurrentStartReturnId = 0; if ( m_CurrentEndReturnId == 0 ) { imageUpdateRequired = true; } // if (m_LastStartIndex < 0 || m_LastStartIndex > m_ProductIndexArray.GetCount() ) // { // m_LastStartIndex = 0; // } } } if ( m_CurrentEndReturnId != 0 ) { if (CScaleformMgr::IsReturnValueSet(m_CurrentEndReturnId)) { m_LastEndIndex = CScaleformMgr::GetReturnValueInt(m_CurrentEndReturnId); m_CurrentEndReturnId = 0; if ( m_CurrentStartReturnId == 0 ) { imageUpdateRequired = true; } // if (m_LastEndIndex < 0 || m_LastEndIndex > m_ProductIndexArray.GetCount() ) // { // m_LastEndIndex = 0; // } } } //We have our new start and end IDs after a move, sort out the images if (imageUpdateRequired || m_PendingImageRequest) { m_PendingImageRequest = false; PopulateImageRequests(); } UpdateImageRequests(); if ( pCommerceUtil->IsCategoryInfoPopulated() && m_CurrentState == STATE_NEUTRAL && CBusySpinner::IsOn() ) { CBusySpinner::Off( SPINNER_SOURCE_STORE_LOADING ); } if ( pCommerceUtil->IsContentInfoPopulated() && m_PendingHighlightChange && m_ItemReturnId == 0 && m_ColumnReturnId == 0 ) { ProcessHighlightChange(); m_PendingHighlightChange = false; UpdateContextHelp(); } UpdateLeaderBoardDataFetch(); //If we were in the checkout we need to detect when that is no longer the case and correctly update the product states if ( m_WasInCheckout && pCommerceUtil->IsInCheckout() == false && CLiveManager::IsOnline() && CLiveManager::IsOnlineRos() && pCommerceUtil->DidLastCheckoutSucceed() ) { storeUIDebugf1("Update :: Now out of store"); //SEND AN UPDATE TO THE CONTENT SYSTEM TO RESCAN HERE //Send the metric containing the completed checkout product here const int TEMP_PRICE_STRING_LEN = 128; char tempPriceString[TEMP_PRICE_STRING_LEN]; atString priceString; if (m_PendingCheckoutProduct != nullptr && pCommerceUtil->GetProductPrice(m_PendingCheckoutProduct, tempPriceString, TEMP_PRICE_STRING_LEN) >= 0.0f) { CNetworkTelemetry::AppendMetric(MetricIngameStoreCheckoutComplete(tempPriceString, (m_PendingCheckoutProduct ? m_PendingCheckoutProduct->GetPlatformId().c_str() : ""))); } // PS3 only xbox should process event for this #if __PS3 EXTRACONTENT.OnContentDownloadCompleted(); #endif // __PS3 #if NET_ENABLE_MEMBERSHIP_FUNCTIONALITY // refresh membership status - defaulted to true if(Tunables::IsInstantiated() && Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_MEMBERSHIP_REFRESH_ON_PURCHASED_MEMBERSHIP", 0x2be302ce), true)) { storeUIDebugf1("Update :: Requesting Membership Status"); rlScMembership::RequestMembershipStatus(NetworkInterface::GetLocalGamerIndex()); } // Add relevant subscription Ids if(rlScMembership::HasMembership(NetworkInterface::GetLocalGamerIndex())) { storeUIDebugf1("Update :: AddSubscriptionId: %s", rlScMembership::GetSubscriptionId()); CLiveManager::GetCommerceMgr().GetCommerceUtil()->AddSubscriptionId(rlScMembership::GetSubscriptionId()); } // disable membership notifications for script // don't reset the upsell item here - we need it to reinsert ourselves in the selection window if(!m_SubscriptionUpsellItemId.empty()) { storeUIDebugf1("Update :: Upsell Path, suppressing membership"); CLiveManager::SetSuppressMembershipForScript(true); // we also want to show the welcome here m_PendingNonCriticalWarningScreen = STORE_SUBSCRIPTION_WELCOME; CLiveManager::SetHasShownMembershipWelcome(); } #endif pCommerceUtil->DoDataRequest(false); m_RefetchingPlatformData = true; } if ( m_RefetchingPlatformData == true && !pCommerceUtil->IsInDataFetchState() && pCommerceUtil->IsContentInfoPopulated() ) { m_RefetchingPlatformData = false; //ClearLoadedImages(); //m_PendingListSetup = true; TriggerHighlightChange(); UpdateContextHelp(); //Check to see if a previously requested product is now owned, increment the money spent stat if so. if ( m_PurchaseCheckProductID.GetLength() > 0 ) { rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil != NULL ) { const cCommerceItemData *purchasedItem = pCommerceUtil->GetItemData(m_PurchaseCheckProductID); if ( purchasedItem && purchasedItem->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { const cCommerceProductData *pProductData = static_cast(purchasedItem); //Check to see if the item is now owned if ( pProductData->IsConsumable() ) { //Its a consumable. Store its inventory level m_ConsumableProductCheckId = m_PurchaseCheckProductID; m_ConsumableProductCheckLevel = CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel(pProductData->GetConsumableId()); //then refetch data to compare. CLiveManager::GetCommerceMgr().GetConsumableManager()->SetOwnershipDataDirty(); //Pause auto consume to avoid potential timing issues CLiveManager::GetCommerceMgr().SetAutoConsumePaused(true); } else if ( pCommerceUtil->IsProductPurchased(pProductData) ) { //Really not appropriate, due to currency differences //AddProductsPriceToCashSpentStat(pProductData); } else { StatsInterface::IncrementStat(STAT_MPPLY_STORE_CHECKOUTS_CANCELLED, 1); } } } m_PurchaseCheckProductID.Reset(); } else { //Probably a code entry. Rescan consumables in case it was a cash entry. CLiveManager::GetCommerceMgr().GetConsumableManager()->SetOwnershipDataDirty(); } } //Do we have a pending consumables level check and valid consumables data since the last refresh? if ( m_ConsumableProductCheckId.GetLength() > 0 && CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataPopulated() ) { //Yes, compare the ownership level rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil != NULL ) { const cCommerceItemData *purchasedItem = pCommerceUtil->GetItemData(m_ConsumableProductCheckId); if ( purchasedItem && purchasedItem->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { const cCommerceProductData *pProductData = static_cast(purchasedItem); if (CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel(pProductData->GetConsumableId()) > m_ConsumableProductCheckLevel) { AddProductsPriceToCashSpentStat(pProductData); } else { StatsInterface::IncrementStat(STAT_MPPLY_STORE_CHECKOUTS_CANCELLED, 1); } } } m_ConsumableProductCheckId.Reset(); } if ( m_WasConsumableDataDirtyLastFrame && CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataPopulated() ) { m_WasConsumableDataDirtyLastFrame = false; UpdateChangeInConsumableLevelCheck(); UpdatePendingCash(); } UpdateTopOfScreenTextInfo(); if ( CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataDirty() ) { m_WasConsumableDataDirtyLastFrame = true; } if ( pCommerceUtil->IsContentInfoPopulated() && m_PendingSelection ) { ProcessSelection(); m_PendingSelection = false; UpdateContextHelp(); } CBusySpinner::Update(); m_WasInCheckout = pCommerceUtil->IsInCheckout(); } void cStoreMainScreen::Render() { GRC_ALLOC_SCOPE_AUTO_PUSH_POP(); float fTimeStep = fwTimer::GetSystemTimeStep(); if (m_StoreBgMovieId >= 0) { CScaleformMgr::RenderMovie( m_StoreBgMovieId, fTimeStep, true ); } if (m_CurrentState == STATE_LOADING_ASSETS || m_CurrentState == STATE_INITIALISING || m_CurrentState == STATE_PENDING_INITIAL_DATA) { CPauseMenu::RenderAnimatedSpinner(-1.0f, true, false); // fix for 1495684 } if ( /*m_CurrentState == STATE_LOADING_ASSETS ||*/ m_CurrentState == STATE_PENDING_INITIAL_DATA && !cStoreScreenMgr::IsExitRequested() ) { //char cHtmlFormattedBusyString[256]; //char* html = CTextConversion::TextToHtml(TheText.Get("STORE_LOADING"),cHtmlFormattedBusyString,sizeof(cHtmlFormattedBusyString)); //CBusySpinner::On( html, SPINNER_ICON_LOADING, SPINNER_SOURCE_STORE_LOADING ); //CPauseMenu::RenderAnimatedSpinner(-1.0f,true); } if (!m_Initialised) { return; } #if RSG_PC // hide instructional buttons if SCUI is showing ...hints are confusing if (!g_rlPc.IsUiShowing()) #endif { if ( m_ButtonMovieId >= 0) { CScaleformMgr::RenderMovie( m_ButtonMovieId, fTimeStep, true ); } else { int buttonMovieIndex = CScaleformMgr::FindMovieByFilename(BUTTONMENU_FILENAME); if ( buttonMovieIndex >= 0 ) { CScaleformMgr::RenderMovie( buttonMovieIndex, fTimeStep, true ); } } } if ( m_StoreMovieId >= 0) { CScaleformMgr::RenderMovie( m_StoreMovieId, fTimeStep, true ); } if (CNewHud::GetHudComponentMovieId(NEW_HUD_SAVING_GAME) >= 0) { CScaleformMgr::RenderMovie( CNewHud::GetHudComponentMovieId(NEW_HUD_SAVING_GAME), fTimeStep, true ); } if ( m_BlackFadeMovieId >= 0 ) { CScaleformMgr::RenderMovie( m_BlackFadeMovieId, fTimeStep, true ); } } void cStoreMainScreen::UpdateInitialising() { //They really, really should be if we are in here. storeUIAssert(AreAllAssetsActive()); if (AreAllAssetsActive()) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "MENU_STATE")) { s32 menuStateParam = 1; //1 is the value we pass for the store screen. CScaleformMgr::AddParamInt(menuStateParam); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_CONTENTIMAGE_SIZE")) { CScaleformMgr::AddParamInt( static_cast(pow(2.0f, cStoreScreenMgr::GetStoreItemNumPerColumnExp() ) )); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_SHOP_LOGO")) { CScaleformMgr::AddParamInt(cStoreScreenMgr::GetBranding()); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHOW_SHOP_LOGO")) { CScaleformMgr::AddParamBool( cStoreScreenMgr::GetBranding() != 0 ); CScaleformMgr::EndMethod(); } UpdateTopOfScreenTextInfo(); // if (m_PedHeadShotHandle != 0 && CScaleformMgr::BeginMethod( m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_CHAR_IMG")) // { // const char* pedHeadShotTextureName = PEDHEADSHOTMANAGER.GetTextureName((s32)m_PedHeadShotHandle); // CScaleformMgr::AddParamString( pedHeadShotTextureName, false); // CScaleformMgr::AddParamString( pedHeadShotTextureName, false); // bool // CScaleformMgr::AddParamBool( pedHeadShotTextureName[0] != '\0'); // fixes 1212873 // CScaleformMgr::EndMethod(); // } } else { storeUIErrorf("Assets were not loaded upon reaching the store menu. Extreme badness alert."); return; } m_CurrentState = STATE_NEUTRAL; m_Initialised = true; CLiveManager::GetCommerceMgr().OnEnterUIStore(); if ( !m_TimerSinceInputEnabled.IsStarted() ) { m_TimerSinceInputEnabled.Start(); } UpdateContextHelp(); //Take the loading spinner off screen //CBusySpinner::Off( SPINNER_SOURCE_STORE_LOADING ); } bool cStoreMainScreen::AreAllAssetsActive() { bool retVal = ( m_StoreMovieId != INVALID_MOVIE_ID && CScaleformMgr::IsMovieActive(m_StoreMovieId) && m_StoreBgMovieId != INVALID_MOVIE_ID && CScaleformMgr::IsMovieActive(m_StoreBgMovieId) && m_BlackFadeMovieId != INVALID_MOVIE_ID); if ( m_ButtonMovieId != INVALID_MOVIE_ID && !CScaleformMgr::IsMovieActive(m_ButtonMovieId) ) { retVal = false; } return retVal; } void cStoreMainScreen::CheckIncomingFunctions( atHashWithStringBank methodName, const GFxValue* /*args*/ ) { if (!m_Initialised) { return; } if (methodName == ATSTRINGHASH("LOAD_PAUSE_MENU_STORE_CONTENT",0x879bc589)) { storeUIDebugf3("Store Advert screen callback recieved by main menu.\n"); if (!DoQuickCheckout()) { m_PendingListSetup = true; #if RSG_NP m_isCommerceLogoDisplaying = g_rlNp.GetCommonDialog().ShowCommerceLogo(rlNpCommonDialog::COMMERCE_LOGO_POS_LEFT); m_shouldCommerceLogoDisplay = true; #endif } return; } if ( methodName == ATSTRINGHASH("FADE_TO_BLACK_COMPLETED",0x8d27a5a7) ) { if ( m_CurrentState == STATE_STORE_FADE_TO_BLACK ) { rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if (m_PendingCheckoutProduct != NULL) { pCommerceUtil->CheckoutProduct(m_PendingCheckoutProduct); } else { pCommerceUtil->ProductCodeEntry(); } m_HasBeenInCheckout = true; m_CurrentState = STATE_IN_CHECKOUT; // NOTE: good alternative position to HideCommerceLogo at end of fade, rather than start. 50/50 what looks nicer. } } if ( methodName == ATSTRINGHASH("FADE_TO_TRANSPARENT_COMPLETED",0x351af647) ) { if ( m_CurrentState == STATE_STORE_FADE_TO_TRANS ) { m_CurrentState = STATE_NEUTRAL; } } } void cStoreMainScreen::UpdateInput() { if (m_ColumnReturnId != 0 || m_ItemReturnId != 0) // fix for 1912443 - dont request multiple return value requests { return; } if (!AreAllAssetsActive()) { return; } if (cStoreScreenMgr::IsExitRequested()) { return; } if ( m_InNoProductsMode || !m_StoreListsSetup ) { //This input update allows the user access to exit and code entry. UpdateNoContentInput(); return; } bool highlightChangeRequired = false; bool currentSlotRangeCheckRequired = false; int axisR = static_cast(CControlMgr::GetMainFrontendControl().GetFrontendRStickUpDown().GetNorm() * 128.0f); if ( RIGHT_STICK_DEADZONE < Abs(axisR) ) { if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_ANALOG_STICK_INPUT") ) { CScaleformMgr::AddParamBool(false); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(axisR); CScaleformMgr::EndMethod(); } } if ( m_CurrentStartReturnId == 0 && CPauseMenu::CheckInput(FRONTEND_INPUT_UP,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) ) { if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT") ) { CScaleformMgr::AddParamInt(DPADUP); CScaleformMgr::EndMethod(); } if ( m_LastColumnIndex == ITEM_COLUMN_INDEX ) { currentSlotRangeCheckRequired = true; m_LastSelectionTransition = ITEM_TO_ITEM; } else if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { m_LastSelectionTransition = CAT_TO_CAT; } else { AssertMsg(false,"invalid column index"); } highlightChangeRequired = true; } if ( m_CurrentStartReturnId == 0 && CPauseMenu::CheckInput(FRONTEND_INPUT_DOWN,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT")) { CScaleformMgr::AddParamInt(DPADDOWN); CScaleformMgr::EndMethod(); } if ( m_LastColumnIndex == ITEM_COLUMN_INDEX ) { if (m_LastItemIndex < m_ProductIndexArray.GetCount()) { currentSlotRangeCheckRequired = true; } m_LastSelectionTransition = ITEM_TO_ITEM; } else if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { m_LastSelectionTransition = CAT_TO_CAT; } else { AssertMsg(false,"invalid column index"); } highlightChangeRequired = true; } if ( CPauseMenu::CheckInput(FRONTEND_INPUT_BACK,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) && CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT_CIRCLE")) { CScaleformMgr::EndMethod(); if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } storeUIDebugf1("UpdateInput :: User requested exit"); cStoreScreenMgr::RequestExit(); CPauseMenu::PlaySound("BACK"); } else { //We are on the item column. if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT") ) { CScaleformMgr::AddParamInt(DPADLEFT); CScaleformMgr::EndMethod(); } m_LastSelectionTransition = ITEM_TO_CAT; highlightChangeRequired = true; } } if ( !m_InNoProductsMode && CPauseMenu::CheckInput(FRONTEND_INPUT_ACCEPT,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) && !cStoreScreenMgr::IsExitRequested() ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT_CROSS")) { CScaleformMgr::EndMethod(); } if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { m_LastSelectionTransition = CAT_TO_ITEM; //Set the selected item to be the top if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "HIGHLIGHT_ITEM")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::AddParamInt(0); CScaleformMgr::EndMethod(); } highlightChangeRequired = true; } else { //We are on an item m_PendingSelection = true; } } if (CPauseMenu::CheckInput(FRONTEND_INPUT_Y,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) && !cStoreScreenMgr::IsExitRequested() ) { // rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); // // if (pCommerceUtil) // { // pCommerceUtil->ProductCodeEntry(); // } //Start the fade down if (m_BlackFadeMovieId != INVALID_MOVIE_ID && CScaleformMgr::BeginMethod( m_BlackFadeMovieId, SF_BASE_CLASS_STORE, "FADE_TO_BLACK" )) { CScaleformMgr::AddParamInt(STORE_FADE_TIME); CScaleformMgr::EndMethod(); } m_PendingCheckoutProduct = NULL; m_CurrentState = STATE_STORE_FADE_TO_BLACK; if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT_TRIANGLE")) { CScaleformMgr::EndMethod(); } CPauseMenu::PlaySound("SELECT"); #if RSG_NP g_rlNp.GetCommonDialog().HideCommerceLogo(); m_shouldCommerceLogoDisplay = false; m_isCommerceLogoDisplaying = false; #endif } if (highlightChangeRequired) { TriggerHighlightChange(); } if (currentSlotRangeCheckRequired) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_START_INDEX")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); m_CurrentStartReturnId = CScaleformMgr::EndMethodReturnValue(m_CurrentStartReturnId); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_END_INDEX")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); m_CurrentEndReturnId = CScaleformMgr::EndMethodReturnValue(m_CurrentEndReturnId); } } } void cStoreMainScreen::UpdateNoContentInput() { if ( CPauseMenu::CheckInput(FRONTEND_INPUT_BACK,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) && CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT_CIRCLE")) { CScaleformMgr::EndMethod(); if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } storeUIDebugf1("UpdateNoContentInput :: User requested exit"); cStoreScreenMgr::RequestExit(); } } if (CPauseMenu::CheckInput(FRONTEND_INPUT_Y,false,CHECK_INPUT_OVERRIDE_FLAG_STORAGE_DEVICE) && !cStoreScreenMgr::IsExitRequested() ) { //Start the fade down if (m_BlackFadeMovieId != INVALID_MOVIE_ID && CScaleformMgr::BeginMethod( m_BlackFadeMovieId, SF_BASE_CLASS_STORE, "FADE_TO_BLACK" )) { CScaleformMgr::AddParamInt(STORE_FADE_TIME); CScaleformMgr::EndMethod(); } m_PendingCheckoutProduct = NULL; m_CurrentState = STATE_STORE_FADE_TO_BLACK; if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT_TRIANGLE")) { CScaleformMgr::EndMethod(); } CPauseMenu::PlaySound("SELECT"); } } void cStoreMainScreen::ProcessSelection() { storeUIDebugf3("Selected column %d selection %d",m_LastColumnIndex,m_LastItemIndex); rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } if ( m_LastColumnIndex == DETAIL_COLUMN_INDEX || m_LastColumnIndex == ITEM_COLUMN_INDEX ) { if ( m_LastCenterItemIndex == INVALID_ITEM_INDEX || m_LastCenterItemIndex >= m_ProductIndexArray.GetCount() ) { return; } const cCommerceItemData* pItemData = pCommerceUtil->GetItemData(m_ProductIndexArray[ m_LastCenterItemIndex ]); if ( pItemData != NULL && pItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { const cCommerceProductData *pProductData = static_cast(pItemData); ProcessCheckoutSelected(pProductData); } } else { //We are on the category column, so fire a dpad right message if ( CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_INPUT_EVENT")) { CScaleformMgr::AddParamInt(DPADRIGHT); CScaleformMgr::EndMethod(); TriggerHighlightChange(); } } } void cStoreMainScreen::ProcessHighlightChange() { storeUIDebugf3("ProcessHighlightChange :: Column: %d, Selection: %d", m_LastColumnIndex, m_LastItemIndex); if ( m_LastColumnIndex == ITEM_COLUMN_INDEX ) { //Store the item index for later m_LastCenterItemIndex = m_LastItemIndex; if ( m_LastItemIndex == INVALID_ITEM_INDEX || m_LastItemIndex >= m_ProductIndexArray.GetCount() ) { return; } const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } const cCommerceItemData* pItemData = pCommerceUtil->GetItemData(m_ProductIndexArray[ m_LastItemIndex ]); if ( pItemData != NULL && pItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { const cCommerceProductData *pProductData = static_cast(pItemData); DisplayItemDetails( pProductData ); } //Set slots with no loaded texture to display a blank. for ( s32 iSlots = 0; iSlots < (Min(m_ProductIndexArray.GetCount(), MAX_NUM_ITEMS_IN_MIDDLE_COLUMN) ); iSlots++ ) { int requestedSlotIndex = (m_LastStartIndex + iSlots) % m_ProductIndexArray.GetCount(); if ( m_LoadedImageIndices.Find( requestedSlotIndex ) == -1 ) { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if ( pCommerceUtil ) { const cCommerceItemData *itemData = pCommerceUtil->GetItemData( m_ProductIndexArray[ requestedSlotIndex ] ); PopulateItemSlotWithData((m_LastStartIndex + iSlots) % m_ProductIndexArray.GetCount(), itemData, false); } } } } if ( m_LastColumnIndex == CAT_COLUMN_INDEX) { if ( m_LastItemIndex == INVALID_ITEM_INDEX || m_LastItemIndex >= m_CategoryIndexArray.GetCount() || m_CategoryIndexArray.GetCount() == 1 ) { return; } m_CurrentlySelectedCategory = m_LastItemIndex; if ( m_LastSelectionTransition != ITEM_TO_CAT ) { ClearLoadedImages(); m_PendingListSetup = true; } } } void cStoreMainScreen::DisplayItemDetails( const cCommerceProductData* pProductData ) { rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } storeUIAssert( pProductData != NULL ); if ( pProductData == NULL ) { return; } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT_EMPTY")) { //Plus one because this function seems to count the columns up from one. Charming. CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "ADD_COLUMN")) { //Plus one because this function seems to count the columns up from one. Charming. CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamString("DetailsList"); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_COLUMN_TITLE")) { CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamString(TheText.Get("CMRC_DETAILS")); CScaleformMgr::EndMethod(); } char cashAmountString[CONSUMABLE_AMOUNT_STR_LEN]; cashAmountString[0] = 0; if ( pProductData->IsConsumable() && MoneyInterface::GetCashPackValue(pProductData->GetConsumableId() ) != -1 ) { CTextConversion::FormatIntForHumans(MoneyInterface::GetCashPackValue(pProductData->GetConsumableId()), cashAmountString, CONSUMABLE_AMOUNT_STR_LEN, "%s"); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { atString tempString = pProductData->GetName(); if ( pProductData->IsConsumable() && MoneyInterface::GetCashPackValue(pProductData->GetConsumableId() ) != -1 ) { tempString.Replace(CONSUMABLE_AMOUNT_SUB_TAG, cashAmountString); } CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamString(tempString.c_str()); CScaleformMgr::EndMethod(); } if ( pCommerceUtil->GetItemLongDescription(pProductData) != NULL ) { atString longDescString(pCommerceUtil->GetItemLongDescription(pProductData)); if ( pProductData->IsConsumable() && MoneyInterface::GetCashPackValue(pProductData->GetConsumableId() ) != -1 ) { longDescString.Replace(CONSUMABLE_AMOUNT_SUB_TAG, cashAmountString); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamInt(1); CScaleformMgr::AddParamString(longDescString); CScaleformMgr::EndMethod(); } //We have our long description. m_PendingLongDescriptionItem = NULL; } else { m_PendingLongDescriptionItem = pProductData; } if ( pProductData->GetEnumeratedFlag() ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DESCRIPTION")) { const int TEMP_PRICE_STRING_LEN = 128; char tempPriceString[TEMP_PRICE_STRING_LEN]; atString priceString; if ( pCommerceUtil->GetProductPrice(pProductData,tempPriceString,TEMP_PRICE_STRING_LEN) != 0.0f) { priceString = tempPriceString; priceString += PRICE_STRING_SUFFIX; } //char* string = TheText.Get("MKT_AIM_SPD"); char* statusString = NULL; bool showPrice = true; if ( pCommerceUtil->IsProductInstalled( pProductData ) ) { statusString = TheText.Get("CMRC_INSTALLED"); showPrice = false; } else if ( pCommerceUtil->IsProductPurchased( pProductData ) && !pProductData->IsConsumable() ) { statusString = TheText.Get("CMRC_PRCHSD"); showPrice = false; } else { statusString = TheText.Get("CMRC_AVAIL"); } if ( pProductData->IsConsumable() ) { showPrice = true; } //Check if we have the data for how many of our friends own this product const int *pNumFriendsWhoOwnProduct = m_NumFriendsWhoOwnProductMap.Access(pProductData->GetId() ); if ( pNumFriendsWhoOwnProduct == NULL ) { //We dont have the data for this product. Set it as our next request. mp_PendingNumFriendsWhoOwnProduct = pProductData; } else { //Clear a pending request. mp_PendingNumFriendsWhoOwnProduct = NULL; } if (showPrice) { CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamString(TheText.Get("CMRC_PRICE")); if ( priceString.GetLength() == 0 ) { CScaleformMgr::AddParamString(TheText.Get("STORE_FREE_PRICE")); } else { CScaleformMgr::AddParamString(priceString.c_str()); } } else { CScaleformMgr::AddParamInt(2); priceString.Clear(); CScaleformMgr::AddParamString(priceString.c_str()); CScaleformMgr::AddParamString(priceString.c_str()); } if ( pNumFriendsWhoOwnProduct ) { CScaleformMgr::AddParamInt(*pNumFriendsWhoOwnProduct); } else { CScaleformMgr::AddParamInt(0); } CScaleformMgr::AddParamString(statusString); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamBool(true); CScaleformMgr::AddParamBool(pNumFriendsWhoOwnProduct != NULL); CScaleformMgr::AddParamBool(true); CScaleformMgr::EndMethod(); } } else { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DESCRIPTION")) { //char* string = TheText.Get("MKT_AIM_SPD"); CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamString(TheText.Get("CMRC_PRICE")); CScaleformMgr::AddParamString(""); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamString(TheText.Get("CMRC_AVAIL")); CScaleformMgr::AddParamInt(15); CScaleformMgr::AddParamBool(false); CScaleformMgr::AddParamBool(false); CScaleformMgr::AddParamBool(false); CScaleformMgr::EndMethod(); } } //Setup the status colour if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_STATUS_COLOURS")) { #if __XENON CRGBA bgColour = CHudColour::GetRGBA(HUD_COLOUR_PLATFORM_GREEN); CRGBA fgColour = CHudColour::GetRGBA(HUD_COLOUR_BLACK); #else CRGBA bgColour = CHudColour::GetRGBA(HUD_COLOUR_PLATFORM_BLUE); CRGBA fgColour = CHudColour::GetRGBA(HUD_COLOUR_WHITE); #endif CScaleformMgr::AddParamInt(bgColour.GetRed()); CScaleformMgr::AddParamInt(bgColour.GetGreen()); CScaleformMgr::AddParamInt(bgColour.GetBlue()); CScaleformMgr::AddParamInt(fgColour.GetRed()); CScaleformMgr::AddParamInt(fgColour.GetGreen()); CScaleformMgr::AddParamInt(fgColour.GetBlue()); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_DATA_SLOT")) { CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::EndMethod(); } } void cStoreMainScreen::ClearItemDetails( ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "REMOVE_COLUMN")) { //Plus one because this function seems to count the columns up from one. Charming. CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "ADD_COLUMN")) { //Plus one because this function seems to count the columns up from one. Charming. CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamString("DetailsList"); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_COLUMN_TITLE")) { CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamString(TheText.Get("CMRC_DETAILS")); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamString(""); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::AddParamInt(1); CScaleformMgr::AddParamString(""); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DESCRIPTION")) { CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamString(""); CScaleformMgr::AddParamString(""); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamString(""); CScaleformMgr::AddParamInt(15); CScaleformMgr::AddParamBool(false); CScaleformMgr::AddParamBool(false); CScaleformMgr::AddParamBool(false); CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_DATA_SLOT")) { CScaleformMgr::AddParamInt(DETAIL_COLUMN_INDEX); CScaleformMgr::EndMethod(); } } void cStoreMainScreen::UpdateContextHelp() { //Because of the various paths into the store, we do not always own the instructional button movie. int buttonMovieId = m_ButtonMovieId; if ( buttonMovieId == INVALID_MOVIE_ID ) { buttonMovieId = CScaleformMgr::FindMovieByFilename(BUTTONMENU_FILENAME); } //We couldn't get the movie ID from either source. Leave. if ( buttonMovieId == INVALID_MOVIE_ID ) { return; } if ( buttonMovieId >= 0 ) { if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT_EMPTY")) { CScaleformMgr::EndMethod(); } if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_CLEAR_SPACE")) { CScaleformMgr::AddParamInt(200); CScaleformMgr::EndMethod(); } if ( m_InNoProductsMode || cStoreScreenMgr::GetCheckoutInitialItem()) { //We don't want any additional buttons in this case. } else if ( m_LastColumnIndex == CAT_COLUMN_INDEX ) { if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(30); CScaleformMgr::AddParamString(TheText.Get("CMRC_SELECT")); CScaleformMgr::EndMethod(); } } else if ( m_LastCenterItemIndex != INVALID_ITEM_INDEX && m_LastCenterItemIndex < m_ProductIndexArray.GetCount() ) { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssertf( pCommerceUtil, "Commerce utility was not initialised or present" ); if ( pCommerceUtil == NULL ) { return; } const cCommerceItemData* pItemData = pCommerceUtil->GetItemData(m_ProductIndexArray[ m_LastCenterItemIndex ]); if ( pItemData != NULL && pItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { const cCommerceProductData *pProductData = static_cast(pItemData); if ( pCommerceUtil->IsProductInstalled( pProductData ) ) { if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(30); CScaleformMgr::AddParamString(TheText.Get("CMRC_DLOAD")); CScaleformMgr::EndMethod(); } } else if ( pCommerceUtil->IsProductPurchased( pProductData ) && !pProductData->IsConsumable() ) { if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(30); CScaleformMgr::AddParamString(TheText.Get("CMRC_DLOAD")); CScaleformMgr::EndMethod(); } } else if ( pCommerceUtil->IsProductPurchasable( pProductData ) ) { if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(30); CScaleformMgr::AddParamString(TheText.Get("CMRC_PURCHASE")); CScaleformMgr::EndMethod(); } } } } if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(1); CScaleformMgr::AddParamInt(31); CScaleformMgr::AddParamString(TheText.Get("CMRC_BACK")); CScaleformMgr::EndMethod(); } // Don't show the "Redeem Code"-button during a quick checkout if ( !cStoreScreenMgr::GetCheckoutInitialItem() && CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT") ) { CScaleformMgr::AddParamInt(2); CScaleformMgr::AddParamInt(33); CScaleformMgr::AddParamString(TheText.Get("CMRC_CODE")); CScaleformMgr::EndMethod(); } if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "DRAW_INSTRUCTIONAL_BUTTONS")) { CScaleformMgr::EndMethod(); } if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_BACKGROUND_COLOUR")) { CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(0); CScaleformMgr::AddParamInt(80); CScaleformMgr::EndMethod(); } } } void cStoreMainScreen::PopulateImageRequests() { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } int numRequiredImages = Min( MAX_NUM_ITEMS_IN_MIDDLE_COLUMN, m_ProductIndexArray.GetCount() ); //create an array of our currently required images atArray requiredImageArray; for ( int iRequiredImageIt = 0; iRequiredImageIt < numRequiredImages; iRequiredImageIt++ ) { int requiredImageIndex = (m_LastStartIndex + iRequiredImageIt ) % m_ProductIndexArray.GetCount(); requiredImageArray.PushAndGrow(requiredImageIndex); } //Check for unloads first. bool deletionOccured = false; do { deletionOccured = false; for (s32 iLoadedImageIndices = 0; iLoadedImageIndices < m_LoadedImageIndices.GetCount(); iLoadedImageIndices++) { if ( requiredImageArray.Find(m_LoadedImageIndices[ iLoadedImageIndices ]) == -1 ) { //We are outside the currently requested range, unload const cCommerceItemData *itemData = pCommerceUtil->GetItemData(m_ProductIndexArray[ m_LoadedImageIndices[ iLoadedImageIndices ] ] ); if ( itemData && itemData->GetImagePaths().GetCount() > 0 ) { const atString path(itemData->GetImagePaths()[0]); //DO NOT CHECK IN! cStoreScreenMgr::GetTextureManager()->ReleaseTexture( path ); PopulateItemSlotWithData(m_LoadedImageIndices[ iLoadedImageIndices ], itemData, false); } deletionOccured = true; m_LoadedImageIndices.Delete( iLoadedImageIndices ); break; } } } while (deletionOccured); //Now do the same check on the requested image array do { deletionOccured = false; for (s32 iRequestedImageIndices = 0; iRequestedImageIndices < m_RequestedImageIndices.GetCount(); iRequestedImageIndices++) { if ( requiredImageArray.Find(m_RequestedImageIndices[ iRequestedImageIndices ]) == -1 ) { //We are outside the currently requested range, remove from requests m_RequestedImageIndices.Delete( iRequestedImageIndices ); deletionOccured = true; break; } } } while (deletionOccured); for (int s = 0; s < requiredImageArray.GetCount() ; s++ ) { bool found = false; //Check if the image is already in our downloaded list if ( m_LoadedImageIndices.Find(requiredImageArray[s]) != -1 ) { found = true; } //Check if the image is already in our requested list if ( m_RequestedImageIndices.Find(requiredImageArray[s]) != -1 ) { found = true; } if (!found) { if ( m_RequestedImageIndices.GetCount() < m_RequestedImageIndices.GetCapacity() ) { //See if the product has an image to request. storeUIAssert( requiredImageArray[s] >= 0 && requiredImageArray[s] < m_ProductIndexArray.GetCount() ); const cCommerceItemData *itemData = pCommerceUtil->GetItemData( m_ProductIndexArray[ requiredImageArray[s] ] ); if ( itemData && itemData->GetImagePaths().GetCount() > 0 ) { m_RequestedImageIndices.PushAndGrow(requiredImageArray[s]); } } else { for (s32 i = 0; i < m_RequestedImageIndices.GetCount(); i++) { storeUIDebugf3("m_RequestedImageIndices[%d]: %d",i,m_RequestedImageIndices[i]); } storeUIDebugf3("Trying to add index %d",requiredImageArray[s]); storeUIAssertf(false,"Too many requests to download images in main store screen"); } } } } void cStoreMainScreen::UpdateImageRequests() { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } for (int iRequestedImageIter = 0; iRequestedImageIter < m_RequestedImageIndices.GetCount(); iRequestedImageIter++) { int requestedImageIndex = m_RequestedImageIndices[iRequestedImageIter]; storeUIAssert( requestedImageIndex >= 0 && requestedImageIndex < m_ProductIndexArray.GetCount() ); const cCommerceItemData *itemData = pCommerceUtil->GetItemData( m_ProductIndexArray[ requestedImageIndex ] ); if ( itemData && itemData->GetImagePaths().GetCount() > 0 && (m_LoadedImageIndices.Find( requestedImageIndex ) == -1) && cStoreScreenMgr::GetTextureManager()->RequestTexture( itemData->GetImagePaths()[0] ) ) { storeUIDebugf3("Requested texture %s is now available from DL Tex man. This is slot %d", itemData->GetImagePaths()[0].c_str(), requestedImageIndex ); //We have our texture. Populate the slot. PopulateItemSlotWithData( requestedImageIndex , itemData ); m_LoadedImageIndices.PushAndGrow(requestedImageIndex); } } //Pass to remove downloaded textures for ( int iLoadedImageIter = 0; iLoadedImageIter < m_LoadedImageIndices.GetCount(); iLoadedImageIter++ ) { int loadedImageIndex = m_LoadedImageIndices[ iLoadedImageIter ]; for (int iRequestedImageIter = 0; iRequestedImageIter < m_RequestedImageIndices.GetCount(); iRequestedImageIter++) { int requestedImageIndex = m_RequestedImageIndices[iRequestedImageIter]; if ( loadedImageIndex == requestedImageIndex ) { storeUIDebugf3("Removing slot %d from the requested images array", requestedImageIndex ); //We have downloaded this slots image. Remove from the request list m_RequestedImageIndices.Delete( iRequestedImageIter ); break; } } } } void cStoreMainScreen::PopulateItemSlotWithData( int a_Slot, const cCommerceItemData* a_ItemData, bool showImage ) { storeUIAssert(a_ItemData); if ( a_ItemData == NULL ) { return; } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SET_DATA_SLOT")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::AddParamInt(a_Slot); if ( a_ItemData->GetImagePaths().GetCount() > 0 && showImage ) { atString cloudStrippedPath = a_ItemData->GetImagePaths()[0]; cStoreTextureManager::ConvertPathToTextureName(cloudStrippedPath); CScaleformMgr::AddParamString( cloudStrippedPath ); } else { CScaleformMgr::AddParamString(""); } CScaleformMgr::EndMethod(); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_DATA_SLOT")) { CScaleformMgr::AddParamInt(ITEM_COLUMN_INDEX); CScaleformMgr::EndMethod(); } } void cStoreMainScreen::ClearLoadedImages() { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert( pCommerceUtil ); if ( pCommerceUtil == NULL ) { return; } for (s32 iLoadedImageIndices = 0; iLoadedImageIndices < m_LoadedImageIndices.GetCount(); iLoadedImageIndices++) { //We are outside the currently requested range, unload const cCommerceItemData *itemData = pCommerceUtil->GetItemData(m_LoadedImageIndices[iLoadedImageIndices] ); if ( itemData && itemData->GetImagePaths().GetCount() > 0 ) { const atString path(itemData->GetImagePaths()[0]); cStoreScreenMgr::GetTextureManager()->ReleaseTexture( path ); } } m_LoadedImageIndices.Reset(); } void cStoreMainScreen::ClearImageRequests() { storeUIAssert( cStoreScreenMgr::GetTextureManager() ); const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if ( cStoreScreenMgr::GetTextureManager() == NULL || pCommerceUtil == NULL ) { return; } for ( int i = 0; i < m_RequestedImageIndices.GetCount(); i++ ) { const cCommerceItemData *itemData = pCommerceUtil->GetItemData(m_ProductIndexArray[i]); if ( itemData && itemData->GetImagePaths().GetCount() > 0 ) { const atString path(itemData->GetImagePaths()[0]); cStoreScreenMgr::GetTextureManager()->CancelRequest( path ); } } m_RequestedImageIndices.Reset(); m_RequestedImageIndices.Reserve( MAX_REQUESTED_IMAGES ); } void cStoreMainScreen::TriggerHighlightChange() { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_COLUMN")) { m_ColumnReturnId = CScaleformMgr::EndMethodReturnValue(m_ColumnReturnId); } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "GET_CURRENT_SELECTION")) { m_ItemReturnId = CScaleformMgr::EndMethodReturnValue(m_ItemReturnId); } m_PendingHighlightChange = true; } void cStoreMainScreen::UpdatePendingInitialData() { if ( CLiveManager::GetCommerceMgr().HasValidData() ) { m_CurrentState = STATE_INITIALISING; } //Error checking should go here for failed commerce data } void cStoreMainScreen::RequestScaleformMovies() { int buttonMovieId = CScaleformMgr::FindMovieByFilename(BUTTONMENU_FILENAME); if ( buttonMovieId == -1 ) { m_ButtonMovieId = CScaleformMgr::CreateMovie(BUTTONMENU_FILENAME, Vector2(0,0), Vector2(1.0f, 1.0f)); CBusySpinner::RegisterInstructionalButtonMovie(m_ButtonMovieId); // register this "instructional button" movie with the spinner system } else { //The instructional movie is already loaded. Take this opportunity to empty it if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "SET_DATA_SLOT_EMPTY")) { CScaleformMgr::EndMethod(); } if ( CScaleformMgr::BeginMethod(buttonMovieId, SF_BASE_CLASS_GENERIC, "CLEAR_ALL")) { CScaleformMgr::EndMethod(); } } m_StoreBgMovieId = CScaleformMgr::CreateMovie(BACKGROUNDMOVIE_FILENAME, Vector2(0,0), Vector2(1.0f, 1.0f)); m_BlackFadeMovieId = CScaleformMgr::CreateMovie(BLACKFADE_FILENAME, Vector2(0.0f, 0.0f), Vector2(1.0f, 1.0f)); Vector2 pos(cStoreScreenMgr::GetMovieXPos(), cStoreScreenMgr::GetMovieYPos()); Vector2 size(cStoreScreenMgr::GetMovieWidth(), cStoreScreenMgr::GetMovieHeight()); m_StoreMovieId = CScaleformMgr::CreateMovie(STOREMENU_FILENAME, pos, size); // this movie has seperate values for 4:3 and 16:9 as it wont fit inside safezone of 4:3 m_CurrentState = STATE_LOADING_ASSETS; } void cStoreMainScreen::UpdateLoadingAssets() { if (AreAllAssetsActive()) { //DISPLAY THE "LOADING DATA" dialog here m_CurrentState = STATE_PENDING_INITIAL_DATA; } } void cStoreMainScreen::SetBgScrollSpeed( int speed ) { if ( m_StoreMovieId != INVALID_MOVIE_ID ) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "ANIMATE_BACKGROUND")) { CScaleformMgr::AddParamInt(speed); CScaleformMgr::EndMethod(); } } } void cStoreMainScreen::OnMembershipGained() { storeUIDebugf1("OnMembershipGained :: Flagging List Refresh"); m_PendingListSetup = true; } void cStoreMainScreen::AddProductsPriceToCashSpentStat( const cCommerceProductData* pProductData ) { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if (pCommerceUtil == NULL) { commerceAssertf(false, "AddProductsPriceToCashSpentStat :: Commerce util does not exist"); return; } char priceBuffer[SIZE_OF_PRICE_BUFFER]; float priceOfRequestedProduct = pCommerceUtil->GetProductPrice(pProductData,priceBuffer,SIZE_OF_PRICE_BUFFER); StatsInterface::IncrementStat(STAT_MPPLY_STORE_MONEY_SPENT, priceOfRequestedProduct); //THIS IS A VERY BAD NETWORK HEAVY FUNCTION! DO NOT CALL A LOT!!!!! StatsInterface::TryMultiplayerSave( STAT_SAVETYPE_STORE ); } bool cStoreMainScreen::UpdateWarningScreen() { if (m_WasInCheckout) { //Don't want to pull the rug out from under container functions. return true; } if (cStoreScreenMgr::IsExitRequested()) { return true; } if (sm_bPlayerHasJustSignedOut) { //We just leave for this now if (m_StoreMovieId != INVALID_MOVIE_ID && CScaleformMgr::IsMovieActive(m_StoreMovieId) && CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } if (m_CurrentDCWarningScreen != STORE_WARNING_NONE) CWarningScreen::Remove(); storeUIDebugf1("UpdateWarningScreen :: Requesting exit after sign out"); m_CurrentDCWarningScreen = STORE_WARNING_NONE; cStoreScreenMgr::RequestExit(); return false; } //Sign out messages don't come through immediately, so we need to delay the offline check briefly. if (!CLiveManager::IsOnline() || !CLiveManager::IsOnlineRos() ) { m_FramesOffline++; } else { m_FramesOffline = 0; } const rlGamerInfo* pGamerInfo = NetworkInterface::GetActiveGamerInfo(); if ( (m_FramesOffline > OFFLINE_FRAMES_TO_WAIT_FOR_SIGNOUT_MESSAGE) || CLiveManager::GetCommerceMgr().GetCommerceUtil()->IsInErrorState() || (pGamerInfo && pGamerInfo->IsOnline() && !rlGamerInfo::HasStorePrivileges(pGamerInfo->GetLocalIndex())) || m_CurrentDCWarningScreen != STORE_WARNING_NONE ) { CBusySpinner::Off( SPINNER_SOURCE_STORE_LOADING ); if ( STORE_WARNING_NONE == m_CurrentDCWarningScreen ) { //Since this is the first display, take this opportunity to show debug output identifying the cause. storeUIDisplayf("Displaying store DC message. Cause: CLiveManager::IsOnlineRos() [%d] CLiveManager::IsOnline() [%d], CLiveManager::GetCommerceMgr().GetCommerceUtil()->IsInErrorState() [%d], rlGamerInfo::HasStorePrivileges(pGamerInfo->GetLocalIndex() [%d]", CLiveManager::IsOnlineRos(), CLiveManager::IsOnline(), CLiveManager::GetCommerceMgr().GetCommerceUtil()->IsInErrorState(), rlGamerInfo::HasStorePrivileges(pGamerInfo->GetLocalIndex()) ); if ( CLiveManager::IsOnline() && pGamerInfo && !rlGamerInfo::HasStorePrivileges(pGamerInfo->GetLocalIndex()) ) { m_CurrentDCWarningScreen = STORE_WARNING_AGE; } else if (!CLiveManager::IsOnline() && m_WasSignedOnlineOnEnter) { m_CurrentDCWarningScreen = STORE_WARNING_PLATFORM_LOST; } else if (!CLiveManager::IsOnline()) { m_CurrentDCWarningScreen = STORE_WARNING_PLATFORM_OFFLINE; } else { m_CurrentDCWarningScreen = STORE_WARNING_ROS; } } atHashWithStringBank message; switch(m_CurrentDCWarningScreen) { case(STORE_WARNING_PLATFORM_OFFLINE): message = "STORE_PLAT_OFFLINE"; break; case(STORE_WARNING_PLATFORM_LOST): message = "STORE_PLAT_LOS"; break; case(STORE_WARNING_ROS): message = "STORE_UNAVAIL_ROS"; break; case(STORE_WARNING_AGE): message = "HUD_AGERES"; break; default: message = "STORE_UNAVAIL"; } CWarningScreen::SetMessage( WARNING_MESSAGE_STANDARD, message, FE_WARNING_OK); if(CWarningScreen::CheckAllInput() == FE_WARNING_OK) { if (m_StoreMovieId != INVALID_MOVIE_ID && CScaleformMgr::IsMovieActive(m_StoreMovieId) && CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } storeUIDebugf1("UpdateWarningScreen :: Requesting exit after error"); m_CurrentDCWarningScreen = STORE_WARNING_NONE; cStoreScreenMgr::RequestExit(); } return false; } // if we've received an invite, that's not confirmed and we are not suppressed by script // static bool simulateInvite = false; if ( ( CLiveManager::GetInviteMgr().HasPendingAcceptedInvite() && !CLiveManager::GetInviteMgr().HasConfirmedOrIsAutoConfirm() && !CLiveManager::GetInviteMgr().IsSuppressed())/* || simulateInvite */) { if (CBusySpinner::IsOn()) { CBusySpinner::Off( SPINNER_SOURCE_STORE_LOADING ); } CWarningScreen::SetMessage( WARNING_MESSAGE_STANDARD, CLiveManager::GetInviteMgr().GetConfirmText(), FE_WARNING_OK_CANCEL, false, -1, CLiveManager::GetInviteMgr().GetConfirmSubstring()); eWarningButtonFlags result = CWarningScreen::CheckAllInput(); if (result == FE_WARNING_OK) { if (m_StoreMovieId != INVALID_MOVIE_ID && CScaleformMgr::IsMovieActive(m_StoreMovieId) && CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } storeUIDebugf1("UpdateWarningScreen :: Requesting exit after invite"); cStoreScreenMgr::RequestExit(); //Since we have accepted an invite, we don't want to leave via the pause menu cStoreScreenMgr::SetReturnToPauseMenu(false); //Confirm invite CLiveManager::GetInviteMgr().AutoConfirmInvite(); // simulateInvite = false; } else if (result == FE_WARNING_CANCEL) { CLiveManager::GetInviteMgr().CancelInvite(); // simulateInvite = false; } return false; } return true; } bool cStoreMainScreen::UpdateNonCriticalWarningScreen() { if (cStoreScreenMgr::IsExitRequested()) { return true; } if ( m_CurrentDCWarningScreen != STORE_WARNING_NONE ) { return true; } //Structured like this so that we can easily add new messages later. if ( m_CurrentNonCriticalWarningScreen == STORE_NON_CRIT_WARNING_NONE ) { switch(m_PendingNonCriticalWarningScreen) { case STORE_NON_CRIT_WARNING_NONE: break; case STORE_CANNOT_PURCHASE_CASH: storeUIDisplayf("Setting m_CurrentNonCriticalWarningScreen to STORE_CANNOT_PURCHASE_CASH"); m_CurrentNonCriticalWarningScreen = STORE_CANNOT_PURCHASE_CASH; m_PendingNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; break; case STORE_SUBSCRIPTION_UPSELL: storeUIDisplayf("Setting m_CurrentNonCriticalWarningScreen to STORE_SUBSCRIPTION_UPSELL"); m_CurrentNonCriticalWarningScreen = STORE_SUBSCRIPTION_UPSELL; m_PendingNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; break; case STORE_SUBSCRIPTION_WELCOME: storeUIDisplayf("Setting m_CurrentNonCriticalWarningScreen to STORE_SUBSCRIPTION_WELCOME"); m_CurrentNonCriticalWarningScreen = STORE_SUBSCRIPTION_WELCOME; m_PendingNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; break; } } if ( m_CurrentNonCriticalWarningScreen != STORE_NON_CRIT_WARNING_NONE ) { atHashWithStringBank header; atHashWithStringBank message; eWarningButtonFlags buttons = FE_WARNING_OK; switch( m_CurrentNonCriticalWarningScreen ) { case(STORE_CANNOT_PURCHASE_CASH): header = 0u; message = "STORE_CASH_PRODS_DISABLED"; break; case(STORE_SUBSCRIPTION_UPSELL): header = "STORE_PS_SUBSCRIBE"; message = "STORE_PS_UPSELL"; buttons = FE_WARNING_YES_NO; break; case(STORE_SUBSCRIPTION_WELCOME): header = "ALERT_MEMBER_CONGRATS"; message = "ALERT_MEMBER_CONGRATS_MESSAGE"; break; default: storeUIAssertf(false,"Non implemented non critical message."); } CWarningScreen::SetMessageWithHeader( WARNING_MESSAGE_STANDARD, header, message, buttons); const eWarningButtonFlags buttonPressed = CWarningScreen::CheckAllInput(); if(buttonPressed == FE_WARNING_OK || buttonPressed == FE_WARNING_YES) { switch (m_CurrentNonCriticalWarningScreen) { case(STORE_CANNOT_PURCHASE_CASH): break; case(STORE_SUBSCRIPTION_UPSELL): if(storeUIVerifyf(!m_SubscriptionUpsellItemId.empty(), "Invalid Upsell Item!")) { cCommerceUtil* pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); const cCommerceItemData* pUpsellItem = pCommerceUtil->GetItemDataByItemId(m_SubscriptionUpsellItemId.c_str()); if(pUpsellItem && pUpsellItem->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT) { const cCommerceProductData* pUpsellProduct = static_cast(pUpsellItem); // get upsells const atArray& subscriptionUpsells = pUpsellProduct->GetSubscriptionUpsells(); const unsigned numUpsells = subscriptionUpsells.GetCount(); // need to find our subscription product const unsigned numItems = pCommerceUtil->GetNumItems(); for(unsigned i = 0; i < numItems; i++) { const cCommerceItemData* pItemData = pCommerceUtil->GetItemData(i); if(pItemData == nullptr) continue; if(pItemData->GetItemType() != cCommerceItemData::ITEM_TYPE_PRODUCT) continue; const cCommerceProductData* pOtherProductData = static_cast(pItemData); if(!pOtherProductData->IsConsumable()) continue; // check for our upsell in the list for(int d = 0; d < numUpsells; d++) { if(subscriptionUpsells[d] == pOtherProductData->GetConsumableId()) { // found our subscription upsell storeUIDebugf1("ProcessCheckoutSelected :: SelectedProduct: %s, UpsellProduct: %s, SubscriptionId: %s", pUpsellProduct->GetName().c_str(), pOtherProductData->GetName().c_str(), subscriptionUpsells[d].c_str()); // this starts a fade before the checkout begins StartCheckout(pOtherProductData); // suppress membership updates for script from here MEMBERSHIP_ONLY(CLiveManager::SetSuppressMembershipForScript(true)); // found our product, bail break; } } } } } break; case(STORE_SUBSCRIPTION_WELCOME): default: break; } m_CurrentNonCriticalWarningScreen = STORE_NON_CRIT_WARNING_NONE; } return false; } return true; } void cStoreMainScreen::UpdateLeaderBoardDataFetch() { //Do we have a fetch in progress? Is it done? if ( mp_CurrentLeaderboardRead && mp_CurrentLeaderboardRead->Finished()) { if ( mp_CurrentLeaderboardRead->Suceeded() ) { //Get the total. int friendsOwningThisProduct = 0; for ( int s = 0; s < mp_CurrentLeaderboardRead->GetNumRows(); s++ ) { if ( mp_CurrentLeaderboardRead->GetRow(s)->m_ColumnValues[0].Int64Val == 1 ) { friendsOwningThisProduct++; } } int *existingTotal = m_NumFriendsWhoOwnProductMap.Access( m_CurrentLeaderboardDataFetchProductId ); if ( existingTotal ) { *existingTotal = friendsOwningThisProduct; } else { m_NumFriendsWhoOwnProductMap.Insert(m_CurrentLeaderboardDataFetchProductId,friendsOwningThisProduct); } } else { //Not sure what to do in this case. } //Clean up. m_CurrentLeaderboardDataFetchProductId.Reset(); CNetwork::GetLeaderboardMgr().GetLeaderboardReadMgr().ClearLeaderboardRead(mp_CurrentLeaderboardRead); mp_CurrentLeaderboardRead = NULL; //Check if we have fulfilled the current request if ( mp_PendingNumFriendsWhoOwnProduct ) { const int *currentRequestFriendsOwned = m_NumFriendsWhoOwnProductMap.Access(mp_PendingNumFriendsWhoOwnProduct->GetId()); if ( currentRequestFriendsOwned ) { DisplayItemDetails(mp_PendingNumFriendsWhoOwnProduct); mp_PendingNumFriendsWhoOwnProduct = NULL; } } } //Are we not reading, and do we have an outstanding query? if ( mp_CurrentLeaderboardRead == NULL && mp_PendingNumFriendsWhoOwnProduct != NULL && rlFriendsManager::GetTotalNumFriends(NetworkInterface::GetLocalGamerIndex()) > 0 ) { const int COMMERCE_LEADERBOARD_ID = 927; const Leaderboard* lbConf = GAME_CONFIG_PARSER.GetLeaderboard(COMMERCE_LEADERBOARD_ID); if (lbConf == NULL) { return; } //Build our group selector rlLeaderboard2GroupSelector groupSelector; groupSelector.m_NumGroups = 1; strncpy(groupSelector.m_Group[0].m_Category,"ProductId",RL_LEADERBOARD2_CATEGORY_MAX_CHARS); strncpy(groupSelector.m_Group[0].m_Id,mp_PendingNumFriendsWhoOwnProduct->GetId(),RL_LEADERBOARD2_GROUP_ID_MAX_CHARS); //I am currently assuming these following two are empty params. Will consult. rlLeaderboard2GroupSelector groupSelectors[MAX_LEADERBOARD_READ_GROUPS]; int numberOfGroups = 0; const u32 LB_MAX_NUM_FRIENDS = 100; CNetworkReadLeaderboards& readmgr = CNetwork::GetLeaderboardMgr().GetLeaderboardReadMgr(); //For testing const bool includeMyself = false; mp_CurrentLeaderboardRead = readmgr.ReadByFriends( COMMERCE_LEADERBOARD_ID, RL_LEADERBOARD2_TYPE_GROUP_MEMBER, groupSelector, RL_INVALID_CLAN_ID, numberOfGroups, groupSelectors, includeMyself, 0, LB_MAX_NUM_FRIENDS); if ( mp_CurrentLeaderboardRead == NULL ) { mp_PendingNumFriendsWhoOwnProduct = NULL; return; } //Fetch started correctly. m_CurrentLeaderboardDataFetchProductId = mp_PendingNumFriendsWhoOwnProduct->GetId(); } } void cStoreMainScreen::OnPresenceEvent(const rlPresenceEvent* evt) { if (evt) { if (evt->GetId() == PRESENCE_EVENT_SIGNIN_STATUS_CHANGED) { const rlPresenceEventSigninStatusChanged* s = evt->m_SigninStatusChanged; // check if this is a sign out event, only consider the currently active profile if(s && ( s->SignedOut() || s->SignedOffline() ) && ( s->m_GamerInfo.GetLocalIndex() == CControlMgr::GetMainPlayerIndex() ) ) { storeUIDisplayf("Main profile (%d) signed out", s->m_GamerInfo.GetLocalIndex()); sm_bPlayerHasJustSignedOut = true; } } } } void cStoreMainScreen::DoEmptyStoreCheck() { const rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); storeUIAssert(pCommerceUtil); if ( pCommerceUtil == NULL ) { return; } int numActiveProducts = 0; //Loop through the categories for ( int iCat = 0; iCat < m_CategoryIndexArray.GetCount(); iCat++ ) { //GTA only supports one level of categories const cCommerceItemData *pItemData = pCommerceUtil->GetItemData( m_CategoryIndexArray[iCat] ); if(pItemData && pItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_CATEGORY) { const cCommerceCategoryData *pCategoryData = static_cast(pItemData); for (int iProds = 0; iProds < pCategoryData->GetNumProducts(); iProds++ ) { int itemIndex = pCategoryData->GetItemIndex(iProds); const cCommerceItemData *pCatItemToCheck = pCommerceUtil->GetItemData(itemIndex); //Check if this is a product if ( pCatItemToCheck->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT ) { numActiveProducts++; } } } } storeUIDebugf3("Found %d valid products", numActiveProducts); if ( numActiveProducts == 0 ) { m_InNoProductsMode = true; DisplayNoProductsWarning(); UpdateContextHelp(); } else { m_InNoProductsMode = false; } } void cStoreMainScreen::DisplayNoProductsWarning() { if ( m_StoreMovieId == SF_INVALID_MOVIE ) { return; } if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "DISPLAY_ERROR_MESSAGE")) { CScaleformMgr::AddParamString(""); CScaleformMgr::AddParamString(TheText.Get("STORE_NO_CONTENT")); CScaleformMgr::EndMethod(); } } bool cStoreMainScreen::DoQuickCheckout() { if (!cStoreScreenMgr::GetCheckoutInitialItem()) { return false; } const rage::cCommerceItemData* pInitialItemData = nullptr; rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if (pCommerceUtil == nullptr || pCommerceUtil->IsInErrorState()) { cStoreScreenMgr::SetCheckoutInitialItem(false); return false; } const int num = pCommerceUtil->GetNumItems(); for (int i = 0; i < num; i++) { if (pCommerceUtil->GetItemData(i)->GetId() == cStoreScreenMgr::GetInitialItemID()) { //we have found our initial product pInitialItemData = pCommerceUtil->GetItemData(i); break; } } if (pInitialItemData != nullptr && pInitialItemData->GetItemType() == cCommerceItemData::ITEM_TYPE_PRODUCT) { const cCommerceProductData *pProductData = static_cast(pInitialItemData); if (ProcessCheckoutSelected(pProductData)) { storeUIDebugf1("DoQuickCheckout: ItemId: %s", cStoreScreenMgr::GetInitialItemID().c_str()); m_QuickCheckout = true; return true; } } storeUIWarningf("DoQuickCheckout :: Skipped - %s not found", cStoreScreenMgr::GetInitialItemID().c_str()); cStoreScreenMgr::SetCheckoutInitialItem(false); return false; } void cStoreMainScreen::UpdateCheckout() { rage::cCommerceUtil *pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if (pCommerceUtil != nullptr && pCommerceUtil->IsInCheckout()) { return; } if (m_QuickCheckout) { ExitQuickCheckout(); } else { //Start the fade down if (CScaleformMgr::BeginMethod(m_BlackFadeMovieId, SF_BASE_CLASS_STORE, "FADE_TO_TRANSPARENT")) { CScaleformMgr::AddParamInt(STORE_FADE_TIME); CScaleformMgr::EndMethod(); } m_CurrentState = STATE_STORE_FADE_TO_TRANS; #if RSG_NP m_isCommerceLogoDisplaying = g_rlNp.GetCommonDialog().ShowCommerceLogo(rlNpCommonDialog::COMMERCE_LOGO_POS_LEFT); m_shouldCommerceLogoDisplay = true; #endif } } void cStoreMainScreen::ExitQuickCheckout() { if (cStoreScreenMgr::IsExitRequested()) { return; } cStoreScreenMgr::SetCheckoutInitialItem(false); m_QuickCheckout = false; if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "SHUTDOWN_MOVIE")) { CScaleformMgr::EndMethod(); } storeUIDebugf1("ExitQuickCheckout :: Requesting exit"); cStoreScreenMgr::RequestExit(); } bool cStoreMainScreen::ProcessCheckoutSelected(const cCommerceProductData *pProductData) { // reset this here in all paths to be sure m_SubscriptionUpsellItemId.Clear(); m_SubscriptionUpsellTargetItemId.Clear(); if(pProductData == nullptr) return false; cCommerceUtil* pCommerceUtil = CLiveManager::GetCommerceMgr().GetCommerceUtil(); if(pCommerceUtil == nullptr) return false; // check if we need to show an upsell instead of the actual product // - true if we have any missing upsells tagged on the product // - true if we have any upsells (fulfilled or not) and have -commerceAlwaysShowUpsell const bool showSubscriptionUpsell = pCommerceUtil->HasMissingSubscriptionUpsells(pProductData) NOTFINAL_ONLY(|| (pCommerceUtil->HasSubscriptionUpsell(pProductData) && PARAM_commerceAlwaysShowUpsell.Get())); if(showSubscriptionUpsell) { // show a warning screen that an upsell is required for this product and offer an upsell option // cache the product so that we can return to this in the menu once the purchase is complete m_PendingNonCriticalWarningScreen = STORE_SUBSCRIPTION_UPSELL; // store the upsell item m_SubscriptionUpsellItemId = pProductData->GetId(); // an upsell product is a dummy entry for an existing product - get to the actual itemId by removing // the upsell portion of the described itemId int indexOfSplit = pProductData->GetId().IndexOf('_'); if(indexOfSplit > 0) { // step forward m_SubscriptionUpsellTargetItemId = &pProductData->GetId().c_str()[++indexOfSplit]; } #if RSG_OUTPUT const cCommerceItemData* targetItemData = pCommerceUtil->GetItemDataByItemId(m_SubscriptionUpsellTargetItemId.c_str()); storeUIAssert(targetItemData != nullptr); #endif storeUIDebugf1("ProcessCheckoutSelected :: Showing upsell - Product: %s - %s, Target: %s - %s", pProductData->GetName().c_str(), pProductData->GetId().c_str(), targetItemData ? targetItemData->GetName().c_str() : "Invalid", targetItemData ? targetItemData->GetId().c_str() : "Invalid"); // indicate success return true; } else if(pProductData->GetEnumeratedFlag()) { storeUIDebugf1("ProcessCheckoutSelected :: Calling CheckoutProduct on item %s", pProductData->GetId().c_str()); //Check we are allowed to purchase cash. if (pProductData->IsConsumable() && (CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataDirty() || !CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataPopulated())) { storeUIDisplayf("User is attempting to purchase another consumable before the value of the previous cosumable has been assessed."); return false; } else if (pProductData->IsConsumable() && !IsUserAllowedToPurchaseCash(pProductData->GetConsumableId())) { storeUIDisplayf("Setting m_NeedToDisplayCashProductsDisabledMessage to true."); m_PendingNonCriticalWarningScreen = STORE_CANNOT_PURCHASE_CASH; return false; } else if (m_ConsumableProductCheckId.GetLength() > 0) { //We are waiting for info on a previously purchased cash product. Don't allow another cash purchase until we have updated. return false; } if (!pCommerceUtil->IsProductPurchased(pProductData) || pProductData->IsConsumable()) { m_PurchaseCheckProductID = pProductData->GetId(); CNetworkTelemetry::GetPurchaseMetricInformation().AddProductId(m_PurchaseCheckProductID); } // start the checkout process StartCheckout(pProductData); return true; } return false; } bool cStoreMainScreen::StartCheckout(const cCommerceProductData* pProductData) { if(pProductData != nullptr) { storeUIDebugf1("StartCheckout :: ProductName: %s, ProductId: %s", pProductData->GetName().c_str(), pProductData->GetId().c_str()); m_PendingCheckoutProduct = pProductData; //Start the fade down if (CScaleformMgr::BeginMethod(m_BlackFadeMovieId, SF_BASE_CLASS_STORE, "FADE_TO_BLACK")) { CScaleformMgr::AddParamInt(STORE_FADE_TIME); CScaleformMgr::EndMethod(); } m_CurrentState = STATE_STORE_FADE_TO_BLACK; #if RSG_NP g_rlNp.GetCommonDialog().HideCommerceLogo(); m_shouldCommerceLogoDisplay = false; m_isCommerceLogoDisplaying = false; #endif return true; } return false; } bool cStoreMainScreen::IsUserAllowedToPurchaseCash(const char* currentConsumableId) { if ( !cStoreScreenMgr::AreCashProductsAllowed() ) { return false; } bool currentConsumableHandled = false; atString currentSelectedIDString(currentConsumableId); if (currentConsumableId == NULL) { currentConsumableHandled = true; } MoneyInterface::CashProductBatches batchesPending; if ( !CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataDirty() && CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataPopulated() ) { const CCashProductsData::CCashProductsList& productsList = CashPackInfo::GetInstance().GetProductsList(); for (int i = 0; i < productsList.GetCount(); i++ ) { atString packName; if ( !productsList[i].GetSKUPackName(packName) ) { continue; } int consumableLevel = CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel( packName.c_str() ); if ( consumableLevel > 0 ) { MoneyInterface::CashProductBatch batch; batch.m_Quantity = consumableLevel; //See if this is our currently selected ID, and if so bump the quantity by one and mark as handled. if ( packName == currentSelectedIDString ) { batch.m_Quantity++; currentConsumableHandled = true; } batch.m_id.SetFromString(packName); batchesPending.PushAndGrow(batch); } } } else { //We have no pending products, but still need space for the selected item batchesPending.Reserve(1); } //If the currently selected consumable has not been added to the batches, we take care of that here. if ( !currentConsumableHandled ) { MoneyInterface::CashProductBatch batch; batch.m_Quantity = 1; batch.m_id.SetFromString(currentSelectedIDString); currentConsumableHandled = true; batchesPending.PushAndGrow(batch); } return MoneyInterface::CanBuyCashProducts( batchesPending ); } void cStoreMainScreen::UpdateTopOfScreenTextInfo() { int hour = CClock::GetHour(); int minute = CClock::GetMinute(); // get the date/time: char const *asciiDayString = TheText.Get(CClock::GetDayOfWeekTextId()); char dateTimeString[64] = {"\0"}; formatf(dateTimeString, NELEM(dateTimeString), "%s %02d:%02d", asciiDayString, hour, minute); char cashBuff[64]; CFrontendStatsMgr::FormatInt64ToCash(cStoreScreenMgr::GetWalletCashForDisplay(), cashBuff, NELEM(cashBuff)); //Cash char CashString[128] = {0}; if (cStoreScreenMgr::WasOpenedFromNetworkGame() || NetworkInterface::IsGameInProgress()) { char bankBuff[64]; CFrontendStatsMgr::FormatInt64ToCash(cStoreScreenMgr::GetBankCashForDisplay() + m_PendingCash, bankBuff, NELEM(bankBuff)); // build the string out of text from the text file & also the converted cash and bank cash safecpy(CashString, TheText.Get("MENU_PLYR_BANK"), NELEM(CashString)); safecat(CashString, " ", NELEM(CashString)); safecat(CashString, (char*)bankBuff, NELEM(CashString)); safecat(CashString, " ", NELEM(CashString)); safecat(CashString, TheText.Get("MENU_PLYR_CASH"), NELEM(CashString)); safecat(CashString, " ", NELEM(CashString)); safecat(CashString, (char*)cashBuff, NELEM(CashString)); } else { safecpy(CashString, (char*)cashBuff, NELEM(CashString)); // just standard cash } if (CScaleformMgr::IsMovieActive(m_StoreMovieId)) { if (CScaleformMgr::BeginMethod(m_StoreMovieId, SF_BASE_CLASS_STORE, "")) { CScaleformMgr::AddParamString( cStoreScreenMgr::GetPlayerNameToDisplay(), false); CScaleformMgr::AddParamString( dateTimeString, false); CScaleformMgr::AddParamString( CashString, false); CScaleformMgr::AddParamBool( false ); CScaleformMgr::EndMethod(); } } } void cStoreMainScreen::InitialiseConsumablesLevel() { const CCashProductsData::CCashProductsList& productsList = CashPackInfo::GetInstance().GetProductsList(); m_InitialConsumableLevels.Reset(); m_InitialConsumableLevels.Reserve(productsList.GetCount()); for ( int i=0; i < productsList.GetCount(); i++ ) { atString packName; if (!productsList[i].GetSKUPackName(packName)) { continue; } int consumableLevel = 0; if (CLiveManager::GetCommerceMgr().GetConsumableManager()->IsOwnershipDataPopulated()) { consumableLevel = CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel(packName); } m_InitialConsumableLevels.PushAndGrow(consumableLevel); } } void cStoreMainScreen::UpdateChangeInConsumableLevelCheck() { const CCashProductsData::CCashProductsList& productsList = CashPackInfo::GetInstance().GetProductsList(); for ( int i=0; i < productsList.GetCount(); i++ ) { atString packName; if (!productsList[i].GetSKUPackName(packName)) { continue; } int numberOfPackOwned = CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel(packName); //Take this opportunity to trigger telemetry based on level changes. if ( m_InitialConsumableLevels[i] != numberOfPackOwned ) { int newlyObtainedConsumableNum = numberOfPackOwned - m_InitialConsumableLevels[i]; if (newlyObtainedConsumableNum < 0) newlyObtainedConsumableNum *= -1; //DO EVENT: We just obtained newlyObtainedConsumableNum of autoConsumeArray[i] via the in game store. //POSSIBLE FALSE POSITIVE CASE: Simultaneous web purchase or code redemption while in store. Differentiation on that level requires platform data. int amount = static_cast(productsList[i].m_Value * newlyObtainedConsumableNum); float usdeAmount = productsList[i].m_USDE * newlyObtainedConsumableNum; CNetworkTelemetry::AppendMetric(MetricInGamePurchaseVC(amount, usdeAmount)); //This line just here to avoid the warning as error. (void)newlyObtainedConsumableNum; //Now we set the initial level to the new level in case of multiple purchases in the same store session. m_InitialConsumableLevels[i] = numberOfPackOwned; } } } void cStoreMainScreen::UpdatePendingCash() { const CCashProductsData::CCashProductsList& productsList = CashPackInfo::GetInstance().GetProductsList(); m_PendingCash = 0; for ( int i=0; i < productsList.GetCount(); i++ ) { atString packName; if (!productsList[i].GetSKUPackName(packName)) { continue; } int numberOfPackOwned = CLiveManager::GetCommerceMgr().GetConsumableManager()->GetConsumableLevel(packName); int valueOfPack = MoneyInterface::GetCashPackValue(packName); if ( valueOfPack != -1 ) { m_PendingCash += ( numberOfPackOwned * valueOfPack ); } } }