Page MenuHomeWildfire Games
Paste P137

gridBrowser + mapbrowser update + semi functional map browser diff
ArchivedPublic

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?

nani archived this paste.Mar 19 2019, 7:15 PM