Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.js
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.js
@@ -1246,6 +1246,9 @@
*/
function updateSettingsPanelPosition(offset)
{
+ if (offset === 0)
+ return;
+
let settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
let settingsPanelSize = settingsPanel.size;
settingsPanelSize.left += offset;
@@ -2407,6 +2410,40 @@
updateGUIObjects();
}
+function MapBrowserOpen()
+{
+ Engine.PushGuiPage("page_mapbrowser.xml",
+ {
+ "callback" : "MapBrowserCallback",
+ "mapSelected" : true,
+ "map" : {
+ "type" : g_GameAttributes.mapType,
+ "filter" : g_GameAttributes.mapFilter,
+ "name" : g_GameAttributes.map // includes the map path
+ }
+ });
+}
+
+/**
+ * Called after closing the dialog.
+ */
+function MapBrowserCallback(data)
+{
+ if (!g_IsController || !data.mapSelected)
+ return;
+
+ const typeId = g_Dropdowns.mapType.ids().indexOf(data.map.type);
+ g_Dropdowns.mapType.select(typeId);
+
+ const filterId = g_Dropdowns.mapFilter.ids().indexOf(data.map.filter);
+ g_Dropdowns.mapFilter.select(filterId);
+
+ const mapId = g_Dropdowns.mapSelection.ids().indexOf(data.map.path + data.map.name);
+ g_Dropdowns.mapSelection.select(mapId);
+
+ updateGUIObjects();
+}
+
function openAIConfig(playerSlot)
{
g_LastViewedAIPlayer = playerSlot;
Index: binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.xml
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.xml
@@ -139,6 +139,9 @@
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
@@ -0,0 +1,393 @@
+function GameMap(fileName, type)
+{
+ this.loaded = false;
+ this.type = type;
+ this.file = {
+ path: GameMap.types[ type ].path,
+ name: fileName,
+ extension: GameMap.types[ type ].extension
+ };
+}
+
+GameMap.types = {
+ "random":
+ {
+ "path": "maps/random/",
+ "extension": ".json"
+ },
+ "scenario":
+ {
+ "path": "maps/scenarios/",
+ "extension": ".xml"
+ },
+ "skirmish":
+ {
+ "path": "maps/skirmishes/",
+ "extension": ".xml"
+ }
+};
+
+GameMap.typesList = Object.keys(GameMap.types);
+
+GameMap.filters = {
+ "default":
+ {
+ "name": translateWithContext("map filter", "Default"),
+ "tooltip": translateWithContext("map filter", "All maps except naval and demo maps."),
+ "filter": mapFilters => mapFilters.every(filter => [ "naval", "demo", "hidden" ].indexOf(filter) == -1),
+ "Default": true
+ },
+ "naval":
+ {
+ "name": translate("Naval Maps"),
+ "tooltip": translateWithContext("map filter", "Maps where ships are needed to reach the enemy."),
+ "filter": mapFilters => mapFilters.indexOf("naval") != -1
+ },
+ "demo":
+ {
+ "name": translate("Demo Maps"),
+ "tooltip": translateWithContext("map filter", "These maps are not playable but for demonstration purposes only."),
+ "filter": mapFilters => mapFilters.indexOf("demo") != -1
+ },
+ "new":
+ {
+ "name": translate("New Maps"),
+ "tooltip": translateWithContext("map filter", "Maps that are brand new in this release of the game."),
+ "filter": mapFilters => mapFilters.indexOf("new") != -1
+ },
+ "trigger":
+ {
+ "name": translate("Trigger Maps"),
+ "tooltip": translateWithContext("map filter", "Maps that come with scripted events and potentially spawn enemy units."),
+ "filter": mapFilters => mapFilters.indexOf("trigger") != -1
+ },
+ "all":
+ {
+ "name": translate("All Maps"),
+ "tooltip": translateWithContext("map filter", "Every map of the chosen maptype."),
+ "filter": mapFilters => true
+ }
+};
+
+GameMap.filtersList = Object.keys(GameMap.filters);
+
+//returns a list of GameMap objects of all maps of given type
+GameMap.getMapsOfType = function (type)
+{
+ let maps = [];
+
+ if (!GameMap.types[ type ])
+ { return maps; }
+
+ const filePath = GameMap.types[ type ].path;
+ const fileExtension = GameMap.types[ type ].extension;
+
+ for (let fileName of listFiles(filePath, fileExtension, false))
+ {
+ if (fileName.startsWith("_"))
+ { continue; }
+ maps.push(new GameMap(fileName, type));
+ }
+ return maps;
+};
+
+GameMap.getMapsAll = function ()
+{
+ let mapList = [];
+ for (let type of GameMap.typesList)
+ mapList.push(...GameMap.getMapsOfType(type));
+ return mapList;
+};
+
+/* Returns a dictionary: each key is the filter with its corresponding map list
+ * mapList is a list of GameMap objects
+ */
+GameMap.getMapsByFilter = function (mapList)
+{
+ let mapsFiltered = {};
+ GameMap.filtersList.forEach((filter) => mapsFiltered[ filter ] = []);
+ for (let map of mapList)
+ {
+ if (!map.loaded)
+ map.load();
+
+ for (let filter of GameMap.filtersList)
+ {
+ if (GameMap.filters[ filter ].filter(map.filter))
+ mapsFiltered[ filter ].push(map);
+ }
+ }
+ return mapsFiltered;
+};
+
+// load data from settings
+GameMap.prototype.load = function ()
+{
+ this.loaded = true;
+ this.data = (this.type == "random") ?
+ Engine.ReadJSONFile(this.file.path + this.file.name + this.file.extension) :
+ Engine.LoadMapSettings(this.file.path + this.file.name);
+
+ this.loadName();
+ this.loadDescription();
+ this.loadPreview();
+ this.loadFilters();
+
+ return this.data;
+};
+
+GameMap.prototype.loadName = function ()
+{
+ return this.name = (!this.data || !this.data.settings || !this.data.settings.Name) ?
+ translate("No map name.") :
+ translate(this.data.settings.Name);
+};
+
+GameMap.prototype.loadDescription = function ()
+{
+ return this.description = (!this.data || !this.data.settings || !this.data.settings.Description) ?
+ translate("No map description.") :
+ translate(this.data.settings.Description);
+};
+
+GameMap.prototype.loadPreview = function ()
+{
+ const path = "cropped:0.78125,0.5859375:session/icons/mappreview/";
+ return this.preview = (!this.data || !this.data.settings || !this.data.settings.Preview) ?
+ path + "nopreview.png" :
+ path + this.data.settings.Preview;
+};
+
+GameMap.prototype.loadFilters = function ()
+{
+ let filter = !!this.data.settings ? this.data.settings.Keywords || [] : [];
+ if (filter.indexOf("all") == -1)
+ filter.push("all")
+ return this.filter = filter;
+}
+
+
+let mapBrowser; // GridBrowser instance, manages the maps previews
+let mapList; // Dictionary of maps by filter (of current map type list)
+let mapFilter; // List of current selected filter from mapList
+let mapFilterDropdown;
+
+let mapSelected = {};
+mapSelected.set = function (map, filter = undefined)
+{
+ this.map = map;
+ this.filter = (filter !== undefined) ? filter : mapFilterDropdown.list_data[ mapFilterDropdown.selected ];
+
+ if (!map.loaded)
+ map.load();
+
+ Engine.GetGUIObjectByName("nameSelected").caption = map.name;
+ Engine.GetGUIObjectByName("previewSelected").sprite = map.preview;
+ Engine.GetGUIObjectByName("descriptionSelected").caption = map.description;
+}
+
+function childFunction(map, pageIndex, pageList, listIndex, list, childObject)
+{
+ if (!map.loaded)
+ map.load();
+
+ const id = (name, index = pageIndex) => `${name}[${index}]`;
+
+ const image = Engine.GetGUIObjectByName(id("mapPreview"));
+ const button = Engine.GetGUIObjectByName(id("mapButton"));
+ const mapBox = Engine.GetGUIObjectByName(id("mapBox"));
+ const mapName = Engine.GetGUIObjectByName(id("mapName"));
+
+ const animeButtonSettingsSelected = {
+ "color": "255 0 0 100",
+ "size": "4 4 -4 -4"
+ };
+ const animeButtonSettingsUnselected = {
+ "color": "255 0 0 0",
+ "size": "8 8 -8 -8"
+ };
+
+ // Draw box outline if this box is the current selected map
+ if (mapSelected.map.file.name === map.file.name)
+ new AnimateGUI(button, animeButtonSettingsSelected);
+ else
+ new AnimateGUI(button, animeButtonSettingsUnselected);
+
+ mapName.caption = map.name;
+
+ image.sprite = map.preview;
+
+ button.tooltip = map.description;
+ button.onMouseLeftPress = () =>
+ {
+ mapSelected.set(map);
+
+ // if previously was selected then unselect (meaning it has a sprite)
+ if (this.getPageIndexOfSelected() != -1)
+ {
+ let lastButtonSelected = Engine.GetGUIObjectByName(id("mapButton", this.getPageIndexOfSelected()));
+ new AnimateGUI(lastButtonSelected, animeButtonSettingsUnselected);
+ }
+ new AnimateGUI(button, animeButtonSettingsSelected);
+
+ new AnimateGUI(image, {
+ "size": "3 3 -3 -3",
+ "duration": 100
+ });
+
+ this.setIndexOfSelected(listIndex)
+ };
+ button.onMouseLeftRelease = () =>
+ {
+ new AnimateGUI(image, {
+ "size": "1 1 -1 -1",
+ "duration": 100
+ });
+ };
+ button.onMouseLeftDoubleClick = () =>
+ {
+ mapSelected.set(map);
+ this.setIndexOfSelected(listIndex)
+ mapBrowserReturn(true);
+ };
+ button.onMouseEnter = () =>
+ {
+ new AnimateGUI(mapBox, { "size": "-10 -10 10 10" });
+ }
+ button.onMouseLeave = () =>
+ {
+ new AnimateGUI(mapBox, { "size": "0 0 0 0" });
+ new AnimateGUI(image, {
+ "size": "1 1 -1 -1",
+ "duration": 100
+ });
+ }
+};
+
+let mapZoom = {};
+mapZoom.originalSize = {
+ width: 400,
+ height: 300
+};
+mapZoom.levels = [ 0.35, 0.40, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.4, 3.3 ];
+mapZoom.selected = mapZoom.levels.indexOf(0.6);
+mapZoom.getLevel = function ()
+{
+ return mapZoom.levels[ mapZoom.selected ];
+}
+mapZoom.getSize = function ()
+{
+ const level = mapZoom.getLevel();
+ return {
+ width: mapZoom.originalSize.width * level,
+ height: mapZoom.originalSize.height * level
+ };
+}
+mapZoom.in = function ()
+{
+ if (mapZoom.selected == mapZoom.levels.length - 1)
+ return;
+ ++mapZoom.selected;
+ let size = mapZoom.getSize();
+ mapBrowser.setChildDimensions(size.width, size.height);
+}
+mapZoom.out = function ()
+{
+ if (mapZoom.selected == 0)
+ return;
+ --mapZoom.selected;
+ let size = mapZoom.getSize();
+ mapBrowser.setChildDimensions(size.width, size.height);
+}
+
+function init(data)
+{
+ // Sets map type, filter depending of data input
+ const type = data.mapSelected ? data.map.type : "random";
+ const filter = data.mapSelected ? data.map.filter : "default";
+
+ // Get all maps names and make lists
+ mapList = GameMap.getMapsByFilter(GameMap.getMapsOfType(type));
+ mapFilter = mapList[ filter ];
+
+ // Edge case: "random" is not a map
+ const validMapSelected = data.mapSelected && data.map.name != "random";
+
+ // Map name from gamesetup has path included
+ const fullMapName = validMapSelected ? data.map.name : mapFilter[ 0 ].file.path + mapFilter[ 0 ].file.name;
+ let mapSelectedIndex = mapFilter.map(map => map.file.path + map.file.name).indexOf(fullMapName);
+
+ // Get childen size (with zoom set)
+ const childSize = mapZoom.getSize();
+ // Set the first selected map so childFunction doesn't read an undefined value from mapSelected
+ mapSelected.set(mapFilter[ mapSelectedIndex ], filter);
+ // Create maps' grid
+ mapBrowser = new GridBrowser("MapBrowserContainer", "currentPageNum", mapFilter, mapSelectedIndex, childSize.width, childSize.height, childFunction);
+
+ // Map filter type dropdown: set list, currently selected and behaviour
+ mapFilterDropdown = Engine.GetGUIObjectByName("mapFilterDropdown");
+ mapFilterDropdown.list = GameMap.filtersList.map(filter => GameMap.filters[ filter ].name);
+ mapFilterDropdown.list_data = GameMap.filtersList;
+ mapFilterDropdown.selected = mapFilterDropdown.list_data.indexOf(filter);
+ mapFilterDropdown.onSelectionChange = () =>
+ {
+ const filter = mapFilterDropdown.list_data[ mapFilterDropdown.selected ];
+ mapFilter = mapList[ filter ];
+ mapBrowser.setList(mapFilter);
+ mapBrowser.goToPage(0);
+ };
+
+ // Map type dropdown: set list, currently selected and behaviour
+ const mapTypeDropdown = Engine.GetGUIObjectByName("mapTypeDropdown");
+ mapTypeDropdown.list = g_Settings.MapTypes.map(type => type.Title);
+ mapTypeDropdown.list_data = g_Settings.MapTypes.map(type => type.Name);
+ mapTypeDropdown.selected = mapTypeDropdown.list_data.indexOf(type);
+ mapTypeDropdown.onSelectionChange = () =>
+ {
+ const type = mapTypeDropdown.list_data[ mapTypeDropdown.selected ];
+ const filter = mapFilterDropdown.list_data[ mapFilterDropdown.selected ];
+ mapList = GameMap.getMapsByFilter(GameMap.getMapsOfType(type));
+ mapFilter = mapList[ filter ];
+ mapBrowser.setList(mapFilter);
+ mapBrowser.goToPage(0);
+ };
+
+ // Zoom buttons
+ Engine.GetGUIObjectByName("mapsZoomIn").onPress = mapZoom.in;
+ Engine.GetGUIObjectByName("mapsZoomOut").onPress = mapZoom.out;
+
+ // Tooltips
+ let goldenText = (text) => "[color=\"255 251 131\"]" + text + "[/color]";
+ Engine.GetGUIObjectByName("mapsZoomIn").tooltip = translate(goldenText("\\[MouseWheelUp]") + ": Make map previews bigger.");
+ Engine.GetGUIObjectByName("mapsZoomOut").tooltip = translate(goldenText("\\[MouseWheelDown]") + ": Make map previews smaller?");
+ Engine.GetGUIObjectByName("prevButton").tooltip = colorizeHotkey(translate("%(hotkey)s: Goes to previous page."), "tab.prev");
+ Engine.GetGUIObjectByName("nextButton").tooltip = colorizeHotkey(translate("%(hotkey)s: Goes to next page."), "tab.next");
+ Engine.GetGUIObjectByName("selectMapButton").tooltip = translate("Select map.");
+ Engine.GetGUIObjectByName("closeButton").tooltip = colorizeHotkey(translate("%(hotkey)s: Close map browser."), "cancel");
+}
+
+function mapBrowserReturn(sendSelected = false)
+{
+ // Pop the page before calling the callback, so the callback runs
+ // in the parent GUI page's context
+ const data = sendSelected ?
+ {
+ "mapSelected": true,
+ "map": {
+ "path": mapSelected.map.file.path,
+ "name": mapSelected.map.file.name,
+ "type": mapSelected.map.type,
+ "filter": mapSelected.filter
+ }
+ } :
+ {
+ "mapSelected": false
+ };
+
+ Engine.PopGuiPageCB(data);
+}
+
+function onTick()
+{
+ AnimateGUI.onTick();
+}
\ No newline at end of file
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onTick();
+
+
+
+ Map Browser
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mapZoom.in()
+ mapZoom.out()
+
+
+
+
+ mapZoom.in()
+ mapZoom.out()
+
+
+ mapBrowser.generateGrid();
+
+
+
+ mapZoom.in()
+ mapZoom.out()
+
+
+
+
+
+
+
+
+ Page:
+
+
+
+
+
+ Previous
+ mapBrowser.previousPage();
+
+
+
+ Next
+ mapBrowser.nextPage();
+
+
+
+
+
+ +
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select map
+ mapBrowserReturn(true);
+
+
+
+
+
+
+
+ Close
+ mapBrowserReturn(false);
+
+
+
+
+
+
+ mapBrowser.previousPage();
+
+
+ mapBrowser.nextPage();
+
+
+
+
Index: binaries/data/mods/public/gui/page_mapbrowser.xml
===================================================================
--- binaries/data/mods/public/gui/page_mapbrowser.xml
+++ binaries/data/mods/public/gui/page_mapbrowser.xml
@@ -0,0 +1,13 @@
+
+
+ common/modern/setup.xml
+ common/modern/styles.xml
+ common/modern/sprites.xml
+
+ common/setup.xml
+ common/sprites.xml
+ common/styles.xml
+
+ common/global.xml
+ mapbrowser/mapbrowser.xml
+