This diff tries to address two infamously annoying aspects of the gamesetup:
(1) Despite the 50+ cleanup commits in december 2015, a significant portion of it's code is still duplicated.
(2) If a developer wants to add a new gamesetup option, he has to edit both the XML document and the JS code in various places whose logic is puzzling to those who didn't get their handy dirty before.
As topping on the cake, the diff also implements:
(3) Singleplayer games with bots only
(4) Autocompleting of option titles and values (Victory Condition Regicide please!)
The diff isn't a jack of all trades device ("eierlegende Wollmilchsau") and does not fix the persisting of setting bugs (not only the loading of that one file but also occuring when changing random maps) and does not implement settings that are only valid on some maps. However this diff is a crucial prepration of implementing that and already very large to review.
(1) For an example of duplication, consider the initMapTypes, initMapFilters, initNumberOfPlayers, initGameSpeed, initPopulationCaps, initStartingResources, initCeasefire, initVictoryConditions, initVictoryDurations, initMapSizes functions who all do the same initialization of a dropdown GUI object.
But there is a typical duplication catch. Due to the selected / onSelectionChange order, some of these dropdowns set defaults, others don't.
(2) For an example of having to add ugly gamesetup diffs when adding a new gamesetup option, consider the capture the relic dropdown https://code.wildfiregames.com/D152#455a9afc or the disable spies checkbox from https://code.wildfiregames.com/D117#455a9afc
With this proposed diff, the XML doesn't have to be changed anymore at all and the JS code will look like this:
"disableSpies": { "title": () => translate("Disable Spies"), "tooltip": () => translate("Disable spies during the game."), "default": () => false, "defined": () => g_GameAttributes.settings.DisableSpies !== undefined, "get": () => g_GameAttributes.settings.DisableSpies, "set": checked => { g_GameAttributes.settings.DisableSpies = checked; }, "enabled": () => g_GameAttributes.mapType != "scenario", },
"victoryDuration": { "title": () => translate("Victory Duration"), "tooltip": () => translate("Number of minutes until the player has won."), "labels": () => g_VictoryDurations.Title, "ids": () => g_VictoryDurations.Duration, "default": () => g_VictoryDurations.Default, "defined": () => g_GameAttributes.settings.VictoryDuration !== undefined, "get": () => g_GameAttributes.settings.VictoryDuration, "select": (idx) => { g_GameAttributes.settings.VictoryDuration = g_VictoryDurations.Duration[idx]; }, "hidden": () => g_GameAttributes.settings.GameType != "wonder" && g_GameAttributes.settings.GameType != "capture_the_relic", "enabled": () => g_GameAttributes.mapType != "scenario", },
Plus one line in the g_OptionOrderGUI stating where this object should appear.
To tackle the persist-match-settings issue, we should likely not save nor broadcast the g_GameAttributes array at all, only broadcast the chosen settings (and derive inherited settings like VictoryScripts on the clientside), so that map specific attributes like the disabledTemplates are not remembered in the first place. However it seems this rewrite would rather suite in a separate diff, so that reviewers don't have to look at (1) and (2) in the same diff.