1
0
forked from 0ad/0ad

Rewrite Main Menu.

Specify menu and submenu buttons title, tooltip, hotkey and actions
comfortably in an extensible JS object and derive the GUI object
settings based on that rather than relying on complex markup alternating
with JS in the same file.
Automatically compute the position and number of submenu items instead
of letting the author manually compute and keep that in sync in various
places of the XML file.

Use object oriented programming, so that there is a strong separation of
concerns, allowing the readers and authors to only involve themselves
with the component relevant to the feature they work on, refs #5387.
Use class keyword instead of prototype keyword for the JS classes,
because that enforces a policy where no globals are inserted between
class members and informs the reader of that as soon as reading the
classes first line, anticipating fragmentation.

Group project information and community links in a more clearly arranged
JS file.
Keep and document splashscreen onTick hack from a684f7646b, #2042,
#5578.
String change: Inform instead of warn (deter) about the state of the
game in the main menu.
Depends on Engine.SetGlobalHotkey from D2260.

Differential Revision: https://code.wildfiregames.com/D2240
Class syntax comments by: Krinkle, bb, Chakakhan, smiley, nani, fpre
Main menu comments by: Krinkle, nani, Stan (and Imarok in D820)
This was SVN commit r22854.
This commit is contained in:
elexis 2019-09-06 01:20:46 +00:00
parent afaa4417e4
commit adf448db4d
12 changed files with 594 additions and 818 deletions

View File

@ -8,14 +8,14 @@
==========================================
-->
<sprite name="productLogo">
<sprite name="0ADLogo">
<image texture="pregame/shell/logo/0ad_logo.png"
real_texture_placement="0 0 256 128"
size="0 -5% 100% 105%"
/>
</sprite>
<sprite name="companyLogo">
<sprite name="WildfireGamesLogo">
<image texture="pregame/shell/logo/wfg_logo_white.png"
real_texture_placement="0 0 32 32"
/>

View File

@ -0,0 +1,48 @@
class BackgroundHandler
{
constructor(layers)
{
this.initTime = Date.now();
this.layerSet = layers;
this.initBackgrounds();
}
initBackgrounds()
{
this.layerSet.forEach((layer, i) => {
let background = Engine.GetGUIObjectByName("background[" + i + "]");
background.sprite = layer.sprite;
background.z = i;
background.hidden = false;
});
}
onTick()
{
let now = Date.now();
this.layerSet.forEach((layer, i) => {
let background = Engine.GetGUIObjectByName("background[" + i + "]");
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);
});
}
}

View File

@ -0,0 +1,108 @@
class MainMenuItemHandler
{
constructor(menuItems, menuSpeed = 1.2, margin = 4, buttonHeight = 28)
{
this.menuItems = menuItems;
this.menuSpeed = menuSpeed;
this.margin = margin;
this.buttonHeight = buttonHeight;
this.lastTickTime = Date.now();
this.mainMenu = Engine.GetGUIObjectByName("mainMenu");
this.mainMenuButtons = Engine.GetGUIObjectByName("mainMenuButtons");
this.submenu = Engine.GetGUIObjectByName("submenu");
this.submenuButtons = Engine.GetGUIObjectByName("submenuButtons");
this.MainMenuPanelRightBorderTop = Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop");
this.MainMenuPanelRightBorderBottom = Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom");
this.setupMenuButtons(this.mainMenuButtons.children, this.menuItems);
this.setupHotkeys(this.menuItems);
Engine.GetGUIObjectByName("closeMenuButton").onPress = () => { this.closeSubmenu(); };
}
setupMenuButtons(buttons, menuItems)
{
buttons.forEach((button, i) => {
let item = menuItems[i];
button.hidden = !item;
if (button.hidden)
return;
button.size = new GUISize(
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;
button.enabled = item.enabled === undefined || item.enabled;
button.onPress = () => {
this.closeSubmenu();
if (item.onPress)
item.onPress();
else
this.openSubmenu(i);
};
button.hidden = false;
});
if (buttons.length < menuItems.length)
error("GUI page has space for " + buttons.length + " menu buttons, but " + menuItems.length + " items are provided!");
}
setupHotkeys(menuItems)
{
for (let name in menuItems)
{
let item = menuItems[name];
if (item.onPress && item.hotkey)
{
Engine.SetGlobalHotkey(item.hotkey, () => {
this.closeSubmenu();
item.onPress();
});
}
if (item.submenu)
this.setupHotkeys(item.submenu);
}
}
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.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%";
}
closeSubmenu()
{
this.submenu.hidden = true;
this.submenu.size = this.mainMenu.size;
this.MainMenuPanelRightBorderTop.size = "100%-2 0 100% 100%";
}
onTick()
{
let now = Date.now();
let maxOffset = this.mainMenu.size.right - this.submenu.size.left;
let offset = Math.min(this.menuSpeed * (now - this.lastTickTime), maxOffset);
this.lastTickTime = now;
if (this.submenu.hidden || offset <= 0)
return;
let size = this.submenu.size;
size.left += offset;
size.right += offset;
this.submenu.size = size;
}
}

View File

@ -0,0 +1,194 @@
var g_MainMenuItems = [
{
"caption": translate("Learn To Play"),
"tooltip": translate("Learn how to play, start the tutorial, discover the technology trees, and the history behind the civilizations"),
"submenu": [
{
"caption": translate("Manual"),
"tooltip": translate("Open the 0 A.D. Game Manual."),
"onPress": () => {
Engine.PushGuiPage("page_manual.xml");
}
},
{
"caption": translate("Tutorial"),
"tooltip": translate("Start the economic tutorial."),
"onPress": () => {
Engine.SwitchGuiPage("page_gamesetup.xml", {
"tutorial": true
});
}
},
{
"caption": translate("Structure Tree"),
"tooltip": colorizeHotkey(translate("%(hotkey)s: View the structure tree of civilizations featured in 0 A.D."), "structree"),
"hotkey": "structree",
"onPress": () => {
Engine.PushGuiPage("page_structree.xml");
}
},
{
"caption": translate("History"),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Learn about the many civilizations featured in 0 A.D."), "civinfo"),
"hotkey": "civinfo",
"onPress": () => {
Engine.PushGuiPage("page_civinfo.xml");
}
}
]
},
{
"caption": translate("Single Player"),
"tooltip": translate("Click here to start a new single player game."),
"submenu": [
{
"caption": translate("Matches"),
"tooltip": translate("Start the economic tutorial."),
"onPress": () => {
Engine.SwitchGuiPage("page_gamesetup.xml", {});
}
},
{
"caption": translate("Campaigns"),
"tooltip": translate("Relive history through historical military campaigns. \\[NOT YET IMPLEMENTED]"),
"enabled": false
},
{
"caption": translate("Load Game"),
"tooltip": translate("Click here to load a saved game."),
"onPress": () => {
Engine.PushGuiPage("page_loadgame.xml", {
"type": "offline"
});
}
},
{
"caption": translate("Replays"),
"tooltip": translate("Playback previous games."),
"onPress": () => {
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Singleplayer"
}
}
});
}
}
]
},
{
"caption": translate("Multiplayer"),
"tooltip": translate("Fight against one or more human players in a multiplayer game."),
"submenu": [
{
// Translation: Join a game by specifying the host's IP address.
"caption": translate("Join Game"),
"tooltip": translate("Joining an existing multiplayer game."),
"onPress": () => {
Engine.PushGuiPage("page_gamesetup_mp.xml", {
"multiplayerGameType": "join"
});
}
},
{
"caption": translate("Host Game"),
"tooltip": translate("Host a multiplayer game."),
"onPress": () => {
Engine.PushGuiPage("page_gamesetup_mp.xml", {
"multiplayerGameType": "host"
});
}
},
{
"caption": translate("Game Lobby"),
"tooltip":
colorizeHotkey(translate("%(hotkey)s: Launch the multiplayer lobby to join and host publicly visible games and chat with other players."), "lobby") +
(Engine.StartXmppClient ? "" : translate("Launch the multiplayer lobby. \\[DISABLED BY BUILD]")),
"enabled": !!Engine.StartXmppClient,
"hotkey": "lobby",
"onPress": () => {
if (Engine.StartXmppClient)
Engine.PushGuiPage("page_prelobby_entrance.xml");
}
},
{
"caption": translate("Replays"),
"tooltip": translate("Playback previous games."),
"onPress": () => {
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Multiplayer"
}
}
});
}
}
]
},
{
"caption": translate("Settings"),
"tooltip": translate("Game options and scenario design tools."),
"submenu": [
{
"caption": translate("Options"),
"tooltip": translate("Adjust game settings."),
"onPress": () => {
Engine.PushGuiPage("page_options.xml");
}
},
{
"caption": translate("Language"),
"tooltip": translate("Choose the language of the game."),
"onPress": () => {
Engine.PushGuiPage("page_locale.xml");
}
},
{
"caption": translate("Mod Selection"),
"tooltip": translate("Select and download mods for the game."),
"onPress": () => {
Engine.SwitchGuiPage("page_modmod.xml");
}
},
{
"caption": translate("Welcome Screen"),
"tooltip": translate("Show the Welcome Screen. Useful if you hid it by mistake."),
"onPress": () => {
Engine.PushGuiPage("page_splashscreen.xml");
}
}
]
},
{
"caption": translate("Scenario Editor"),
"tooltip": translate('Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument "-editor".'),
"onPress": () => {
if (Engine.AtlasIsAvailable())
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D. and open the Scenario Editor?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.RestartInAtlas]);
else
messageBox(
400, 200,
translate("The scenario editor is not available or failed to load. See the game logs for additional information."),
translate("Error"));
}
},
{
"caption": translate("Exit"),
"tooltip": translate("Exits the game."),
"onPress": () => {
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D.?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.Exit]);
}
}
];

View File

@ -0,0 +1,72 @@
/**
* IMPORTANT: Remember to update session/top_panel/label.xml in sync with this.
*/
var g_ProjectInformation = {
"organizationName": {
"caption": translate("WILDFIRE GAMES")
},
"organizationLogo": {
"sprite": "WildfireGamesLogo"
},
"productLogo": {
"sprite": "0ADLogo"
},
"productBuild": {
"caption": getBuildString()
},
"productDescription": {
"caption": setStringTags(translate("Alpha XXIV"), { "font": "sans-bold-16" }) + "\n\n" +
translate("Notice: This game is under development and many features have not been added yet.")
}
};
var g_CommunityButtons = [
{
"caption": translate("Website"),
"tooltip": translate("Click to open play0ad.com in your web browser."),
"size": "8 100%-180 50%-4 100%-152",
"onPress": () => {
openURL("https://play0ad.com/");
}
},
{
"caption": translate("Chat"),
"tooltip": translate("Click to open the 0 A.D. IRC chat in your browser. (#0ad on webchat.quakenet.org)"),
"size": "50%+4 100%-180 100%-8 100%-152",
"onPress": () => {
openURL("https://webchat.quakenet.org/?channels=0ad");
}
},
{
"caption": translate("Report a Bug"),
"tooltip": translate("Click to visit 0 A.D. Trac to report a bug, crash, or error."),
"size": "8 100%-144 100%-8 100%-116",
"onPress": () => {
openURL("https://trac.wildfiregames.com/wiki/ReportingErrors/");
}
},
{
"caption": translate("Translate the Game"),
"tooltip": translate("Click to open the 0 A.D. translate page in your browser."),
"size": "8 100%-108 100%-8 100%-80",
"onPress": () => {
openURL("https://trac.wildfiregames.com/wiki/Localization");
}
},
{
"caption": translate("Donate"),
"tooltip": translate("Help with the project expenses by donating."),
"size": "8 100%-72 100%-8 100%-44",
"onPress": () => {
openURL("https://play0ad.com/community/donate/");
}
},
{
"caption": translate("Credits"),
"tooltip": translate("Click to see the 0 A.D. credits."),
"size": "8 100%-36 100%-8 100%-8",
"onPress": () => {
Engine.PushGuiPage("page_credits.xml");
}
}
];

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<object>
<object name="productLogo" type="image" ghost="true" size="50%-110 30 50%+110 130"/>
<object type="image" style="TranslucentPanelThinBorder" size="8 100%-420 100%-8 100%-94">
<object name="productDescription" font="sans-14" type="text" textcolor="white" size="8 8 100%-8 100%-36" ghost="true"/>
<object name="communityButtons">
<repeat count="10">
<object type="button" style="StoneButton" tooltip_style="pgToolTip" hidden="true"/>
</repeat>
</object>
</object>
<object size="4 100%-84 100%-4 100%-56" ghost="true">
<object name="organizationLogo" type="image" ghost="true" size="50%-16 0 50%+16 32"/>
<object name="organizationName" type="text" style="MediumTitleText" ghost="true" size="50%-128 32 50%+128 48"/>
</object>
<object type="text" name="productBuild" style="MediumTitleText" ghost="true" size="50%-128 100%-36 50%+128 100%"/>
</object>

View File

@ -0,0 +1,62 @@
class SplashScreenHandler
{
constructor(initData, hotloadData)
{
this.showSplashScreen = hotloadData ? hotloadData.showSplashScreen : initData && initData.isStartup;
}
getHotloadData()
{
// Only show splash screen(s) once at startup, but not again after hotloading
return {
"showSplashScreen": this.showSplashScreen
};
}
// 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()
{
if (this.showSplashScreen)
this.openPage();
}
openPage()
{
this.showSplashScreen = false;
if (Engine.ConfigDB_GetValue("user", "gui.splashscreen.enable") === "true" ||
Engine.ConfigDB_GetValue("user", "gui.splashscreen.version") < Engine.GetFileMTime("gui/splashscreen/splashscreen.txt"))
Engine.PushGuiPage("page_splashscreen.xml", {}, this.showRenderPathMessage);
else
this.showRenderPathMessage();
}
showRenderPathMessage()
{
// Warn about removing fixed render path
if (Engine.Renderer_GetRenderPath() != "fixed")
return;
messageBox(
600, 300,
"[font=\"sans-bold-16\"]" +
sprintf(translate("%(warning)s You appear to be using non-shader (fixed function) graphics. This option will be removed in a future 0 A.D. release, to allow for more advanced graphics features. We advise upgrading your graphics card to a more recent, shader-compatible model."), {
"warning": coloredText("Warning:", "200 20 20")
}) +
"\n\n" +
// Translation: This is the second paragraph of a warning. The
// warning explains that the user is using “non-shader“ graphics,
// and that in the future this will not be supported by the game, so
// the user will need a better graphics card.
translate("Please press \"Read More\" for more information or \"OK\" to continue."),
translate("WARNING!"),
[translate("OK"), translate("Read More")],
[
null,
() => {
Engine.OpenURL("https://www.wildfiregames.com/forum/index.php?showtopic=16734");
}
]);
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<object ghost="true" z="1">
<repeat count="20">
<object name="background[n]" type="image" hidden="true" ghost="true"/>
</repeat>
</object>

View File

@ -1,244 +1,70 @@
var currentSubmenuType; // contains submenu type
var MARGIN = 4; // menu border size
var g_ShowSplashScreens;
/**
* Available backdrops
* Available backgrounds, added by the files in backgrounds/.
*/
var g_BackgroundLayerData = [];
/**
* Chosen backdrop
*/
var g_BackgroundLayerset;
var g_BackgroundHandler;
var g_MenuHandler;
var g_SplashScreenHandler;
var g_T0 = Date.now();
var g_LastTickTime = Date.now();
function init(initData, hotloadData)
function init(data, hotloadData)
{
initMusic();
g_MenuHandler = new MainMenuItemHandler(g_MainMenuItems);
g_BackgroundHandler = new BackgroundHandler(pickRandom(g_BackgroundLayerData));
g_SplashScreenHandler = new SplashScreenHandler(data, hotloadData && hotloadData.splashScreenHandler);
global.music.setState(global.music.states.MENU);
// Initialize currentSubmenuType with placeholder to avoid null when switching
currentSubmenuType = "submenuSinglePlayer";
// Only show splash screen(s) once at startup, but not again after hotloading
g_ShowSplashScreens = hotloadData ? hotloadData.showSplashScreens : initData && initData.isStartup;
// Pick a random background and initialise it
g_BackgroundLayerset = pickRandom(g_BackgroundLayerData);
for (let i = 0; i < g_BackgroundLayerset.length; ++i)
{
let guiObj = Engine.GetGUIObjectByName("background[" + i + "]");
guiObj.hidden = false;
guiObj.sprite = g_BackgroundLayerset[i].sprite;
guiObj.z = i;
}
Engine.GetGUIObjectByName("structreeButton").tooltip = colorizeHotkey(
translate("%(hotkey)s: View the structure tree of civilizations featured in 0 A.D."),
"structree");
Engine.GetGUIObjectByName("civInfoButton").tooltip = colorizeHotkey(
translate("%(hotkey)s: Learn about the many civilizations featured in 0 A.D."),
"civinfo");
Engine.GetGUIObjectByName("lobbyButton").tooltip = colorizeHotkey(
translate("%(hotkey)s: Launch the multiplayer lobby to join and host publicly visible games and chat with other players."),
"lobby");
}
function getHotloadData()
{
return { "showSplashScreens": g_ShowSplashScreens };
}
function scrollBackgrounds()
{
for (let i = 0; i < g_BackgroundLayerset.length; ++i)
{
let guiObj = Engine.GetGUIObjectByName("background[" + i + "]");
let screen = guiObj.parent.getComputedSize();
let h = screen.bottom - screen.top;
let w = h * 16/9;
let iw = h * 2;
let offset = g_BackgroundLayerset[i].offset((Date.now() - g_T0) / 1000, w);
if (g_BackgroundLayerset[i].tiling)
{
let left = offset % iw;
if (left >= 0)
left -= iw;
guiObj.size = new GUISize(left, screen.top, screen.right, screen.bottom);
}
else
guiObj.size = new GUISize(screen.right/2 - h + offset, screen.top, screen.right/2 + h + offset, screen.bottom);
}
new MusicHandler();
new ProjectInformationHandler(g_ProjectInformation);
new CommunityButtonHandler();
}
function onTick()
{
let now = Date.now();
let tickLength = Date.now() - g_LastTickTime;
g_LastTickTime = now;
g_MenuHandler.onTick();
g_BackgroundHandler.onTick();
g_SplashScreenHandler.onTick();
}
scrollBackgrounds();
function getHotloadData()
{
return {
"splashScreenHandler": g_SplashScreenHandler.getHotloadData()
};
}
updateMenuPosition(tickLength);
// Show splash screens here, so we don't interfere with main menu hotloading
if (g_ShowSplashScreens)
class MusicHandler
{
constructor()
{
g_ShowSplashScreens = false;
if (Engine.ConfigDB_GetValue("user", "gui.splashscreen.enable") === "true" ||
Engine.ConfigDB_GetValue("user", "gui.splashscreen.version") < Engine.GetFileMTime("gui/splashscreen/splashscreen.txt"))
ShowSplashScreen();
else
ShowRenderPathMessage();
initMusic();
global.music.setState(global.music.states.MENU);
}
}
function ShowSplashScreen()
class ProjectInformationHandler
{
Engine.PushGuiPage("page_splashscreen.xml", {}, ShowRenderPathMessage);
}
function ShowRenderPathMessage()
{
// Warn about removing fixed render path
if (Engine.Renderer_GetRenderPath() == "fixed")
messageBox(
600, 300,
"[font=\"sans-bold-16\"]" +
sprintf(translate("%(warning)s You appear to be using non-shader (fixed function) graphics. This option will be removed in a future 0 A.D. release, to allow for more advanced graphics features. We advise upgrading your graphics card to a more recent, shader-compatible model."), {
"warning": coloredText("Warning:", "200 20 20")
}) +
"\n\n" +
// Translation: This is the second paragraph of a warning. The
// warning explains that the user is using “non-shader“ graphics,
// and that in the future this will not be supported by the game, so
// the user will need a better graphics card.
translate("Please press \"Read More\" for more information or \"OK\" to continue."),
translate("WARNING!"),
[translate("OK"), translate("Read More")],
[ null, function() { Engine.OpenURL("https://www.wildfiregames.com/forum/index.php?showtopic=16734"); } ]
);
}
/**
* Slide menu.
*/
function updateMenuPosition(dt)
{
let submenu = Engine.GetGUIObjectByName("submenu");
if (submenu.hidden == false)
constructor(projectInformation)
{
// Number of pixels per millisecond to move
let SPEED = 1.2;
let maxOffset = Engine.GetGUIObjectByName("mainMenu").size.right - submenu.size.left;
if (maxOffset > 0)
{
let offset = Math.min(SPEED * dt, maxOffset);
let size = submenu.size;
size.left += offset;
size.right += offset;
submenu.size = size;
}
for (let objectName in projectInformation)
for (let propertyName in projectInformation[objectName])
Engine.GetGUIObjectByName(objectName)[propertyName] = projectInformation[objectName][propertyName];
}
}
/**
* Opens the menu by revealing the screen which contains the menu.
*/
function openMenu(newSubmenu, position, buttonHeight, numButtons)
class CommunityButtonHandler
{
currentSubmenuType = newSubmenu;
Engine.GetGUIObjectByName(currentSubmenuType).hidden = false;
constructor()
{
let buttons = Engine.GetGUIObjectByName("communityButtons").children;
let submenu = Engine.GetGUIObjectByName("submenu");
let top = position - MARGIN;
let bottom = position + ((buttonHeight + MARGIN) * numButtons);
submenu.size = new GUISize(submenu.size.left, top, submenu.size.right, bottom);
g_CommunityButtons.forEach((buttonInfo, i) => {
let button = buttons[i];
button.hidden = false;
for (let propertyName in buttonInfo)
button[propertyName] = buttonInfo[propertyName];
});
// Blend in right border of main menu into the left border of the submenu
blendSubmenuIntoMain(top, bottom);
submenu.hidden = false;
}
function closeMenu()
{
Engine.GetGUIObjectByName(currentSubmenuType).hidden = true;
let submenu = Engine.GetGUIObjectByName("submenu");
submenu.hidden = true;
submenu.size = Engine.GetGUIObjectByName("mainMenu").size;
Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop").size = "100%-2 0 100% 100%";
}
/**
* Sizes right border on main menu panel to match the submenu.
*/
function blendSubmenuIntoMain(topPosition, bottomPosition)
{
Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop").size = "100%-2 0 100% " + (topPosition + MARGIN);
Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom").size = "100%-2 " + (bottomPosition) + " 100% 100%";
}
function exitGamePressed()
{
closeMenu();
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D.?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.Exit]
);
}
function pressedScenarioEditorButton()
{
closeMenu();
if (Engine.AtlasIsAvailable())
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D. and open the Scenario Editor?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.RestartInAtlas]
);
else
messageBox(
400, 200,
translate("The scenario editor is not available or failed to load. See the game logs for additional information."),
translate("Error")
);
}
function getLobbyDisabledByBuild()
{
return translate("Launch the multiplayer lobby to join and host publicly visible games and chat with other players. \\[DISABLED BY BUILD]");
}
function openStrucTree(page)
{
closeMenu();
Engine.PushGuiPage(page, {}, storeCivInfoPage);
}
function storeCivInfoPage(data)
{
if (data.nextPage)
Engine.PushGuiPage(data.nextPage, { "civ": data.civ }, storeCivInfoPage);
if (buttons.length < g_CommunityButtons.length)
error("GUI page has space for " + buttons.length + " community buttons, but " + menuItems.length + " items are provided!");
}
}

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script directory="gui/common/"/>
@ -7,605 +6,12 @@
<script directory="gui/pregame/backgrounds/"/>
<script directory="gui/pregame/userreport/"/>
<!--
==========================================
- MAIN MENU
==========================================
-->
<object>
<action on="Tick">
onTick();
</action>
<repeat count="5">
<object name="background[n]"
type="image"
hidden="true"
ghost="true"
/>
</repeat>
<!--
==========================================
- MAIN MENU - STATIC TOOLTIP WINDOW
==========================================
-->
<object name="pgToolTip"
style="TranslucentPanel"
type="text"
hidden="true"
size="100%-290 25 100% 140"
ghost="true"
/>
<!--
==========================================
- MAIN MENU - USER REPORT
==========================================
-->
<include file="gui/pregame/backgrounds.xml"/>
<include file="gui/pregame/menupanel.xml"/>
<include file="gui/pregame/userreport/userreport.xml"/>
<!--
==========================================
- SUBMENU
==========================================
-->
<object hotkey="cancel">
<action on="Press">
closeMenu();
</action>
</object>
<!-- hide submenu when user clicks on the background -->
<object type="button">
<action on="Press">
closeMenu();
</action>
</object>
<!-- submenu -->
<object name="submenu"
type="image"
style="SubmenuPanel"
size="60 50%-100 300 50%+100"
hidden="true"
>
<!-- submenuLearnToPlay -->
<object name="submenuLearn"
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
hidden="true"
>
<!-- LEARN TO PLAY BUTTON -->
<object
type="button"
style="StoneButtonFancy"
size="0 0 100% 28"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Manual</translatableAttribute>
<translatableAttribute id="tooltip">Open the 0 A.D. Game Manual.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_manual.xml");
</action>
</object>
<!-- START TUTORIAL BUTTON -->
<object
type="button"
style="StoneButtonFancy"
size="0 32 100% 60"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Tutorial</translatableAttribute>
<translatableAttribute id="tooltip">Start the economic tutorial.</translatableAttribute>
<action on="Press">
Engine.SwitchGuiPage("page_gamesetup.xml", { "tutorial": true });
</action>
</object>
<!-- STRUCTREE BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="0 64 100% 92"
tooltip_style="pgToolTip"
name="structreeButton"
hotkey="structree"
>
<translatableAttribute id="caption">Structure Tree</translatableAttribute>
<action on="Press">openStrucTree("page_structree.xml");</action>
</object>
<!-- HISTORY BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="0 96 100% 124"
tooltip_style="pgToolTip"
name="civInfoButton"
hotkey="civinfo"
>
<translatableAttribute id="caption">History</translatableAttribute>
<action on="Press">openStrucTree("page_civinfo.xml");</action>
</object>
</object>
<!-- submenuSinglePlayer -->
<object name="submenuSinglePlayer"
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
hidden="true"
>
<object
type="button"
style="StoneButtonFancy"
size="0 0 100% 28"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Matches</translatableAttribute>
<translatableAttribute id="tooltip">Click here to start a new single player game.</translatableAttribute>
<action on="Press">
Engine.SwitchGuiPage("page_gamesetup.xml", {});
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 32 100% 60"
tooltip_style="pgToolTip"
enabled="false"
>
<translatableAttribute id="caption">Campaigns</translatableAttribute>
<translatableAttribute id="tooltip">Relive history through historical military campaigns. \[NOT YET IMPLEMENTED]</translatableAttribute>
<action on="Press">
closeMenu();
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 64 100% 92"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Load Game</translatableAttribute>
<translatableAttribute id="tooltip">Click here to load a saved game.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_loadgame.xml", { "type": "offline" });
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 96 100% 124"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Replays</translatableAttribute>
<translatableAttribute id="tooltip">Playback previous games.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Singleplayer"
}
}
});
</action>
</object>
</object>
<!-- submenuMultiplayer -->
<object name="submenuMultiplayer"
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
hidden="true"
>
<object
type="button"
style="StoneButtonFancy"
size="0 0 100% 28"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption" comment="Join a game by specifying the host's IP address.">Join Game</translatableAttribute>
<translatableAttribute id="tooltip">Joining an existing multiplayer game.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_gamesetup_mp.xml", { "multiplayerGameType": "join" });
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 32 100% 60"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Host Game</translatableAttribute>
<translatableAttribute id="tooltip">Host a multiplayer game.\n\nRequires UDP port 20595 to be open.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host" });
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 64 100% 92"
tooltip_style="pgToolTip"
name="lobbyButton"
hotkey="lobby"
>
<translatableAttribute id="caption">Game Lobby</translatableAttribute>
<action on="Press">
if (!Engine.StartXmppClient)
return;
closeMenu();
Engine.PushGuiPage("page_prelobby_entrance.xml");
</action>
<action on="load">
if (!Engine.StartXmppClient)
{
this.enabled = false;
this.tooltip = getLobbyDisabledByBuild();
}
</action>
</object>
<object
type="button"
style="StoneButtonFancy"
size="0 96 100% 124"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Replays</translatableAttribute>
<translatableAttribute id="tooltip">Playback previous games.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Multiplayer"
}
}
});
</action>
</object>
</object>
<!-- submenuOptions -->
<object name="submenuOptions"
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
hidden="true"
>
<object
style="StoneButtonFancy"
type="button"
size="0 0 100% 28"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Options</translatableAttribute>
<translatableAttribute id="tooltip">Adjust game settings.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_options.xml");
</action>
</object>
<object
style="StoneButtonFancy"
type="button"
size="0 32 100% 60"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Language</translatableAttribute>
<translatableAttribute id="tooltip">Choose the language of the game.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_locale.xml");
</action>
</object>
<object
style="StoneButtonFancy"
type="button"
size="0 64 100% 92"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Mod Selection</translatableAttribute>
<translatableAttribute id="tooltip">Select and download mods for the game.</translatableAttribute>
<action on="Press">
Engine.SwitchGuiPage("page_modmod.xml");
</action>
</object>
<object
style="StoneButtonFancy"
type="button"
size="0 96 100% 124"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Welcome Screen</translatableAttribute>
<translatableAttribute id="tooltip">Show the Welcome Screen. Useful if you hid it by mistake.</translatableAttribute>
<action on="Press">
closeMenu();
ShowSplashScreen();
</action>
</object>
</object>
</object>
<!--
==========================================
- MAIN MENU PANEL
==========================================
-->
<object name="mainMenu"
type="image"
style="MainMenuPanel"
size="60 -2 300 100%+2"
z="50"
>
<!-- These are used to make the right side blend in with the submenu -->
<object name="MainMenuPanelRightBorderTop"
type="image"
sprite="MainMenuPanelRightBorder"
ghost="true"
size="100%-2 0 100% 100%"
/>
<object name="MainMenuPanelRightBorderBottom"
type="image"
sprite="MainMenuPanelRightBorder"
ghost="true"
size="100%-2 0 100% 100%"
/>
<object
type="image"
sprite="productLogo"
ghost="true"
size="50%-110 30 50%+110 130"
/>
<object
type="image"
size="8 156 100%-8 356"
ghost="false"
>
<!-- LEARN BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="4 4 100%-4 32"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Learn to Play</translatableAttribute>
<translatableAttribute id="tooltip">Learn how to play, start the tutorial, discover the technology trees, and the history behind the civilizations</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuLearn", this.parent.size.top + this.size.top, this.size.bottom - this.size.top, 4);
</action>
</object>
<!-- SINGLEPLAYER BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="4 36 100%-4 64"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Single Player</translatableAttribute>
<translatableAttribute id="tooltip">Challenge the computer player to a single player match.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuSinglePlayer", this.parent.size.top + this.size.top, this.size.bottom - this.size.top, 4);
</action>
</object>
<!-- MULTIPLAYER BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="4 68 100%-4 96"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Multiplayer</translatableAttribute>
<translatableAttribute id="tooltip">Fight against one or more human players in a multiplayer game.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuMultiplayer", this.parent.size.top + this.size.top, this.size.bottom - this.size.top, 4);
</action>
</object>
<!-- OPTIONS BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="4 100 100%-4 128"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Settings</translatableAttribute>
<translatableAttribute id="tooltip">Game options and mod selection.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuOptions", this.parent.size.top + this.size.top, this.size.bottom - this.size.top, 4);
</action>
</object>
<!-- SCENARIO EDITOR BUTTON -->
<object
style="StoneButtonFancy"
type="button"
size="4 132 100%-4 160"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Scenario Editor</translatableAttribute>
<translatableAttribute id="tooltip">Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument &quot;-editor&quot;.</translatableAttribute>
<action on="Press">
pressedScenarioEditorButton();
</action>
</object>
<!-- EXIT BUTTON -->
<object
type="button"
style="StoneButtonFancy"
size="4 164 100%-4 192"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Exit</translatableAttribute>
<translatableAttribute id="tooltip">Exits the game.</translatableAttribute>
<action on="Press">exitGamePressed();</action>
</object>
</object>
<!-- PRE-RELEASE INFO -->
<object size="8 100%-420 100%-8 100%-94"
type="image"
style="TranslucentPanelThinBorder"
>
<!-- PRE-RELEASE WARNING -->
<object
font="sans-14"
type="text"
textcolor="white"
size="8 8 100%-8 100%-36"
ghost="true"
>
<!-- IMPORTANT: remember to update session/top_panel/label.xml in sync with this: -->
<attribute id="caption">
<keep>[font="sans-bold-16"]</keep>
<translate>Alpha XXIV</translate>
<keep>[/font]\n\n</keep>
<translate>WARNING: This is an early development version of the game. Many features have not been added yet.</translate>
</attribute>
</object>
<!-- GET INVOLVED -->
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="8 100%-180 50%-4 100%-152"
>
<translatableAttribute id="caption">Website</translatableAttribute>
<translatableAttribute id="tooltip">Click to open play0ad.com in your web browser.</translatableAttribute>
<action on="Press">
openURL("https://play0ad.com/");
</action>
</object>
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="50%+4 100%-180 100%-8 100%-152"
>
<translatableAttribute id="caption">Chat</translatableAttribute>
<translatableAttribute id="tooltip">Click to open the 0 A.D. IRC chat in your browser. (#0ad on webchat.quakenet.org)</translatableAttribute>
<action on="Press">
openURL("https://webchat.quakenet.org/?channels=0ad");
</action>
</object>
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="8 100%-144 100%-8 100%-116"
>
<translatableAttribute id="caption">Report a Bug</translatableAttribute>
<translatableAttribute id="tooltip">Click to visit 0 A.D. Trac to report a bug, crash, or error.</translatableAttribute>
<action on="Press">
openURL("https://trac.wildfiregames.com/wiki/ReportingErrors/");
</action>
</object>
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="8 100%-108 100%-8 100%-80"
>
<translatableAttribute id="caption">Translate the Game</translatableAttribute>
<translatableAttribute id="tooltip">Click to open the 0 A.D. translate page in your browser.</translatableAttribute>
<action on="Press">
openURL("https://trac.wildfiregames.com/wiki/Localization");
</action>
</object>
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="8 100%-72 100%-8 100%-44"
>
<translatableAttribute id="caption">Donate</translatableAttribute>
<translatableAttribute id="tooltip">Help with the project expenses by donating.</translatableAttribute>
<action on="Press">
openURL("https://play0ad.com/community/donate/");
</action>
</object>
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
size="8 100%-36 100%-8 100%-8"
>
<translatableAttribute id="caption">Credits</translatableAttribute>
<translatableAttribute id="tooltip">Click to see the 0 A.D. credits.</translatableAttribute>
<action on="Press">
Engine.PushGuiPage("page_credits.xml");
</action>
</object>
</object>
<!-- LOGO AND BUILD VERSION -->
<object
size="4 100%-84 100%-4 100%-56"
ghost="true"
>
<!-- COMPANY LOGO -->
<object
type="image"
sprite="companyLogo"
ghost="true"
size="50%-16 0 50%+16 32"
/>
<!-- COMPANY NAME -->
<object type="text"
style="MediumTitleText"
ghost="true"
size="50%-128 32 50%+128 48"
>
<translatableAttribute id="caption">WILDFIRE GAMES</translatableAttribute>
</object>
</object>
<!-- VERSION -->
<object
type="text"
style="MediumTitleText"
ghost="true"
size="50%-128 100%-36 50%+128 100%"
>
<action on="Load">
this.caption = getBuildString();
</action>
</object>
</object>
<action on="Tick">onTick();</action>
</object>
</objects>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<object>
<object name="pgToolTip" style="TranslucentPanel" type="text" hidden="true" size="100%-290 25 100% 140" ghost="true"/>
<object name="mainMenu" type="image" style="MainMenuPanel" size="60 -2 300 100%+2" z="50">
<object name="mainMenuButtons" size="8 156 100%-8 356">
<repeat count="10">
<object name="mainMenuButton[n]" type="button" style="StoneButtonFancy" tooltip_style="pgToolTip"/>
</repeat>
</object>
<!-- These are used to make the right side blend in with the submenu -->
<object name="MainMenuPanelRightBorderTop" type="image" sprite="MainMenuPanelRightBorder" ghost="true" size="100%-2 0 100% 100%"/>
<object name="MainMenuPanelRightBorderBottom" type="image" sprite="MainMenuPanelRightBorder" ghost="true" size="100%-2 0 100% 100%"/>
<include file="gui/pregame/ProjectInformation.xml"/>
</object>
<object name="submenu" type="image" style="SubmenuPanel" size="60 0 300 0%" hidden="true">
<object name="submenuButtons" type="image" size="0 4 100%-4 100%-4" tooltip_style="pgToolTip">
<repeat count="30">
<object type="button" style="StoneButtonFancy" tooltip_style="pgToolTip"/>
</repeat>
</object>
</object>
<!-- Hide submenu when user clicks on the background -->
<object name="closeMenuButton" type="button" hotkey="cancel"/>
</object>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<object size="50%+20 0 100%-226 100%" name="alphaLabel" type="text" style="ModernLabelText" text_valign="top" ghost="true">
<!-- IMPORTANT: remember to update pregame/mainmenu.xml in sync with this: -->
<!-- IMPORTANT: remember to update pregame/ProjectInformation.js in sync with this: -->
<translatableAttribute id="caption">ALPHA XXIV</translatableAttribute>
<!-- Displays build date and revision number-->