Page MenuHomeWildfire Games
Paste P137

gridBrowser + mapbrowser update + semi functional map browser diff
ActivePublic

Authored by nani on Oct 16 2018, 11:44 PM.
Index: binaries/data/mods/public/gui/common/GridBrowser.js
===================================================================
--- binaries/data/mods/public/gui/common/GridBrowser.js (nonexistent)
+++ binaries/data/mods/public/gui/common/GridBrowser.js (working copy)
@@ -0,0 +1,197 @@
+/*
+ * Function that arranges a list of "boxed-like" xml objects with centered and page based way mode.
+ * Needs an object as container and a object to display the page numbering(if not make hidden object and assign it to it)
+ * childWidth,childHeight: dimensions of each grid box
+ * The grid will ajust in accordance to the screen size from when init() method is called
+ *
+ * To add advanced, more detailed, objects inside each box redefine
+ * the function "defineChildren" after the your GridBrowser is instantiated.
+ *
+ * For example look at mapbrowser.js implementation.
+ */
+
+function GridBrowser()
+{}
+
+// initializer, list is each childen info
+GridBrowser.prototype.init = function(containerName, pageCounterName, list, selectedChild, childWidth, childHeight)
+{
+ this.container = Engine.GetGUIObjectByName(containerName); //xml object container of the children
+ this.pageCounter = Engine.GetGUIObjectByName(pageCounterName); //xml object type="text" for the numbering of each page
+ this.children = this.container.children; //list of xml objects
+ this.numBoxesCreated = this.children.length; //hardcoded in the xml file, maximum children capable of showing at a time
+
+ //order matters
+ this._setChildDimensions(childWidth, childHeight);
+ this._updateGrid();
+ this._updateMaxNumBoxesInScreen();
+ this._generateGrid();
+
+ this._setList(list);
+ this._updateNumPages();
+ this._setSelectedChild(selectedChild)
+ this._updatePageCounter();
+
+ this._generatePage();
+}
+
+GridBrowser.prototype._setSelectedChild = function(index)
+{
+ this.selectedChild = index;
+ this.selectedChild_page = index % this.maxNumBoxesInScreen;
+ let page = Math.floor(index / this.maxNumBoxesInScreen);
+ this._setCurrentPage(page);
+}
+
+GridBrowser.prototype.setSelectedChild = function(index)
+{
+ this._setSelectedChild(index);
+ this._updatePageCounter();
+ this._generatePage();
+}
+
+GridBrowser.prototype.defineChildren = function(value, subIndex, subArray, childObject, index, array)
+{
+ //empty onpurpose, add modifications only at the object instances
+}
+
+GridBrowser.prototype._setList = function(list)
+{
+ this.list = list;
+}
+
+GridBrowser.prototype.setList = function(list)
+{
+ this._setList(list);
+ this._updateNumPages();
+ this._setSelectedChild(0);
+ this._updatePageCounter();
+ this._generatePage();
+
+}
+
+GridBrowser.prototype._setChildDimensions = function(width, height)
+{
+ this.child = {
+ width: width,
+ height: height
+ };
+}
+
+GridBrowser.prototype.setChildDimensions = function(width, height)
+{
+ this._setChildDimensions(width, height);
+ this._updateGrid();
+ this._updateMaxNumBoxesInScreen();
+ this._generateGrid();
+ this._updateNumPages();
+ this._setSelectedChild(this.selectedChild);
+ this._updatePageCounter();
+ this._generatePage();
+}
+
+GridBrowser.prototype._updateMaxNumBoxesInScreen = function()
+{
+ this.maxNumBoxesInScreen = Math.min(this.nColumns * this.nRows, this.numBoxesCreated);
+}
+
+GridBrowser.prototype._updateGrid = function()
+{
+ let rect = this.container.getComputedSize();
+ rect.width = Math.max(rect.right - rect.left, 0);
+ rect.height = Math.max(rect.bottom - rect.top, 0);
+
+ const nColumns = Math.max(1, Math.floor(rect.width / this.child.width));
+ const nRows = Math.max(1, Math.floor(rect.height / this.child.height));
+
+ this.xCenter = this.child.width * nColumns / 2;
+
+ if (nColumns == this.nColumns && nRows == this.nRows)
+ return false; // same grid as before, no need to update
+
+ this.nColumns = nColumns;
+ this.nRows = nRows;
+
+ return true;
+}
+
+GridBrowser.prototype.updateGrid = function()
+{
+
+ if (!this._updateGrid())
+ return;
+ this._updateMaxNumBoxesInScreen();
+ this._generateGrid();
+ this._updateNumPages();
+ this._setSelectedChild(this.selectedChild);
+ this._updatePageCounter();
+ this._generatePage();
+}
+
+GridBrowser.prototype._generateGrid = function()
+{
+ for (let i = 0; i < this.maxNumBoxesInScreen; ++i)
+ {
+ const x = i % this.nColumns;
+ const y = Math.floor(i / this.nColumns);
+
+ let xmin = this.child.width * x;
+ let xmax = this.child.width * (x + 1);
+ let ymin = this.child.height * y;
+ let ymax = this.child.height * (y + 1);
+ xmin = "50%" + "-" + this.xCenter + "+" + xmin;
+ xmax = "50%" + "-" + this.xCenter + "+" + xmax;
+
+ this.children[i].size = [xmin, ymin, xmax, ymax].join(" ");
+ }
+}
+
+GridBrowser.prototype._generatePage = function()
+{
+ let nubOfBoxesToShow = (this.currentPage == this.numPages - 1) ? (this.list.length - 1) % this.maxNumBoxesInScreen + 1 : this.maxNumBoxesInScreen;
+ const startIndex = this.currentPage * this.maxNumBoxesInScreen;
+ const endIndex = startIndex + nubOfBoxesToShow;
+ let subList = this.list.slice(startIndex, endIndex);
+
+ for (let i = 0; i < nubOfBoxesToShow; ++i)
+ {
+ this.children[i].hidden = false;
+ this.defineChildren(subList[i], i, subList, this.children[i], startIndex + i);
+ }
+
+ for (let i = nubOfBoxesToShow; i < this.numBoxesCreated; ++i)
+ this.children[i].hidden = true;
+}
+
+GridBrowser.prototype._updateNumPages = function()
+{
+ let numPages = Math.ceil(this.list.length / this.maxNumBoxesInScreen);
+ this.numPages = (numPages == 0) ? 1 : numPages; // always at least 1 page
+}
+
+GridBrowser.prototype._updatePageCounter = function()
+{
+ this.pageCounter.caption = `${this.currentPage+1}/${this.numPages}`;
+}
+
+GridBrowser.prototype._setCurrentPage = function(page)
+{
+ this.currentPage = page % this.numPages;
+}
+
+GridBrowser.prototype.displacePage = function(displacement)
+{
+ this._setCurrentPage(this.currentPage + displacement);
+ this._updatePageCounter();
+ this._generatePage();
+}
+
+GridBrowser.prototype.nextPage = function()
+{
+ this.displacePage(1);
+}
+
+GridBrowser.prototype.previousPage = function()
+{
+ this.displacePage(this.numPages - 1);
+}
\ No newline at end of file
Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 21907)
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.js (working copy)
@@ -2407,6 +2407,38 @@
updateGUIObjects();
}
+function MapBrowserOpen()
+{
+ Engine.PushGuiPage("page_mapbrowser.xml",
+ {
+ "callback": "MapBrowserCallback"
+ });
+}
+
+/**
+ * Called after closing the dialog.
+ */
+function MapBrowserCallback(mapSelected)
+{
+ if (!g_IsController || !mapSelected.mapSelected)
+ return;
+
+ let type = mapSelected.settingsType;
+ let typeId = g_Dropdowns.mapType.ids().indexOf(type);
+ g_Dropdowns.mapType.select(typeId);
+
+
+ let filter = mapSelected.settingsFilter;
+ let filterId = g_Dropdowns.mapFilter.ids().indexOf(filter);
+ g_Dropdowns.mapFilter.select(filterId);
+
+ let map = mapSelected.mapName;
+ let mapId = g_Dropdowns.mapSelection.ids().indexOf(map);
+ 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 (revision 21907)
+++ binaries/data/mods/public/gui/gamesetup/gamesetup.xml (working copy)
@@ -139,6 +139,9 @@
<object type="image" sprite="ModernDarkBoxGold" name="gamePreviewBox" size="100%-426 40 100%-24 336">
<object type="image" sprite="snMapPreview" size="1 1 401 294" name="mapPreview"/>
<object name="mapInfoName" type="text" style="ModernLeftLabelText" size="5 100%-20 100% 100%-1"/>
+ <object type="button" size="0% 0% 100% 100%">
+ <action on="Press">MapBrowserOpen();</action>
+ </object>
</object>
<!-- Settings -->
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.js (nonexistent)
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.js (working copy)
@@ -0,0 +1,319 @@
+function GameMap(fileName, type)
+{
+ this.loaded = false;
+ this.file = {};
+ this.type = type;
+ this.file.path = GameMap.types[type].path;
+ this.file.name = fileName;
+ this.file.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);
+
+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 Object.keys(GameMap.types))
+ mapList.push(...GameMap.getMapsOfType(type));
+ return mapList;
+};
+
+//returns a dictionary with maplists of each filter
+GameMap.getMapsByFilter = function(mapList)
+{
+ let mapsFiltered = {};
+ GameMap.filtersList.forEach((filter) => mapsFiltered[filter] = []);
+ for (let map of mapList)
+ {
+ if (!map.loaded)
+ map.init();
+
+ map.loadFilters();
+
+ 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.init = function()
+{
+ this.loaded = true;
+ return this.data = (this.type == "random") ?
+ Engine.ReadJSONFile(this.file.path + this.file.name + this.file.extension) :
+ Engine.LoadMapSettings(this.file.path + this.file.name);
+};
+
+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 = new GridBrowser();
+
+let mapSelected = {};
+mapSelected.set = function(map)
+{
+ mapSelected.map = map;
+ mapSelected.mapType = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ mapSelected.mapFilter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+}
+
+mapBrowser.defineChildren = function(map, n, sublist, object, i)
+{
+ if (!map.loaded)
+ {
+ map.init();
+ }
+ if (!map.mapBrowserLoaded)
+ {
+ map.loadName();
+ map.loadDescription();
+ map.loadPreview();
+ map.mapBrowserLoaded = true;
+ }
+
+ const id = (name, i = n) => `${name}[${i}]`
+
+ Engine.GetGUIObjectByName(id("mapButton")).sprite = (this.selectedChild == i) ? "ModernButtonRed" : "";
+
+
+ Engine.GetGUIObjectByName(id("mapName")).caption = map.name;
+ Engine.GetGUIObjectByName(id("mapPreview")).sprite = map.preview;
+
+ let button = Engine.GetGUIObjectByName(id("mapButton"))
+
+ Engine.GetGUIObjectByName(id("mapButton")).onPress = () =>
+ {
+ Engine.GetGUIObjectByName(id("mapButton", this.selectedChild_page)).sprite = "";
+ this.selectedChild = i;
+ this.selectedChild_page = n;
+ Engine.GetGUIObjectByName(id("mapButton")).sprite = "ModernButtonRed";
+ mapSelected.set(map)
+
+
+ };
+
+ Engine.GetGUIObjectByName(`mapButton[${n}]`).onMouseLeftDoubleClick = () =>
+ {
+ mapSelected.set(map);
+ mapBrowserReturn(mapSelected)
+ };
+
+};
+
+let mapList;
+let mapFilter;
+
+let child = {
+ width: 400,
+ height: 300
+}
+let mapZoom = {};
+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];
+mapZoom.selected = 6;
+
+mapZoom.in = function()
+{
+ if (mapZoom.selected == mapZoom.levels.length - 1)
+ return;
+
+ const level = mapZoom.levels[++mapZoom.selected];
+ mapBrowser.setChildDimensions(child.width * level, child.height * level)
+}
+
+mapZoom.out = function()
+{
+ if (mapZoom.selected == 0)
+ return;
+
+ const level = mapZoom.levels[--mapZoom.selected];
+ mapBrowser.setChildDimensions(child.width * level, child.height * level)
+}
+
+let mapTypeDropdown;
+let mapFilterDropdown;
+
+function init(settings)
+{
+
+ 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 = 0;
+ mapTypeDropdown.onSelectionChange = () =>
+ {
+ let type = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapList = GameMap.getMapsOfType(type);
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+ mapBrowser.selectedChild = -1;
+ mapBrowser.selectedChild_page = 0;
+ mapBrowser.setList(mapFilter)
+ };
+
+ mapFilterDropdown = Engine.GetGUIObjectByName("mapFilterDropdown");
+ mapFilterDropdown.list = GameMap.filtersList.map(filter => GameMap.filters[filter].name);
+ mapFilterDropdown.list_data = GameMap.filtersList;
+ mapFilterDropdown.selected = 0;
+ mapFilterDropdown.onSelectionChange = () =>
+ {
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+ mapBrowser.selectedChild = -1;
+ mapBrowser.selectedChild_page = 0;
+ mapBrowser.setList(mapFilter)
+ };
+
+ let mapsZoonInButton = Engine.GetGUIObjectByName("mapsZoomIn");
+ mapsZoonInButton.onPress = mapZoom.in;
+
+ let mapsZoonOutButton = Engine.GetGUIObjectByName("mapsZoomOut");
+ mapsZoonOutButton.onPress = mapZoom.out;
+
+ let container = Engine.GetGUIObjectByName("MapBrowserContainer");
+
+ let type = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapList = GameMap.getMapsOfType(type);
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+
+ const level = mapZoom.levels[++mapZoom.selected];
+ let childWidth = 400 * level;
+ let childHeight = 300 * level;
+ let mapNumber = 2;
+ mapBrowser.init("MapBrowserContainer", "currentPageNum", mapFilter, mapNumber, childWidth, childHeight);
+ mapSelected.set(mapFilter[mapNumber])
+}
+
+function mapBrowserReturn(data = false)
+{
+ // Pop the page before calling the callback, so the callback runs
+ // in the parent GUI page's context
+ if (!data)
+ {
+ Engine.PopGuiPageCB(
+ {
+ mapSelected: false
+ });
+ }
+ else
+ {
+ Engine.PopGuiPageCB(
+ {
+ mapSelected: true,
+ mapName: data.map.file.path + data.map.file.name,
+ settingsType: data.mapType,
+ settingsFilter: data.mapFilter
+ });
+ }
+}
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml (nonexistent)
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml (working copy)
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<objects>
+
+ <script directory="gui/common/"/>
+ <script directory="gui/mapbrowser/"/>
+
+ <!-- Add a translucent black background to fade out the menu page -->
+ <object type="image" sprite="ModernFade"/>
+
+ <object type="image" style="ModernDialog" size="0%+10 0%+20 100%-10 100%-10">
+
+ <object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
+ <translatableAttribute id="caption">Map Browser</translatableAttribute>
+ </object>
+
+ <object
+ name="MapBrowserContainer"
+ type="image"
+ size="30 30 100%-400 100%-40">
+
+ <repeat count="100" var="n">
+ <object hidden="true">
+ <object name="mapButton[n]" type="button" style="ModernButtonRed">
+ <action on="MouseWheelUp">mapZoom.in()</action>
+ <action on="MouseWheelDown">mapZoom.out()</action>
+ </object>
+ <object type="image" sprite="ModernDarkBoxGold" ghost="true" size="8 8 100%-8 100%-8">
+ <object name="mapName[n]" type="text" style="ModernLabelText" size="10 100%-25 100%-10 100%-5"/>
+ <object name="mapPreview[n]" type="image" size="1 1 100%-1 100%-1"/>
+ </object>
+ </object>
+ </repeat>
+ <action on="MouseWheelUp">mapZoom.in()</action>
+ <action on="MouseWheelDown">mapZoom.out()</action>
+ <!-- Recalculate grid to fill all screen space -->
+ <action on="WindowResized">
+ mapBrowser.updateGrid();
+ </action>
+ </object>
+ <object size="100%-400 30 100%-30 90" z="201">
+ <object size="0% 0% 100% 50%">
+ <!-- Page: -->
+ <object
+ type="text"
+ style="ModernRightLabelText"
+ size="0% 0% 20% 100%">
+ <translatableAttribute id="caption">Page:</translatableAttribute>
+ </object>
+ <!-- Page number text-->
+ <object
+ name="currentPageNum"
+ type="text"
+ style="ModernLeftLabelText"
+ size="20% 0% 40% 100%"/>
+ <!-- Previous button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="40% 0% 70% 100%">
+ <translatableAttribute id="caption">Previous</translatableAttribute>
+ <action on="Press">mapBrowser.previousPage();</action>
+ </object>
+ <!-- Next button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="70% 0% 100% 100%">
+ <translatableAttribute id="caption">Next</translatableAttribute>
+ <action on="Press">mapBrowser.nextPage();</action>
+ </object>
+ </object>
+ <object size="0% 50% 100% 100%">
+ <!-- Zoom button in -->
+ <object
+ name="mapsZoomIn"
+ type="button"
+ style="ModernButtonRed"
+ size="0% 0% 0%+30 100%">
+ <translatableAttribute id="caption">+</translatableAttribute>
+ </object>
+ <!-- Zoom button out -->
+ <object
+ name="mapsZoomOut"
+ type="button"
+ style="ModernButtonRed"
+ size="0%+30 0% 0%+60 100%">
+ <translatableAttribute id="caption">-</translatableAttribute>
+ </object>
+ <object size="0%+60 0% 100% 100%">
+ <!-- Map type dropdown -->
+ <object name="mapTypeDropdown"
+ type="dropdown"
+ style="ModernDropDown"
+ size="0% 0% 50% 100%">
+ </object>
+ <!-- Map filter dropdown -->
+ <object name="mapFilterDropdown"
+ type="dropdown"
+ style="ModernDropDown"
+ size="50% 0% 100% 100%">
+ </object>
+ </object>
+ </object>
+ </object>
+ <!-- Close button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="0%+40 100%-20-30 0%+40+200 100%-20"
+ hotkey="cancel">
+ <translatableAttribute id="caption">Close</translatableAttribute>
+ <action on="Press">mapBrowserReturn(false);</action>
+ </object>
+
+
+ <object hotkey="tab.prev"><action on="Press">mapBrowser.previousPage();</action></object>
+ <object hotkey="tab.next"><action on="Press">mapBrowser.nextPage();</action></object>
+
+ </object>
+</objects>
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.js (nonexistent)
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.js (working copy)
@@ -0,0 +1,319 @@
+function GameMap(fileName, type)
+{
+ this.loaded = false;
+ this.file = {};
+ this.type = type;
+ this.file.path = GameMap.types[type].path;
+ this.file.name = fileName;
+ this.file.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);
+
+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 Object.keys(GameMap.types))
+ mapList.push(...GameMap.getMapsOfType(type));
+ return mapList;
+};
+
+//returns a dictionary with maplists of each filter
+GameMap.getMapsByFilter = function(mapList)
+{
+ let mapsFiltered = {};
+ GameMap.filtersList.forEach((filter) => mapsFiltered[filter] = []);
+ for (let map of mapList)
+ {
+ if (!map.loaded)
+ map.init();
+
+ map.loadFilters();
+
+ 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.init = function()
+{
+ this.loaded = true;
+ return this.data = (this.type == "random") ?
+ Engine.ReadJSONFile(this.file.path + this.file.name + this.file.extension) :
+ Engine.LoadMapSettings(this.file.path + this.file.name);
+};
+
+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 = new GridBrowser();
+
+let mapSelected = {};
+mapSelected.set = function(map)
+{
+ mapSelected.map = map;
+ mapSelected.mapType = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ mapSelected.mapFilter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+}
+
+mapBrowser.defineChildren = function(map, n, sublist, object, i)
+{
+ if (!map.loaded)
+ {
+ map.init();
+ }
+ if (!map.mapBrowserLoaded)
+ {
+ map.loadName();
+ map.loadDescription();
+ map.loadPreview();
+ map.mapBrowserLoaded = true;
+ }
+
+ const id = (name, i = n) => `${name}[${i}]`
+
+ Engine.GetGUIObjectByName(id("mapButton")).sprite = (this.selectedChild == i) ? "ModernButtonRed" : "";
+
+
+ Engine.GetGUIObjectByName(id("mapName")).caption = map.name;
+ Engine.GetGUIObjectByName(id("mapPreview")).sprite = map.preview;
+
+ let button = Engine.GetGUIObjectByName(id("mapButton"))
+
+ Engine.GetGUIObjectByName(id("mapButton")).onPress = () =>
+ {
+ Engine.GetGUIObjectByName(id("mapButton", this.selectedChild_page)).sprite = "";
+ this.selectedChild = i;
+ this.selectedChild_page = n;
+ Engine.GetGUIObjectByName(id("mapButton")).sprite = "ModernButtonRed";
+ mapSelected.set(map)
+
+
+ };
+
+ Engine.GetGUIObjectByName(`mapButton[${n}]`).onMouseLeftDoubleClick = () =>
+ {
+ mapSelected.set(map);
+ mapBrowserReturn(mapSelected)
+ };
+
+};
+
+let mapList;
+let mapFilter;
+
+let child = {
+ width: 400,
+ height: 300
+}
+let mapZoom = {};
+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];
+mapZoom.selected = 6;
+
+mapZoom.in = function()
+{
+ if (mapZoom.selected == mapZoom.levels.length - 1)
+ return;
+
+ const level = mapZoom.levels[++mapZoom.selected];
+ mapBrowser.setChildDimensions(child.width * level, child.height * level)
+}
+
+mapZoom.out = function()
+{
+ if (mapZoom.selected == 0)
+ return;
+
+ const level = mapZoom.levels[--mapZoom.selected];
+ mapBrowser.setChildDimensions(child.width * level, child.height * level)
+}
+
+let mapTypeDropdown;
+let mapFilterDropdown;
+
+function init(settings)
+{
+
+ 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 = 0;
+ mapTypeDropdown.onSelectionChange = () =>
+ {
+ let type = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapList = GameMap.getMapsOfType(type);
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+ mapBrowser.selectedChild = -1;
+ mapBrowser.selectedChild_page = 0;
+ mapBrowser.setList(mapFilter)
+ };
+
+ mapFilterDropdown = Engine.GetGUIObjectByName("mapFilterDropdown");
+ mapFilterDropdown.list = GameMap.filtersList.map(filter => GameMap.filters[filter].name);
+ mapFilterDropdown.list_data = GameMap.filtersList;
+ mapFilterDropdown.selected = 0;
+ mapFilterDropdown.onSelectionChange = () =>
+ {
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+ mapBrowser.selectedChild = -1;
+ mapBrowser.selectedChild_page = 0;
+ mapBrowser.setList(mapFilter)
+ };
+
+ let mapsZoonInButton = Engine.GetGUIObjectByName("mapsZoomIn");
+ mapsZoonInButton.onPress = mapZoom.in;
+
+ let mapsZoonOutButton = Engine.GetGUIObjectByName("mapsZoomOut");
+ mapsZoonOutButton.onPress = mapZoom.out;
+
+ let container = Engine.GetGUIObjectByName("MapBrowserContainer");
+
+ let type = mapTypeDropdown.list_data[mapTypeDropdown.selected];
+ let filter = mapFilterDropdown.list_data[mapFilterDropdown.selected];
+ mapList = GameMap.getMapsOfType(type);
+ mapFilter = GameMap.getMapsByFilter(mapList)[filter];
+
+ const level = mapZoom.levels[++mapZoom.selected];
+ let childWidth = 400 * level;
+ let childHeight = 300 * level;
+ let mapNumber = 2;
+ mapBrowser.init("MapBrowserContainer", "currentPageNum", mapFilter, mapNumber, childWidth, childHeight);
+ mapSelected.set(mapFilter[mapNumber])
+}
+
+function mapBrowserReturn(data = false)
+{
+ // Pop the page before calling the callback, so the callback runs
+ // in the parent GUI page's context
+ if (!data)
+ {
+ Engine.PopGuiPageCB(
+ {
+ mapSelected: false
+ });
+ }
+ else
+ {
+ Engine.PopGuiPageCB(
+ {
+ mapSelected: true,
+ mapName: data.map.file.path + data.map.file.name,
+ settingsType: data.mapType,
+ settingsFilter: data.mapFilter
+ });
+ }
+}
Index: binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml
===================================================================
--- binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml (nonexistent)
+++ binaries/data/mods/public/gui/mapbrowser/mapbrowser.xml (working copy)
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<objects>
+
+ <script directory="gui/common/"/>
+ <script directory="gui/mapbrowser/"/>
+
+ <!-- Add a translucent black background to fade out the menu page -->
+ <object type="image" sprite="ModernFade"/>
+
+ <object type="image" style="ModernDialog" size="0%+10 0%+20 100%-10 100%-10">
+
+ <object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
+ <translatableAttribute id="caption">Map Browser</translatableAttribute>
+ </object>
+
+ <object
+ name="MapBrowserContainer"
+ type="image"
+ size="30 30 100%-400 100%-40">
+
+ <repeat count="100" var="n">
+ <object hidden="true">
+ <object name="mapButton[n]" type="button" style="ModernButtonRed">
+ <action on="MouseWheelUp">mapZoom.in()</action>
+ <action on="MouseWheelDown">mapZoom.out()</action>
+ </object>
+ <object type="image" sprite="ModernDarkBoxGold" ghost="true" size="8 8 100%-8 100%-8">
+ <object name="mapName[n]" type="text" style="ModernLabelText" size="10 100%-25 100%-10 100%-5"/>
+ <object name="mapPreview[n]" type="image" size="1 1 100%-1 100%-1"/>
+ </object>
+ </object>
+ </repeat>
+ <action on="MouseWheelUp">mapZoom.in()</action>
+ <action on="MouseWheelDown">mapZoom.out()</action>
+ <!-- Recalculate grid to fill all screen space -->
+ <action on="WindowResized">
+ mapBrowser.updateGrid();
+ </action>
+ </object>
+ <object size="100%-400 30 100%-30 90" z="201">
+ <object size="0% 0% 100% 50%">
+ <!-- Page: -->
+ <object
+ type="text"
+ style="ModernRightLabelText"
+ size="0% 0% 20% 100%">
+ <translatableAttribute id="caption">Page:</translatableAttribute>
+ </object>
+ <!-- Page number text-->
+ <object
+ name="currentPageNum"
+ type="text"
+ style="ModernLeftLabelText"
+ size="20% 0% 40% 100%"/>
+ <!-- Previous button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="40% 0% 70% 100%">
+ <translatableAttribute id="caption">Previous</translatableAttribute>
+ <action on="Press">mapBrowser.previousPage();</action>
+ </object>
+ <!-- Next button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="70% 0% 100% 100%">
+ <translatableAttribute id="caption">Next</translatableAttribute>
+ <action on="Press">mapBrowser.nextPage();</action>
+ </object>
+ </object>
+ <object size="0% 50% 100% 100%">
+ <!-- Zoom button in -->
+ <object
+ name="mapsZoomIn"
+ type="button"
+ style="ModernButtonRed"
+ size="0% 0% 0%+30 100%">
+ <translatableAttribute id="caption">+</translatableAttribute>
+ </object>
+ <!-- Zoom button out -->
+ <object
+ name="mapsZoomOut"
+ type="button"
+ style="ModernButtonRed"
+ size="0%+30 0% 0%+60 100%">
+ <translatableAttribute id="caption">-</translatableAttribute>
+ </object>
+ <object size="0%+60 0% 100% 100%">
+ <!-- Map type dropdown -->
+ <object name="mapTypeDropdown"
+ type="dropdown"
+ style="ModernDropDown"
+ size="0% 0% 50% 100%">
+ </object>
+ <!-- Map filter dropdown -->
+ <object name="mapFilterDropdown"
+ type="dropdown"
+ style="ModernDropDown"
+ size="50% 0% 100% 100%">
+ </object>
+ </object>
+ </object>
+ </object>
+ <!-- Close button -->
+ <object
+ type="button"
+ style="ModernButtonRed"
+ size="0%+40 100%-20-30 0%+40+200 100%-20"
+ hotkey="cancel">
+ <translatableAttribute id="caption">Close</translatableAttribute>
+ <action on="Press">mapBrowserReturn(false);</action>
+ </object>
+
+
+ <object hotkey="tab.prev"><action on="Press">mapBrowser.previousPage();</action></object>
+ <object hotkey="tab.next"><action on="Press">mapBrowser.nextPage();</action></object>
+
+ </object>
+</objects>
Index: binaries/data/mods/public/gui/page_mapbrowser.xml
===================================================================
--- binaries/data/mods/public/gui/page_mapbrowser.xml (nonexistent)
+++ binaries/data/mods/public/gui/page_mapbrowser.xml (working copy)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<page>
+ <include>common/modern/setup.xml</include>
+ <include>common/modern/styles.xml</include>
+ <include>common/modern/sprites.xml</include>
+
+ <include>common/setup.xml</include>
+ <include>common/sprites.xml</include>
+ <include>common/styles.xml</include>
+
+ <include>common/global.xml</include>
+ <include>mapbrowser/mapbrowser.xml</include>
+</page>

Event Timeline

nani created this paste.Oct 16 2018, 11:44 PM
nani created this object with visibility "Public (No Login Required)".
nani added a subscriber: elexis.Oct 16 2018, 11:49 PM

Added:

  • Mouse scroll zoom
  • Buttons zoom
  • Double click select map -> goes to gamesetup with map selected
  • Map types and filters
  • Tab key to navigate pages
  • Improved code overall

Most probably wont add/update anymore given that @elexis seems to have some refractored code that would modify what is left to do.

very nice could you also take some screenshots as previously showcasing uses?