Changeset View
Standalone View
binaries/data/mods/public/gui/mapbrowser/mapbrowser.js
function GameMap(fileName, type) | |||||
{ | |||||
this.loaded = false; | |||||
this.type = type; | |||||
this.file = { | |||||
"path": GameMap.types[type].path, | |||||
"name": fileName, | |||||
"extension": GameMap.types[type].extension | |||||
elexisUnsubmitted Done Inline Actionselexis: ```
/**
* jsdoc syntax
``` | |||||
}; | |||||
Done Inline Actionsquotes around property names no spaces at the property access elexis: quotes around property names
no spaces at the property access | |||||
} | |||||
GameMap.types = { | |||||
"random": { | |||||
Done Inline Actions.prototype? elexis: .prototype? | |||||
Done Inline ActionsIs defined as a static variables (shared in all instances) nani: Is defined as a static variables (shared in all instances) | |||||
Done Inline Actionslike the functions. So might just as well make it consistent? elexis: like the functions. So might just as well make it consistent?
()I recall the `this` keyword is… | |||||
Done Inline ActionsI write it as GameMap.types in an attempt not having to make another global g_GameMapTypes and thus no global scope pollution. nani: I write it as `GameMap.types` in an attempt not having to make another global `g_GameMapTypes`… | |||||
Done Inline ActionsThat's a nice offer, but I was asking for .prototype though. elexis: That's a nice offer, but I was asking for `.prototype` though.
Read: `GameMap.types` ->… | |||||
"path": "maps/random/", | |||||
"extension": ".json" | |||||
}, | |||||
"scenario": { | |||||
Done Inline Actionsunneeded parentheses elexis: unneeded parentheses | |||||
"path": "maps/scenarios/", | |||||
"extension": ".xml" | |||||
}, | |||||
"skirmish": { | |||||
"path": "maps/skirmishes/", | |||||
"extension": ".xml" | |||||
} | |||||
}; | |||||
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), | |||||
Done Inline ActionsDelete this, just use for (let mapTey in this.type) elexis: Delete this, just use `for (let mapTey in this.type)`
Also this looks like a copy, should be… | |||||
"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) | |||||
{ | |||||
if (!GameMap.types[type]) | |||||
return []; | |||||
return listFiles(GameMap.types[type].path, GameMap.types[type].extension, false). | |||||
filter(fileName => !fileName.startsWith("_")). | |||||
map(fileName => new GameMap(fileName, type)); | |||||
}; | |||||
Done Inline ActionsThis is too much copypasta, not ok. elexis: This is too much copypasta, not ok.
If I recall correctly I made this JSON by transforming the… | |||||
/** | |||||
* 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; | |||||
Done Inline Actionsthis sounds like return this.typesList.map(mapType => mapType.foo) elexis: this sounds like return `this.typesList.map(mapType => mapType.foo)` | |||||
Done Inline ActionsNeeds to expand inner array so won't work but seems [].concat.apply([], arrays) will nani: Needs to expand inner array so won't work but seems `[].concat.apply([], arrays)` will | |||||
Done Inline ActionsDeleting it given that I noticed is not used in the code. nani: Deleting it given that I noticed is not used in the code. | |||||
Done Inline Actionsreturn this.MapTypes.reduce((mapTypes, mapType) => ...concat..., []); elexis: `return this.MapTypes.reduce((mapTypes, mapType) => ...concat..., []);` | |||||
}; | |||||
// load data from settings | |||||
Done Inline Actionsin and out are the same function, just with a +1 or -1 argument and x = (x + step) % max elexis: in and out are the same function, just with a +1 or -1 argument and x = (x + step) % max | |||||
Done Inline Actionsx = (x + step) % max is a trap -1% 10 = -1 in javascirpt x = ( x + max + step ) % max solves this but I don't want it to be cyclic nani: x = (x + step) % max is a trap
-1% 10 = -1 in javascirpt
x = ( x + max + step ) % max… | |||||
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(); | |||||
Done Inline ActionsProbably something like mapsFiltered = Object.assign(...) elexis: Probably something like `mapsFiltered = Object.assign(...)` | |||||
Done Inline ActionsCan't find how to make a one liner with Object.assign nani: Can't find how to make a one liner with `Object.assign` | |||||
Done Inline ActionsI never knew about it until bb discovered it and added it in a gamesetup.js. (If I recall correctly theres still an unneeded {} in that call) elexis: I never knew about it until bb discovered it and added it in a `gamesetup.js`. (If I recall… | |||||
Done Inline ActionsIs it really necessary? I looked at the gamesetup.js example and seem overly complex for this case nani: Is it really necessary? I looked at the `gamesetup.js` example and seem overly complex for… | |||||
Done Inline ActionsWe might find out which implementation is nicer after having seen how the line would actually come out. Notice that this code is copy, it shouldn't exist. If it doesn't exist, we don't have to argue how it should be written. But I guess the copy would have to be moved, so can't escape the lint I suppose. Actually I rewrote the function too somewhere and moved it into a prototype. So perhaps we can just exclude these copypasta functions from the review for now. elexis: We might find out which implementation is nicer after having seen how the line would actually… | |||||
this.loadPreview(); | |||||
this.loadFilters(); | |||||
}; | |||||
GameMap.prototype.loadName = function () | |||||
{ | |||||
this.name = (!this.data || !this.data.settings || !this.data.settings.Name) ? | |||||
Done Inline Actions(Probably .filter array function applicable) elexis: (Probably `.filter` array function applicable) | |||||
translate("No map name.") : | |||||
translate(this.data.settings.Name); | |||||
Done Inline Actions("hotkey": "tab.next" support was not committed IIRC meh) elexis: ("hotkey": "tab.next" support was not committed IIRC meh) | |||||
Done Inline ActionsDoes work tho. nani: Does work tho. | |||||
}; | |||||
GameMap.prototype.loadDescription = function () | |||||
{ | |||||
this.description = (!this.data || !this.data.settings || !this.data.settings.Description) ? | |||||
translate("No map description.") : | |||||
translate(this.data.settings.Description); | |||||
}; | |||||
Done Inline ActionsThis is copy&pasted from gamesetup.js too, should use a common helper function elexis: This is copy&pasted from gamesetup.js too, should use a common helper function | |||||
GameMap.prototype.loadPreview = function () | |||||
Done Inline Actions(unneeded braces) elexis: (unneeded braces) | |||||
{ | |||||
let 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 () | |||||
{ | |||||
this.filter = this.data.settings ? this.data.settings.Keywords || ["all"] : ["all"]; | |||||
}; | |||||
// GridBrowser instance, manages the maps previews | |||||
var g_MapBrowser; | |||||
// Object with maps by type from which each type has a object with maps by filter | |||||
var g_AllMapsByType = {}; | |||||
// Handles map list search behaviour | |||||
var g_MapsSearchBoxInput; | |||||
var g_TickCount = 0; | |||||
var g_MapSelected = { | |||||
"set": (selectedListIndex) => | |||||
{ | |||||
Done Inline Actionsabove 3 functions copies too elexis: above 3 functions copies too | |||||
g_MapBrowser.setIndexOfSelected(selectedListIndex); | |||||
if (selectedListIndex == -1) | |||||
return; | |||||
let map = g_MapBrowser.list[selectedListIndex]; | |||||
Done Inline Actionsdouble negation unneeded? Also I think this could be x && y || [] elexis: double negation unneeded? Also I think this could be x && y || [] | |||||
Done Inline Actionseae pro elexis +1 ffffffff: eae pro elexis +1 | |||||
if (!map) | |||||
return; | |||||
Done Inline ActionsI guess that would be ["all"] instead of [] then? elexis: I guess that would be ["all"] instead of [] then? | |||||
Done Inline ActionsWhat do you mean by that? nani: What do you mean by that? | |||||
Done Inline Actionsthought the same nani +1 eae ffffffff: thought the same nani +1 eae | |||||
Done Inline ActionsThat "all" is already present unless it's an empty array? elexis: That "all" is already present unless it's an empty array? | |||||
Done Inline ActionsCan operate directly on this.filter instead of creating a helper variable. elexis: Can operate directly on `this.filter` instead of creating a helper variable.
Return value… | |||||
Done Inline Actionseae ffffffff: eae | |||||
if (!map.loaded) | |||||
map.load(); | |||||
let isSameMap = !g_MapSelected.map ? false : g_MapSelected.map.name == map.name; | |||||
Done Inline ActionsCoding convention is to use g_Foo rather than g_foo elexis: Coding convention is to use g_Foo rather than g_foo
JSDoc | |||||
Done Inline Actionswhat a troll (nani), eae ffffffff: what a troll (nani), eae | |||||
g_MapSelected.map = map; | |||||
g_MapSelected.filter = getFilter(); | |||||
if (isSameMap) | |||||
Done Inline ActionsThere is a common function for that already. We shouldn't have two of them. The protoype in general seems to have this redundancy problem. elexis: There is a common function for that already. We shouldn't have two of them. The protoype in… | |||||
Done Inline ActionsFor the record I copy-pasted some code given that i only needed some functionality of game setup but wasn't possible to just grab some function without the rest of the gamesetup.js code. nani: For the record I copy-pasted some code given that i only needed some functionality of game… | |||||
Done Inline ActionsIt should be possible to grab the mappreview and mapfilter ones without messing with the other code? elexis: It should be possible to grab the mappreview and mapfilter ones without messing with the other… | |||||
return; | |||||
animate.chain("previewSelectedOverlay", | |||||
[{ | |||||
"color": { "a": 150 }, | |||||
"onComplete": () => | |||||
{ | |||||
Engine.GetGUIObjectByName("nameSelected").caption = g_MapSelected.map.name; | |||||
Done Inline ActionsPrototypes better in their own file (like we have in the simulation and rmgen) elexis: Prototypes better in their own file (like we have in the simulation and rmgen) | |||||
Engine.GetGUIObjectByName("previewSelected").sprite = g_MapSelected.map.preview; | |||||
} | |||||
}, | |||||
{ "color": { "a": 0 } } | |||||
], | |||||
{ | |||||
Done Inline Actionslet not in global context. (I heard that should become illegal with future spidermonkey or ES versions) elexis: `let` not in global context. (I heard that should become illegal with future spidermonkey or ES… | |||||
Done Inline Actionsglobal variables start with`g_` http://trac.wildfiregames.com/wiki/Coding_Conventions elexis: global variables start with`g_` http://trac.wildfiregames.com/wiki/Coding_Conventions | |||||
"queue": true, | |||||
"duration": 100 | |||||
} | |||||
Done Inline Actionsvar g_MapSelected = { "set": (map, filter) => { .... } }; elexis: var g_MapSelected = {
"set": (map, filter) => {
....
}
};
| |||||
Done Inline ActionsNeeds a function not am arrow function. nani: Needs a function not am arrow function. | |||||
Done Inline Actionsfunction (map, filter = undefined) is synonymous with function (map, filter) and (map, filter) => elexis: `function (map, filter = undefined)` is synonymous with `function (map, filter)` and `(map… | |||||
); | |||||
animate.chain("descriptionSelected", | |||||
[{ | |||||
"textcolor": { "a": 120 }, | |||||
"onComplete": object => object.caption = g_MapSelected.map.description | |||||
}, | |||||
{ "textcolor": { "a": 255 } } | |||||
], | |||||
{ | |||||
Done Inline Actionsrefs OOP rewrite. (Having each component decide for itself what it wants to display and which data it uses, rather than having the data decide which objects it must update, which leads to lasagna). elexis: refs OOP rewrite. (Having each component decide for itself what it wants to display and which… | |||||
"queue": true, | |||||
"duration": 100 | |||||
} | |||||
); | |||||
} | |||||
}; | |||||
// Stores zoom in/out functions and level of zoom | |||||
Done Inline Actions"mapPreview[" + index + "]" seems easier (also consistent with the rest of the GUI rather than introducing templates which nothing else in 0ad uses as mentioned) elexis: "mapPreview[" + index + "]" seems easier (also consistent with the rest of the GUI rather than… | |||||
var g_MapZoom = { | |||||
"originalSize": { "width": 400, "height": 300 }, | |||||
"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, 2.9], | |||||
Done Inline ActionsIt removes indirection and became a GUI convention to use the same variable name for the GUI object; i.e. mapButton instead of button. elexis: It removes indirection and became a GUI convention to use the same variable name for the GUI… | |||||
"levelsIndexSelected": 5, | |||||
"getSize": () => | |||||
{ | |||||
let level = g_MapZoom.levels[g_MapZoom.levelsIndexSelected]; | |||||
return { | |||||
"width": g_MapZoom.originalSize.width * level, | |||||
Done Inline ActionsSeems a bit more clear with each statement and each property being on a separate line: { "textcolor": { "a": 120 }, "onComplete": () => { Engine.GetGUIObjectByName("descriptionSelected").caption = map.description; } }, elexis: Seems a bit more clear with each statement and each property being on a separate line:
```
{… | |||||
"height": g_MapZoom.originalSize.height * level | |||||
}; | |||||
}, | |||||
"in": () => | |||||
{ | |||||
if (g_MapZoom.levelsIndexSelected == g_MapZoom.levels.length - 1) | |||||
return; | |||||
++g_MapZoom.levelsIndexSelected; | |||||
g_MapBrowser.setChildDimensions(g_MapZoom.getSize()); | |||||
}, | |||||
"out": () => | |||||
{ | |||||
if (g_MapZoom.levelsIndexSelected == 0) | |||||
return; | |||||
--g_MapZoom.levelsIndexSelected; | |||||
g_MapBrowser.setChildDimensions(g_MapZoom.getSize()); | |||||
} | |||||
}; | |||||
Done Inline ActionsAbove 4 functions don't seem to have a reason to be exempt from becoming part of new prototype. (other than the callback, ontick and init functions which are hardcoded in C++, yet) elexis: Above 4 functions don't seem to have a reason to be exempt from becoming part of new prototype. | |||||
Done Inline ActionsBeing the take on this what? nani: Being the take on this what? | |||||
var g_GUIObjects = { | |||||
"mapsZoomIn": { | |||||
"tooltip": translate(setStringTags("\\[" + "MouseWheelUp" + "]", g_HotkeyTags) + ": Make map previews bigger."), | |||||
"onPress": () => { g_MapZoom.in(); } | |||||
}, | |||||
"mapsZoomOut": { | |||||
"tooltip": translate(setStringTags("\\[" + "MouseWheelDown" + "]", g_HotkeyTags) + ": Make map previews smaller?"), | |||||
"onPress": () => { g_MapZoom.out(); } | |||||
Done Inline Actions(Don't mix values and logic; have the caller determine the values where possible) elexis: (Don't mix values and logic; have the caller determine the values where possible) | |||||
Done Inline ActionsDone nani: Done | |||||
}, | |||||
"prevButton": { | |||||
"tooltip": colorizeHotkey(translate("%(hotkey)s: Goes to previous page."), "tab.prev"), | |||||
"onPress": () => { g_MapBrowser.previousPage(); } | |||||
}, | |||||
"nextButton": { | |||||
"tooltip": colorizeHotkey(translate("%(hotkey)s: Goes to next page."), "tab.next"), | |||||
"onPress": () => { g_MapBrowser.nextPage(); } | |||||
}, | |||||
"selectMapButton": { | |||||
"tooltip": translate("Select map."), | |||||
"onPress": () => { mapBrowserReturn(true); } | |||||
}, | |||||
"closeButton": { | |||||
"tooltip": colorizeHotkey(translate("%(hotkey)s: Close map browser."), "cancel"), | |||||
"onPress": () => { mapBrowserReturn(false); } | |||||
Done Inline Actionscontinue; elexis: continue; | |||||
}, | |||||
"dialog": { | |||||
"onWindowResized": () => { g_MapBrowser.generateGrid(); }, | |||||
"onTick": () => { onTick(); }, | |||||
"onMouseWheelUp": () => { g_MapZoom.in(); }, | |||||
"onMouseWheelDown": () => { g_MapZoom.out(); } | |||||
}, | |||||
"mapsSearchBox": { | |||||
"onTab": () => { g_MapBrowser.nextPage(); }, | |||||
"onMouseLeftPress": () => | |||||
{ | |||||
// Makes MapsSearchBoxInput.prototype.onTick process current caption as a new caption | |||||
// as type="input" doesn't have a onFocus event | |||||
if (g_MapsSearchBoxInput) | |||||
g_MapsSearchBoxInput.lastCaption = ""; | |||||
}, | |||||
"onPress": () => | |||||
{ | |||||
// Select first map if text inputed | |||||
Done Inline ActionsCan create the entire object in one statement: g_MapZoom = { "originalSize": ... "levels": ... "getSize": () => ..., .... } elexis: Can create the entire object in one statement:
g_MapZoom = {
"originalSize": ...
"levels"… | |||||
Done Inline ActionsCan't, given they all depend of each other (JS gives error at creation given that it doesn't know of the other keys) nani: Can't, given they all depend of each other (JS gives error at creation given that it doesn't… | |||||
if (!g_MapBrowser.list.length || !g_MapsSearchBoxInput || g_MapsSearchBoxInput.mapsSearchBox.caption.trim() == "") | |||||
return; | |||||
g_MapSelected.set(0); | |||||
mapBrowserReturn(true); | |||||
} | |||||
}, | |||||
"MapBrowserContainer": { | |||||
Done Inline ActionsIs it improtant to have a non-linear scale? elexis: Is it improtant to have a non-linear scale? | |||||
Done Inline ActionsNot really but linear doesn't mean better. nani: Not really but linear doesn't mean better. | |||||
Done Inline Actions(Guess I was wondering if one could find a formula to compute the numbers rather than hardcoding the numbers) elexis: (Guess I was wondering if one could find a formula to compute the numbers rather than… | |||||
"onMouseWheelUp": () => { g_MapZoom.in(); }, | |||||
"onMouseWheelDown": () => { g_MapZoom.out(); } | |||||
}, | |||||
"mapTypeDropdown": { | |||||
"list": g_Settings.MapTypes.map(type => type.Title), | |||||
"list_data": g_Settings.MapTypes.map(type => type.Name), | |||||
"onSelectionChange": () => | |||||
{ | |||||
if (!getType() || !getFilter()) | |||||
return; | |||||
g_MapBrowser.setList(g_AllMapsByType[getType()][getFilter()]); | |||||
Done Inline Actions(Such helper functions are a bit messy, ideally use a data structure that avoids the need for such transformations. prepareForDropdown is something similar, perhaps the same, I didn't check.) elexis: (Such helper functions are a bit messy, ideally use a data structure that avoids the need for… | |||||
Done Inline ActionsI did it thinking of prepareForDropdown as is takes data from the same source structure and transforms it as a more useful one for this case. nani: I did it thinking of `prepareForDropdown` as is takes data from the same source structure and… | |||||
g_MapBrowser.goToPage(0); | |||||
} | |||||
}, | |||||
"mapFilterDropdown": { | |||||
"list": GameMap.filtersList.map(filter => GameMap.filters[filter].name), | |||||
"list_data": GameMap.filtersList, | |||||
Done Inline ActionsI suspect these two functions can become one function with an argument that is +1 or -1, and the zoom level foo = (foo + direction) % max elexis: I suspect these two functions can become one function with an argument that is +1 or -1, and… | |||||
Done Inline ActionsImho, in this case is much nicer to have two separate functions for each case. nani: Imho, in this case is much nicer to have two separate functions for each case. | |||||
"onSelectionChange": () => | |||||
{ | |||||
if (!getType() || !getFilter()) | |||||
return; | |||||
g_MapBrowser.setList(g_AllMapsByType[getType()][getFilter()]); | |||||
g_MapBrowser.goToPage(0); | |||||
} | |||||
} | |||||
}; | |||||
function init(data) | |||||
{ | |||||
// Sets map type, filter depending of data input | |||||
let type = data && data.map ? data.map.type : "random"; | |||||
Done Inline Actions!length !caption.trim() elexis: !length !caption.trim() | |||||
let filter = data && data.map ? data.map.filter : "default"; | |||||
initData(type, filter); | |||||
initGUIObjects(data, type, filter); | |||||
initActions(); | |||||
} | |||||
function initData(type, filter) | |||||
{ | |||||
// Load all maps | |||||
Done Inline ActionsThose hardcoded magic numbers are faux-pas. It should read something like 4:3, 16:9, 400:300, there is already a function that sets the mappreview image, reusing that would avoid that elexis: Those hardcoded magic numbers are faux-pas. It should read something like 4:3, 16:9, 400:300… | |||||
Done Inline ActionsChanged them as fractions 400/512 300/512 nani: Changed them as fractions 400/512 300/512 | |||||
for (let mapType in GameMap.types) | |||||
g_AllMapsByType[mapType] = GameMap.getMapsByFilter(GameMap.getMapsOfType(mapType)); | |||||
Done Inline ActionsBut this also deletes the input when the user wants to correct a typo. Perhaps it's nicer to have searchboxes globally consistent (mod page, lobby page for instance) elexis: But this also deletes the input when the user wants to correct a typo. Perhaps it's nicer to… | |||||
Done Inline ActionsIt does not. Is just stores last input the real input is unchanged. When pressed will check to compare the real caption with the this.lastCaption "copy". nani: It does not. Is just stores last input the real input is unchanged. When pressed will check to… | |||||
Done Inline ActionsSounds weird and I didn't try to understand it, but why does this check for mouse presses when the caption doesn't change upon clicks; just subscribe to OnPress? You can find other available events in SendEvent lines in CInput.cpp. For example the new textedit that I already forgot about. elexis: Sounds weird and I didn't try to understand it, but why does this check for mouse presses when… | |||||
Done Inline ActionsOh, didn't know of textedit event. But either way, the map list can change while text still being on the text input (by the dropdowns) so it needs up update the list to the search list if the search box is selected. Maybe would be easier to understand if you try the patch search and use the drop downs at the same time. nani: Oh, didn't know of textedit event. But either way, the map list can change while text still… | |||||
Done Inline ActionsWell I didn't even try to understand yet. Mostly wondering why this has to go through onTick rather than just calling an update function if the event of your needs is detected? elexis: Well I didn't even try to understand yet. Mostly wondering why this has to go through `onTick`… | |||||
} | |||||
function initGUIObjects(data, type, filter) | |||||
{ | |||||
let mapList = g_AllMapsByType[type][filter]; | |||||
//"random" is not a map. Map name from gamesetup has path included | |||||
let fullMapName = data && data.map.name != "random" ? data.map.name : mapList[0].file.path + mapList[0].file.name; | |||||
let mapListSelectedIndex = mapList.map(map => map.file.path + map.file.name).indexOf(fullMapName); | |||||
g_MapBrowser = new GridBrowser("MapBrowserContainer", "currentPageNum", mapList, mapListSelectedIndex, g_MapZoom.getSize(), childFunction); | |||||
// Define normal objects | |||||
for (let objectName in g_GUIObjects) | |||||
Done Inline Actions!caption === over == only when the type check actually makes a difference elexis: !caption
`===` over `==` only when the type check actually makes a difference | |||||
for (let parameter in g_GUIObjects[objectName]) | |||||
Engine.GetGUIObjectByName(objectName)[parameter] = g_GUIObjects[objectName][parameter]; | |||||
// Needs to be set after the other data has been defined. | |||||
Engine.GetGUIObjectByName("mapTypeDropdown").selected = g_GUIObjects["mapTypeDropdown"].list_data.indexOf(type); | |||||
Done Inline Actions(!caption) elexis: (!caption) | |||||
Done Inline Actions+1 eae ffffffff: +1 eae | |||||
Done Inline ActionsAs is comparing strings I think is more clear the comparison. nani: As is comparing strings I think is more clear the comparison. | |||||
Done Inline Actionselexis means its done his way everywhere in the code bc its shorter eae ffffffff: elexis means its done his way everywhere in the code bc its shorter eae | |||||
Done Inline Actions
Same is true for const in local scope btw. elexis: > elexis means its done his way everywhere in the code bc its shorter eae
* (not my way, I… | |||||
Done Inline Actions
so it became ur way eae ffffffff: > (not my way, I learnt it from others too)
so it became ur way eae | |||||
Engine.GetGUIObjectByName("mapFilterDropdown").selected = g_GUIObjects["mapFilterDropdown"].list_data.indexOf(filter); | |||||
} | |||||
function initActions() | |||||
{ | |||||
// Simulate a click from mouse | |||||
if (g_MapBrowser.getPageIndexOfSelected() != -1) | |||||
Done Inline Actionsfor (let filter in g_Maps.filters) elexis: for (let filter in g_Maps.filters) | |||||
g_MapBrowser.children[g_MapBrowser.getPageIndexOfSelected()].onMouseLeftPress(); | |||||
Engine.GetGUIObjectByName("mapsSearchBox").focus(); | |||||
} | |||||
// Handles the behaviour of each map in the map browser grid | |||||
function childFunction(map, pageIndex, pageList, listIndex, list, selectedChild, childObject, pageChildObjectList, childObjectList) | |||||
Done Inline ActionsDidn't check if it works, but maybe concat elexis: Didn't check if it works, but maybe concat | |||||
{ | |||||
if (!map.loaded) | |||||
map.load(); | |||||
Done Inline Actions(These two statements on a single line. We try to avoid .foo on a separate line, or rather wanting to avoid x = foo leaving a remote impression of a missing semicolon / being a complete statement) elexis: (These two statements on a single line. We try to avoid `.foo` on a separate line, or rather… | |||||
let getObject = (name, index) => Engine.GetGUIObjectByName(name + "[" + (index === undefined ? pageIndex : index) + "]"); | |||||
let mapPreview = getObject("mapPreview"); | |||||
let mapButton = getObject("mapButton"); | |||||
let mapBox = getObject("mapBox"); | |||||
let mapName = getObject("mapName"); | |||||
let animationButtonSelected = { "color": { "a": 100 }, "size": "4 4 -4 -4", }; | |||||
let animationButtonUnselected = { "color": { "a": 0 }, "size": "10 10 -10 -10" }; | |||||
// Outline if this box is the current selected map otherwise remove outline | |||||
selectedChild[pageIndex] = !g_MapSelected.map ? false : g_MapSelected.map.file.name == map.file.name; | |||||
animate(mapButton, selectedChild[pageIndex] ? animationButtonSelected : animationButtonUnselected); | |||||
mapName.caption = map.name; | |||||
mapPreview.sprite = map.preview; | |||||
Done Inline ActionsStructure the code in a way that every function has one designated purpose. For procedural code style, one function for the tooltips, one to init each dropdown and then calling them from init. Object oriented code like the objects in gamesetup.js or UnitAI.js has advantages over procedural style, the GUI JS codebase will likely be rewritten to use OOP consistently (so would be nice to rewrite less later.) elexis: Structure the code in a way that every function has one designated purpose. For procedural code… | |||||
childObject.tooltip = map.description; | |||||
Done Inline Actionsinit function should be the first one within a file elexis: `init` function should be the first one within a file | |||||
childObject.onMouseLeftPress = () => | |||||
{ | |||||
g_MapSelected.set(listIndex); | |||||
// Unselect others | |||||
for (let i = 0; i < selectedChild.length; ++i) | |||||
{ | |||||
if (i == pageIndex || selectedChild[i] == 0) | |||||
Done Inline ActionsInstead of passing a boolean that something was selected, it can pass the data only if it should be changed. elexis: Instead of passing a boolean that something was selected, it can pass the data only if it… | |||||
{ continue; } | |||||
animate(getObject("mapButton", i), animationButtonUnselected); | |||||
selectedChild[i] = false; | |||||
} | |||||
selectedChild[pageIndex] = true; | |||||
animate(mapButton, animationButtonSelected); | |||||
animate(mapPreview, { "size": "3 3 -3 -3", "duration": 100 }); | |||||
}; | |||||
childObject.onMouseLeftRelease = () => { animate(mapPreview, { "size": "2 2 -2 -2", "duration": 100 }) }; | |||||
childObject.onMouseLeftDoubleClick = () => | |||||
{ | |||||
g_MapSelected.set(listIndex); | |||||
mapBrowserReturn(true); | |||||
}; | |||||
childObject.onMouseEnter = () => { animate(mapBox, { "size": "-10 -10 10 10" }) }; | |||||
childObject.onMouseLeave = () => { animate(mapBox, { "size": "0 0 0 0" }) }; | |||||
childObject.onMouseWheelUp = () => { g_MapZoom.in(); }, | |||||
childObject.onMouseWheelDown = () => { g_MapZoom.out(); } | |||||
}; | |||||
function getType() | |||||
{ | |||||
let mapTypeDropdown = Engine.GetGUIObjectByName("mapTypeDropdown"); | |||||
if (mapTypeDropdown.selected == -1) | |||||
return; | |||||
return mapTypeDropdown.list_data[mapTypeDropdown.selected]; | |||||
Done Inline Actionsfoo["bar"] = foo.bar elexis: foo["bar"] = foo.bar | |||||
} | |||||
function getFilter() | |||||
{ | |||||
let mapFilterDropdown = Engine.GetGUIObjectByName("mapFilterDropdown"); | |||||
if (mapFilterDropdown.selected == -1) | |||||
return; | |||||
Done Inline ActionsThis looks like it can't work, because it misses either a "return" keyword before "map1" or, preferably needs the braces removed. elexis: This looks like it can't work, because it misses either a "return" keyword before "map1" or… | |||||
Done Inline ActionsRemoved braces. nani: Removed braces.
But I still think having each part separated makes it more digestible. | |||||
return mapFilterDropdown.list_data[mapFilterDropdown.selected]; | |||||
} | |||||
function MapsSearchBoxInput(inputObjectName, inputObjectNameNotice) | |||||
{ | |||||
this.lastTime = Date.now(); | |||||
this.refresh = 100; | |||||
this.lastCaption = ""; | |||||
this.mapsSearchBox = Engine.GetGUIObjectByName(inputObjectName); | |||||
this.mapsSearchBoxNotice = Engine.GetGUIObjectByName(inputObjectNameNotice); | |||||
this.mapsSearchBox.caption = ""; | |||||
Done Inline ActionssetGUITags(text, { "color": foo }) elexis: setGUITags(text, { "color": foo }) | |||||
Done Inline Actionspro :-) ffffffff: pro
:-) | |||||
} | |||||
MapsSearchBoxInput.prototype.onTick = function () | |||||
{ | |||||
let time = Date.now(); | |||||
if (this.lastTime + this.refresh > time) | |||||
return; | |||||
let caption = this.mapsSearchBox.caption.trim().toLowerCase(); | |||||
if (caption == this.lastCaption) | |||||
return; | |||||
Done Inline ActionsThis is a nice way to put it, but even better would be grouping it per object: "guiObject1": { "label": "foo" "tooltip": "bar", "onPress": () => { }, } Like in gamesetup.js for example. This way if one wants to introduce, remove or modify an object, there would only be a single hunk of the file to edit and all behaviors and properties of the GUI object are seen in a single place rather than spread across the file. elexis: This is a nice way to put it, but even better would be grouping it per object:
```… | |||||
Done Inline Actionstotal better but more to understand the code then :-) ffffffff: total better but more to understand the code then
:-) | |||||
Done Inline Actionscant tooltips var be killed? eae ffffffff: cant tooltips var be killed? eae | |||||
if ((this.lastCaption && !caption) || (!this.lastCaption && caption)) | |||||
Done Inline Actions/** * Avoid comments if they don't really add information that isn't obvious enough from the code. * Otherwise use JSdoc for functions, so one can build a html doc and have editors use the information. */ elexis: ```
/**
* Avoid comments if they don't really add information that isn't obvious enough from… | |||||
animate(this.mapsSearchBoxNotice, { "textcolor": { "a": !caption ? "255" : "0" } }); | |||||
if (!caption) | |||||
{ | |||||
// Normal map list filtered by type and filter | |||||
g_MapBrowser.setList(g_AllMapsByType[getType()][getFilter()]); | |||||
} | |||||
else | |||||
{ | |||||
Done Inline Actionshere you assign a lot of JS code to GUI Objects, why not also assign the other onFoo events from the XML code? elexis: here you assign a lot of JS code to GUI Objects, why not also assign the other onFoo events… | |||||
Done Inline ActionsDone. Left <action>'s that are inside the <repeat> section nani: Done. Left <action>'s that are inside the <repeat> section | |||||
Done Inline Actionsmaybe he reuses initAction function in code? :-) ffffffff: maybe he reuses initAction function in code?
:-) | |||||
let filter = getFilter(); | |||||
Done Inline ActionsEngine.GetGUIObjectByName("nextButton").onPress = () => g_mapBrowser.nextPage(); should either be Engine.GetGUIObjectByName("nextButton").onPress = g_mapBrowser.nextPage; or in general, if the return value is unused: Engine.GetGUIObjectByName("nextButton").onPress = () => { g_mapBrowser.nextPage(); }; elexis: ```
Engine.GetGUIObjectByName("nextButton").onPress = () => g_mapBrowser.nextPage();
```… | |||||
// Custom map list filtered by search text and filter type (from all map types) | |||||
// e.g. if filter is set to "all" then all maps of all types get searched | |||||
let allListFilter = []; | |||||
for (let type in GameMap.types) | |||||
if (g_AllMapsByType[type][filter] !== undefined) | |||||
allListFilter = [...allListFilter, ...g_AllMapsByType[type][filter]]; | |||||
let captionSplit = caption.split(" "); | |||||
let mapsWithNotAllTheCaptionWords = (map) => | |||||
{ | |||||
let mapWords = map.name.toLowerCase().split(" "); | |||||
return captionSplit.every(captionWord => mapWords.some(mapWord => mapWord.indexOf(captionWord) == 0)); | |||||
}; | |||||
let byWordOrder = (map1, map2) => { map1.name.toLowerCase().indexOf(caption) < map2.name.toLowerCase().indexOf(caption) ? -1 : 1; } | |||||
g_MapBrowser.setList(allListFilter.filter(mapsWithNotAllTheCaptionWords).sort(byWordOrder)); | |||||
} | |||||
g_MapBrowser.goToPage(0); | |||||
this.lastCaption = caption; | |||||
Done Inline ActionsonTab = nextPage elexis: onTab = nextPage | |||||
Done Inline Actions+1 :-) ffffffff: +1 :-)
if g_mapBrowser.nextPage() is already defined same for lines above :-) | |||||
this.lastTime = time; | |||||
} | |||||
function mapBrowserReturn(sendSelected) | |||||
{ | |||||
// Pop the page before calling the callback, so the callback runs | |||||
// in the parent GUI page's context | |||||
Engine.PopGuiPageCB(sendSelected && !!g_MapSelected.map ? { | |||||
"map": { | |||||
"path": g_MapSelected.map.file.path, | |||||
"name": g_MapSelected.map.file.name, | |||||
"type": g_MapSelected.map.type, | |||||
"filter": g_MapSelected.filter | |||||
} | |||||
} : {}); | |||||
} | |||||
function onTick() | |||||
{ | |||||
Done Inline Actionscan be inlined elexis: can be inlined | |||||
updateTimers(); | |||||
animate.onTick(); | |||||
// Hack so it doesn't process the last keyboard event from parent page in the child page (e.g hotkey, enter ) | |||||
if (g_TickCount == 1) | |||||
g_MapsSearchBoxInput = new MapsSearchBoxInput("mapsSearchBox", "mapsSearchBoxNotice"); | |||||
if (g_TickCount >= 1) | |||||
g_MapsSearchBoxInput.onTick(); | |||||
Done Inline ActionsKeep it bugged and add it to the ticket. I think causative created one for the lobby hotkey producing the chat "l" key. elexis: Keep it bugged and add it to the ticket. I think causative created one for the lobby hotkey… | |||||
Done Inline Actionspro ffffffff: pro | |||||
++g_TickCount; | |||||
} | |||||
Done Inline Actionsmissing newline elexis: missing newline |