#include "stdafx.h" #include "UI.h" #include "UILayer.h" #include "UIScene.h" UILayer::UILayer(UIGroup *parent) { m_parentGroup = parent; m_hasFocus = false; m_bMenuDisplayed = false; m_bPauseMenuDisplayed = false; m_bContainerMenuDisplayed = false; m_bIgnoreAutosaveMenuDisplayed = false; m_bIgnorePlayerJoinMenuDisplayed = false; } void UILayer::tick() { // Delete old scenes - deleting a scene can cause a new scene to be deleted, so we need to make a copy of the scenes that we are going to try and destroy this tick vectorscenesToDeleteCopy; for( AUTO_VAR(it,m_scenesToDelete.begin()); it != m_scenesToDelete.end(); it++) { UIScene *scene = (*it); scenesToDeleteCopy.push_back(scene); } m_scenesToDelete.clear(); // Delete the scenes in our copy if they are ready to delete, otherwise add back to the ones that are still to be deleted. Actually deleting a scene might also add something back into m_scenesToDelete. for( AUTO_VAR(it,scenesToDeleteCopy.begin()); it != scenesToDeleteCopy.end(); it++) { UIScene *scene = (*it); if( scene->isReadyToDelete()) { delete scene; } else { m_scenesToDelete.push_back(scene); } } while (!m_scenesToDestroy.empty()) { UIScene *scene = m_scenesToDestroy.back(); m_scenesToDestroy.pop_back(); scene->destroyMovie(); } m_scenesToDestroy.clear(); for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { (*it)->tick(); } // Note: reverse iterator, the last element is the top of the stack int sceneIndex = m_sceneStack.size() - 1; //for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) while( sceneIndex >= 0 && sceneIndex < m_sceneStack.size() ) { //(*it)->tick(); UIScene *scene = m_sceneStack[sceneIndex]; scene->tick(); --sceneIndex; // TODO: We may wish to ignore ticking the rest of the stack based on this scene } } void UILayer::render(S32 width, S32 height, C4JRender::eViewportType viewport) { if(!ui.IsExpectingOrReloadingSkin()) { for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { AUTO_VAR(itRef,m_componentRefCount.find((*it)->getSceneType())); if(itRef != m_componentRefCount.end() && itRef->second.second) { if((*it)->isVisible() ) { PIXBeginNamedEvent(0, "Rendering component %d", (*it)->getSceneType() ); (*it)->render(width, height,viewport); PIXEndNamedEvent(); } } } } if(!m_sceneStack.empty()) { int lowestRenderable = m_sceneStack.size() - 1; for(;lowestRenderable >= 0; --lowestRenderable) { if(m_sceneStack[lowestRenderable]->hidesLowerScenes()) break; } if(lowestRenderable < 0) lowestRenderable = 0; for(;lowestRenderable < m_sceneStack.size(); ++lowestRenderable) { if(m_sceneStack[lowestRenderable]->isVisible() && (!ui.IsExpectingOrReloadingSkin() || m_sceneStack[lowestRenderable]->getSceneType()==eUIScene_Timer)) { PIXBeginNamedEvent(0, "Rendering scene %d", m_sceneStack[lowestRenderable]->getSceneType() ); m_sceneStack[lowestRenderable]->render(width, height,viewport); PIXEndNamedEvent(); } } } } bool UILayer::IsSceneInStack(EUIScene scene) { bool inStack = false; for(int i = m_sceneStack.size() - 1;i >= 0; --i) { if(m_sceneStack[i]->getSceneType() == scene) { inStack = true; break; } } return inStack; } bool UILayer::HasFocus(int iPad) { bool hasFocus = false; if(m_hasFocus) { for(int i = m_sceneStack.size() - 1;i >= 0; --i) { if(m_sceneStack[i]->stealsFocus() ) { if(m_sceneStack[i]->hasFocus(iPad)) { hasFocus = true; } break; } } } return hasFocus; } bool UILayer::hidesLowerScenes() { bool hidesScenes = false; for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { if((*it)->hidesLowerScenes()) { hidesScenes = true; break; } } if(!hidesScenes && !m_sceneStack.empty()) { for(int i = m_sceneStack.size() - 1;i >= 0; --i) { if(m_sceneStack[i]->hidesLowerScenes()) { hidesScenes = true; break; } } } return hidesScenes; } void UILayer::getRenderDimensions(S32 &width, S32 &height) { m_parentGroup->getRenderDimensions(width, height); } void UILayer::DestroyAll() { for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { (*it)->destroyMovie(); } for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) { (*it)->destroyMovie(); } } void UILayer::ReloadAll(bool force) { for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { (*it)->reloadMovie(force); } if(!m_sceneStack.empty()) { int lowestRenderable = 0; for(;lowestRenderable < m_sceneStack.size(); ++lowestRenderable) { m_sceneStack[lowestRenderable]->reloadMovie(); } } } bool UILayer::GetMenuDisplayed() { return m_bMenuDisplayed; } bool UILayer::NavigateToScene(int iPad, EUIScene scene, void *initData) { UIScene *newScene = NULL; switch(scene) { // Debug #ifdef _DEBUG_MENUS_ENABLED case eUIScene_DebugOverlay: newScene = new UIScene_DebugOverlay(iPad, initData, this); break; case eUIScene_DebugSetCamera: newScene = new UIScene_DebugSetCamera(iPad, initData, this); break; case eUIScene_DebugCreateSchematic: newScene = new UIScene_DebugCreateSchematic(iPad, initData, this); break; #endif case eUIScene_DebugOptions: newScene = new UIScene_DebugOptionsMenu(iPad, initData, this); break; // Containers case eUIScene_InventoryMenu: newScene = new UIScene_InventoryMenu(iPad, initData, this); break; case eUIScene_CreativeMenu: newScene = new UIScene_CreativeMenu(iPad, initData, this); break; case eUIScene_ContainerMenu: case eUIScene_LargeContainerMenu: newScene = new UIScene_ContainerMenu(iPad, initData, this); break; case eUIScene_BrewingStandMenu: newScene = new UIScene_BrewingStandMenu(iPad, initData, this); break; case eUIScene_DispenserMenu: newScene = new UIScene_DispenserMenu(iPad, initData, this); break; case eUIScene_EnchantingMenu: newScene = new UIScene_EnchantingMenu(iPad, initData, this); break; case eUIScene_FurnaceMenu: newScene = new UIScene_FurnaceMenu(iPad, initData, this); break; case eUIScene_Crafting2x2Menu: case eUIScene_Crafting3x3Menu: newScene = new UIScene_CraftingMenu(iPad, initData, this); break; case eUIScene_TradingMenu: newScene = new UIScene_TradingMenu(iPad, initData, this); break; case eUIScene_AnvilMenu: newScene = new UIScene_AnvilMenu(iPad, initData, this); break; // Help and Options case eUIScene_HelpAndOptionsMenu: newScene = new UIScene_HelpAndOptionsMenu(iPad, initData, this); break; case eUIScene_SettingsMenu: newScene = new UIScene_SettingsMenu(iPad, initData, this); break; case eUIScene_SettingsOptionsMenu: newScene = new UIScene_SettingsOptionsMenu(iPad, initData, this); break; case eUIScene_SettingsAudioMenu: newScene = new UIScene_SettingsAudioMenu(iPad, initData, this); break; case eUIScene_SettingsControlMenu: newScene = new UIScene_SettingsControlMenu(iPad, initData, this); break; case eUIScene_SettingsGraphicsMenu: newScene = new UIScene_SettingsGraphicsMenu(iPad, initData, this); break; case eUIScene_SettingsUIMenu: newScene = new UIScene_SettingsUIMenu(iPad, initData, this); break; case eUIScene_SkinSelectMenu: newScene = new UIScene_SkinSelectMenu(iPad, initData, this); break; case eUIScene_HowToPlayMenu: newScene = new UIScene_HowToPlayMenu(iPad, initData, this); break; case eUIScene_HowToPlay: newScene = new UIScene_HowToPlay(iPad, initData, this); break; case eUIScene_ControlsMenu: newScene = new UIScene_ControlsMenu(iPad, initData, this); break; case eUIScene_ReinstallMenu: newScene = new UIScene_ReinstallMenu(iPad, initData, this); break; case eUIScene_Credits: newScene = new UIScene_Credits(iPad, initData, this); break; // Other in-game case eUIScene_PauseMenu: newScene = new UIScene_PauseMenu(iPad, initData, this); break; case eUIScene_DeathMenu: newScene = new UIScene_DeathMenu(iPad, initData, this); break; case eUIScene_ConnectingProgress: newScene = new UIScene_ConnectingProgress(iPad, initData, this); break; case eUIScene_SignEntryMenu: newScene = new UIScene_SignEntryMenu(iPad, initData, this); break; case eUIScene_InGameInfoMenu: newScene = new UIScene_InGameInfoMenu(iPad, initData, this); break; case eUIScene_InGameHostOptionsMenu: newScene = new UIScene_InGameHostOptionsMenu(iPad, initData, this); break; case eUIScene_InGamePlayerOptionsMenu: newScene = new UIScene_InGamePlayerOptionsMenu(iPad, initData, this); break; #if defined(_XBOX_ONE) || defined(__ORBIS__) case eUIScene_InGameSaveManagementMenu: newScene = new UIScene_InGameSaveManagementMenu(iPad, initData, this); break; #endif case eUIScene_TeleportMenu: newScene = new UIScene_TeleportMenu(iPad, initData, this); break; case eUIScene_EndPoem: if(IsSceneInStack(eUIScene_EndPoem)) { app.DebugPrintf("Skipped EndPoem as one was already showing\n"); return false; } else { newScene = new UIScene_EndPoem(iPad, initData, this); } break; // Frontend case eUIScene_TrialExitUpsell: newScene = new UIScene_TrialExitUpsell(iPad, initData, this); break; case eUIScene_Intro: newScene = new UIScene_Intro(iPad, initData, this); break; case eUIScene_SaveMessage: newScene = new UIScene_SaveMessage(iPad, initData, this); break; case eUIScene_MainMenu: newScene = new UIScene_MainMenu(iPad, initData, this); break; case eUIScene_LoadOrJoinMenu: newScene = new UIScene_LoadOrJoinMenu(iPad, initData, this); break; case eUIScene_LoadMenu: newScene = new UIScene_LoadMenu(iPad, initData, this); break; case eUIScene_JoinMenu: newScene = new UIScene_JoinMenu(iPad, initData, this); break; case eUIScene_CreateWorldMenu: newScene = new UIScene_CreateWorldMenu(iPad, initData, this); break; case eUIScene_LaunchMoreOptionsMenu: newScene = new UIScene_LaunchMoreOptionsMenu(iPad, initData, this); break; case eUIScene_FullscreenProgress: newScene = new UIScene_FullscreenProgress(iPad, initData, this); break; case eUIScene_LeaderboardsMenu: newScene = new UIScene_LeaderboardsMenu(iPad, initData, this); break; case eUIScene_DLCMainMenu: newScene = new UIScene_DLCMainMenu(iPad, initData, this); break; case eUIScene_DLCOffersMenu: newScene = new UIScene_DLCOffersMenu(iPad, initData, this); break; case eUIScene_EULA: newScene = new UIScene_EULA(iPad, initData, this); break; // Other case eUIScene_Keyboard: newScene = new UIScene_Keyboard(iPad, initData, this); break; case eUIScene_QuadrantSignin: newScene = new UIScene_QuadrantSignin(iPad, initData, this); break; case eUIScene_MessageBox: if(IsSceneInStack(eUIScene_MessageBox)) { app.DebugPrintf("Skipped MessageBox as one was already showing\n"); return false; } else { newScene = new UIScene_MessageBox(iPad, initData, this); } break; case eUIScene_Timer: newScene = new UIScene_Timer(iPad, initData, this); break; }; if(newScene == NULL) { app.DebugPrintf("WARNING: Scene %d was not created. Add it to UILayer::NavigateToScene\n", scene); return false; } if(m_sceneStack.size() > 0) { newScene->setBackScene(m_sceneStack[m_sceneStack.size()-1]); } m_sceneStack.push_back(newScene); updateFocusState(); newScene->tick(); return true; } bool UILayer::NavigateBack(int iPad, EUIScene eScene) { if(m_sceneStack.size() == 0) return false; bool navigated = false; if(eScene < eUIScene_COUNT) { UIScene *scene = NULL; do { scene = m_sceneStack.back(); if(scene->getSceneType() == eScene) { navigated = true; break; } else { if(scene->hasFocus(iPad)) { removeScene(scene); } else { // No focus on the top scene, so this use shouldn't be navigating! break; } } } while(m_sceneStack.size() > 0); } else { UIScene *scene = m_sceneStack.back(); if(scene->hasFocus(iPad)) { removeScene(scene); navigated = true; } } return navigated; } void UILayer::showComponent(int iPad, EUIScene scene, bool show) { AUTO_VAR(it,m_componentRefCount.find(scene)); if(it != m_componentRefCount.end()) { it->second.second = show; return; } if(show) addComponent(iPad,scene); } bool UILayer::isComponentVisible(EUIScene scene) { bool visible = false; AUTO_VAR(it,m_componentRefCount.find(scene)); if(it != m_componentRefCount.end()) { visible = it->second.second; } return visible; } UIScene *UILayer::addComponent(int iPad, EUIScene scene, void *initData) { AUTO_VAR(it,m_componentRefCount.find(scene)); if(it != m_componentRefCount.end()) { ++it->second.first; for(AUTO_VAR(itComp,m_components.begin()); itComp != m_components.end(); ++itComp) { if( (*itComp)->getSceneType() == scene ) { return *itComp; } } return NULL; } UIScene *newScene = NULL; switch(scene) { case eUIComponent_Panorama: newScene = new UIComponent_Panorama(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_DebugUIConsole: newScene = new UIComponent_DebugUIConsole(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_DebugUIMarketingGuide: newScene = new UIComponent_DebugUIMarketingGuide(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_Logo: newScene = new UIComponent_Logo(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_Tooltips: newScene = new UIComponent_Tooltips(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_TutorialPopup: newScene = new UIComponent_TutorialPopup(iPad, initData, this); // Start hidden m_componentRefCount[scene] = pair(1,false); break; case eUIScene_HUD: newScene = new UIScene_HUD(iPad, initData, this); // Start hidden m_componentRefCount[scene] = pair(1,false); break; case eUIComponent_Chat: newScene = new UIComponent_Chat(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_PressStartToPlay: newScene = new UIComponent_PressStartToPlay(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; case eUIComponent_MenuBackground: newScene = new UIComponent_MenuBackground(iPad, initData, this); m_componentRefCount[scene] = pair(1,true); break; }; if(newScene == NULL) return NULL; m_components.push_back(newScene); return newScene; } void UILayer::removeComponent(EUIScene scene) { AUTO_VAR(it,m_componentRefCount.find(scene)); if(it != m_componentRefCount.end()) { --it->second.first; if(it->second.first <= 0) { m_componentRefCount.erase(it); for(AUTO_VAR(compIt, m_components.begin()) ; compIt != m_components.end(); ) { if( (*compIt)->getSceneType() == scene) { #ifdef __PSVITA__ // remove any touchboxes ui.TouchBoxesClear((*compIt)); #endif m_scenesToDelete.push_back((*compIt)); (*compIt)->handleDestroy(); // For anything that might require the pointer be valid compIt = m_components.erase(compIt); } else { ++compIt; } } } } } void UILayer::removeScene(UIScene *scene) { #ifdef __PSVITA__ // remove any touchboxes ui.TouchBoxesClear(scene); #endif AUTO_VAR(newEnd, std::remove(m_sceneStack.begin(), m_sceneStack.end(), scene) ); m_sceneStack.erase(newEnd, m_sceneStack.end()); m_scenesToDelete.push_back(scene); scene->handleDestroy(); // For anything that might require the pointer be valid bool hadFocus = m_hasFocus; updateFocusState(); // If this layer has focus, pass it on if (m_hasFocus || hadFocus) { m_hasFocus = false; m_parentGroup->UpdateFocusState(); } } void UILayer::closeAllScenes() { vector temp; temp.insert(temp.end(), m_sceneStack.begin(), m_sceneStack.end()); m_sceneStack.clear(); for(AUTO_VAR(it, temp.begin()); it != temp.end(); ++it) { #ifdef __PSVITA__ // remove any touchboxes ui.TouchBoxesClear(*it); #endif m_scenesToDelete.push_back(*it); (*it)->handleDestroy(); // For anything that might require the pointer be valid } updateFocusState(); // If this layer has focus, pass it on if (m_hasFocus) { m_hasFocus = false; m_parentGroup->UpdateFocusState(); } } // Get top scene on stack (or NULL if stack is empty) UIScene *UILayer::GetTopScene() { if(m_sceneStack.size() == 0) { return NULL; } else { return m_sceneStack[m_sceneStack.size()-1]; } } // Updates layer focus state if no error message is present (unless this is the error layer) bool UILayer::updateFocusState(bool allowedFocus /* = false */) { // If haveFocus is false, request it if (!allowedFocus) { // To update focus in this layer we need to request focus from group // Focus will be denied if there's an upper layer that needs focus allowedFocus = m_parentGroup->RequestFocus(this); } m_bMenuDisplayed = false; m_bPauseMenuDisplayed = false; m_bContainerMenuDisplayed = false; m_bIgnoreAutosaveMenuDisplayed = false; m_bIgnorePlayerJoinMenuDisplayed = false; bool layerFocusSet = false; for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *scene = *it; // UPDATE FOCUS STATES if(!layerFocusSet && allowedFocus && scene->stealsFocus()) { scene->gainFocus(); layerFocusSet = true; } else { scene->loseFocus(); if(allowedFocus && app.GetGameStarted()) { // 4J Stu - This is a memory optimisation so we don't keep scenes loaded in memory all the time // This is required for PS3 (and likely Vita), but I'm removing it on XboxOne so that we can avoid // the scene creation time (which can be >0.5s) since we have the memory to spare #ifndef _XBOX_ONE m_scenesToDestroy.push_back(scene); #endif } } /// UPDATE STACK STATES // 4J-PB - this should just be true m_bMenuDisplayed=true; EUIScene sceneType = scene->getSceneType(); switch(sceneType) { case eUIScene_PauseMenu: m_bPauseMenuDisplayed = true; break; case eUIScene_Crafting2x2Menu: case eUIScene_Crafting3x3Menu: case eUIScene_FurnaceMenu: case eUIScene_ContainerMenu: case eUIScene_LargeContainerMenu: case eUIScene_InventoryMenu: case eUIScene_CreativeMenu: case eUIScene_DispenserMenu: case eUIScene_BrewingStandMenu: case eUIScene_EnchantingMenu: m_bContainerMenuDisplayed=true; // Intentional fall-through case eUIScene_DeathMenu: case eUIScene_FullscreenProgress: case eUIScene_SignEntryMenu: case eUIScene_EndPoem: m_bIgnoreAutosaveMenuDisplayed = true; break; } switch(sceneType) { case eUIScene_FullscreenProgress: case eUIScene_EndPoem: case eUIScene_Credits: case eUIScene_LeaderboardsMenu: m_bIgnorePlayerJoinMenuDisplayed = true; break; } } m_hasFocus = layerFocusSet; return m_hasFocus; } #ifdef __PSVITA__ UIScene *UILayer::getCurrentScene() { // Note: reverse iterator, the last element is the top of the stack for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *scene = *it; // 4J-PB - only used on Vita, so iPad 0 is fine if(scene->hasFocus(0) && scene->canHandleInput()) { return scene; } } return NULL; } #endif void UILayer::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) { // Note: reverse iterator, the last element is the top of the stack for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *scene = *it; if(scene->hasFocus(iPad) && scene->canHandleInput()) { // 4J-PB - ignore repeats of action ABXY buttons // fix for PS3 213 - [MAIN MENU] Holding down buttons will continue to activate every prompt. // 4J Stu - Changed this slightly to add the allowRepeat function so we can allow repeats in the crafting menu if(repeat && !scene->allowRepeat(key) ) { return; } scene->handleInput(iPad, key, repeat, pressed, released, handled); } // Fix for PS3 #444 - [IN GAME] If the user keeps pressing CROSS while on the 'Save Game' screen the title will crash. handled = handled || scene->hidesLowerScenes(); if(handled ) break; } // Components can't take input or focus } void UILayer::HandleDLCMountingComplete() { for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *topScene = *it; app.DebugPrintf("UILayer::HandleDLCMountingComplete - topScene\n"); topScene->HandleDLCMountingComplete(); } } void UILayer::HandleDLCInstalled() { for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *topScene = *it; topScene->HandleDLCInstalled(); } } #ifdef _XBOX_ONE void UILayer::HandleDLCLicenseChange() { for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) { UIScene *topScene = *it; topScene->HandleDLCLicenseChange(); } } #endif bool UILayer::IsFullscreenGroup() { return m_parentGroup->IsFullscreenGroup(); } C4JRender::eViewportType UILayer::getViewport() { return m_parentGroup->GetViewportType(); } void UILayer::handleUnlockFullVersion() { for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) { (*it)->handleUnlockFullVersion(); } } void UILayer::PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic) { __int64 layerStatic = 0; __int64 layerDynamic = 0; for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) { (*it)->PrintTotalMemoryUsage(layerStatic, layerDynamic); } for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) { (*it)->PrintTotalMemoryUsage(layerStatic, layerDynamic); } app.DebugPrintf(app.USER_SR, " \\- Layer static: %d , Layer dynamic: %d\n", layerStatic, layerDynamic); totalStatic += layerStatic; totalDynamic += layerDynamic; } // Returns the first scene of given type if it exists, NULL otherwise UIScene *UILayer::FindScene(EUIScene sceneType) { for (int i = 0; i < m_sceneStack.size(); i++) { if (m_sceneStack[i]->getSceneType() == sceneType) { return m_sceneStack[i]; } } return NULL; }