Index: ps/trunk/binaries/data/mods/public/gui/pregame/BackgroundHandler.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/BackgroundHandler.js +++ ps/trunk/binaries/data/mods/public/gui/pregame/BackgroundHandler.js @@ -2,47 +2,71 @@ { constructor(layers) { + this.backgroundLayers = layers.map((layer, i) => + new BackgroundLayer(layer, i)); + this.initTime = Date.now(); - this.layerSet = layers; - this.initBackgrounds(); + this.backgrounds = Engine.GetGUIObjectByName("backgrounds"); + this.backgrounds.onTick = this.onTick.bind(this); + this.backgrounds.onWindowResized = this.onWindowResized.bind(this); + this.onWindowResized(); } - initBackgrounds() + onWindowResized() { - this.layerSet.forEach((layer, i) => { - - let background = Engine.GetGUIObjectByName("background[" + i + "]"); - background.sprite = layer.sprite; - background.z = i; - background.hidden = false; - }); + let size = this.backgrounds.getComputedSize(); + this.backgroundsSize = deepfreeze(new GUISize(size.top, size.left, size.right, size.bottom)); } onTick() { - let now = Date.now(); + let time = Date.now() - this.initTime; + for (let background of this.backgroundLayers) + background.update(time, this.backgroundsSize); + } +} - this.layerSet.forEach((layer, i) => { +class BackgroundLayer +{ + constructor(layer, i) + { + this.layer = layer; - let background = Engine.GetGUIObjectByName("background[" + i + "]"); + this.background = Engine.GetGUIObjectByName("background[" + i + "]"); + this.background.sprite = this.layer.sprite; + this.background.z = i; + this.background.hidden = false; + } - let screen = background.parent.getComputedSize(); - let h = screen.bottom - screen.top; - let w = h * 16/9; - let iw = h * 2; - - let offset = layer.offset((now - this.initTime) / 1000, w); - - if (layer.tiling) - { - let left = offset % iw; - if (left >= 0) - left -= iw; - background.size = new GUISize(left, screen.top, screen.right, screen.bottom); - } - else - background.size = new GUISize(screen.right/2 - h + offset, screen.top, screen.right/2 + h + offset, screen.bottom); - }); + update(time, backgroundsSize) + { + let height = backgroundsSize.bottom - backgroundsSize.top; + let width = height * this.AspectRatio; + let offset = this.layer.offset(time / 1000, width); + + if (this.layer.tiling) + { + let iw = height * 2; + let left = offset % iw; + if (left >= 0) + left -= iw; + this.background.size = new GUISize( + left, + backgroundsSize.top, + backgroundsSize.right, + backgroundsSize.bottom); + } + else + { + let right = backgroundsSize.right / 2 + offset; + this.background.size = new GUISize( + right - height, + backgroundsSize.top, + right + height, + backgroundsSize.bottom); + } } } + +BackgroundLayer.prototype.AspectRatio = 16 / 9; Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js +++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js @@ -1,11 +1,12 @@ +/** + * This class sets up the main menu buttons, animates submenu that opens when + * clicking on category buttons, assigns the defined actions and hotkeys to every button. + */ class MainMenuItemHandler { - constructor(menuItems, menuSpeed = 1.2, margin = 4, buttonHeight = 28) + constructor(menuItems) { this.menuItems = menuItems; - this.menuSpeed = menuSpeed; - this.margin = margin; - this.buttonHeight = buttonHeight; this.lastTickTime = Date.now(); this.mainMenu = Engine.GetGUIObjectByName("mainMenu"); @@ -17,7 +18,9 @@ this.setupMenuButtons(this.mainMenuButtons.children, this.menuItems); this.setupHotkeys(this.menuItems); - Engine.GetGUIObjectByName("closeMenuButton").onPress = () => { this.closeSubmenu(); }; + + this.mainMenu.onTick = this.onTick.bind(this); + Engine.GetGUIObjectByName("closeMenuButton").onPress = this.closeSubmenu.bind(this); } setupMenuButtons(buttons, menuItems) @@ -29,8 +32,8 @@ return; button.size = new GUISize( - 0, (this.buttonHeight + this.margin) * i, - 0, (this.buttonHeight + this.margin) * i + this.buttonHeight, + 0, (this.ButtonHeight + this.Margin) * i, + 0, (this.ButtonHeight + this.Margin) * i + this.ButtonHeight, 0, 0, 100, 0); button.caption = item.caption; button.tooltip = item.tooltip; @@ -52,17 +55,14 @@ setupHotkeys(menuItems) { - for (let name in menuItems) + for (let i in menuItems) { - let item = menuItems[name]; - + let item = menuItems[i]; if (item.onPress && item.hotkey) - { Engine.SetGlobalHotkey(item.hotkey, () => { this.closeSubmenu(); item.onPress(); }); - } if (item.submenu) this.setupHotkeys(item.submenu); @@ -72,20 +72,39 @@ openSubmenu(i) { this.setupMenuButtons(this.submenuButtons.children, this.menuItems[i].submenu); + let top = this.mainMenuButtons.size.top + this.mainMenuButtons.children[i].size.top; + this.submenu.size = new GUISize( - this.submenu.size.left, top - this.margin, - this.submenu.size.right, top + ((this.buttonHeight + this.margin) * this.menuItems[i].submenu.length)); + this.submenu.size.left, top - this.Margin, + this.submenu.size.right, top + (this.ButtonHeight + this.Margin) * this.menuItems[i].submenu.length); + this.submenu.hidden = false; - this.MainMenuPanelRightBorderTop.size = "100%-2 0 100% " + (this.submenu.size.top + this.margin); - this.MainMenuPanelRightBorderBottom.size = "100%-2 " + this.submenu.size.bottom + " 100% 100%"; + + { + let size = this.MainMenuPanelRightBorderTop.size; + size.bottom = this.submenu.size.top + this.Margin; + size.rbottom = 0; + this.MainMenuPanelRightBorderTop.size = size; + } + + { + let size = this.MainMenuPanelRightBorderBottom.size; + size.top = this.submenu.size.bottom; + this.MainMenuPanelRightBorderBottom.size = size; + } } closeSubmenu() { this.submenu.hidden = true; this.submenu.size = this.mainMenu.size; - this.MainMenuPanelRightBorderTop.size = "100%-2 0 100% 100%"; + + let size = this.MainMenuPanelRightBorderTop.size; + size.top = 0; + size.bottom = 0; + size.rbottom = 100; + this.MainMenuPanelRightBorderTop.size = size; } onTick() @@ -93,7 +112,7 @@ let now = Date.now(); let maxOffset = this.mainMenu.size.right - this.submenu.size.left; - let offset = Math.min(this.menuSpeed * (now - this.lastTickTime), maxOffset); + let offset = Math.min(this.MenuSpeed * (now - this.lastTickTime), maxOffset); this.lastTickTime = now; @@ -106,3 +125,18 @@ this.submenu.size = size; } } + +/** + * Vertical size per button. + */ +MainMenuItemHandler.prototype.ButtonHeight = 28; + +/** + * Distance between consecutive buttons. + */ +MainMenuItemHandler.prototype.Margin = 4; + +/** + * Collapse / expansion speed in pixels per milliseconds used when animating the button menu size. + */ +MainMenuItemHandler.prototype.MenuSpeed = 1.2; Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuPage.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuPage.js +++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuPage.js @@ -0,0 +1,60 @@ +/** + * This is the handler that coordinates all other handlers on this GUI page. + */ +class MainMenuPage +{ + constructor(data, hotloadData, mainMenuItems, backgroundLayerData, projectInformation, communityButtons) + { + this.backgroundHandler = new BackgroundHandler(pickRandom(backgroundLayerData)); + this.menuHandler = new MainMenuItemHandler(mainMenuItems); + this.splashScreenHandler = new SplashScreenHandler(data, hotloadData && hotloadData.splashScreenHandler); + + new MusicHandler(); + new ProjectInformationHandler(projectInformation); + new CommunityButtonHandler(communityButtons); + } + + getHotloadData() + { + return { + "splashScreenHandler": this.splashScreenHandler.getHotloadData() + }; + } +} + +class MusicHandler +{ + constructor() + { + initMusic(); + global.music.setState(global.music.states.MENU); + } +} + +class ProjectInformationHandler +{ + constructor(projectInformation) + { + for (let objectName in projectInformation) + for (let propertyName in projectInformation[objectName]) + Engine.GetGUIObjectByName(objectName)[propertyName] = projectInformation[objectName][propertyName]; + } +} + +class CommunityButtonHandler +{ + constructor(communityButtons) + { + let buttons = Engine.GetGUIObjectByName("communityButtons").children; + + communityButtons.forEach((buttonInfo, i) => { + let button = buttons[i]; + button.hidden = false; + for (let propertyName in buttonInfo) + button[propertyName] = buttonInfo[propertyName]; + }); + + if (buttons.length < communityButtons.length) + error("GUI page has space for " + buttons.length + " community buttons, but " + menuItems.length + " items are provided!"); + } +} Index: ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js +++ ps/trunk/binaries/data/mods/public/gui/pregame/SplashscreenHandler.js @@ -3,6 +3,9 @@ constructor(initData, hotloadData) { this.showSplashScreen = hotloadData ? hotloadData.showSplashScreen : initData && initData.isStartup; + + this.mainMenuPage = Engine.GetGUIObjectByName("mainMenuPage"); + this.mainMenuPage.onTick = this.onFirstTick.bind(this); } getHotloadData() @@ -15,10 +18,13 @@ // Don't call this from the init function in order to not crash when opening the new page on init on hotloading // and not possibly crash when opening the new page on init and throwing a JS error. - onTick() + onFirstTick() { if (this.showSplashScreen) this.openPage(); + + // TODO: support actually deleting the handler + this.mainMenuPage.onTick = () => {}; } openPage() Index: ps/trunk/binaries/data/mods/public/gui/pregame/backgrounds.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/backgrounds.xml +++ ps/trunk/binaries/data/mods/public/gui/pregame/backgrounds.xml @@ -1,5 +1,5 @@ - +