Ticket #3234: wonderOptions.patch

File wonderOptions.patch, 52.5 KB (added by svott, 8 years ago)
  • binaries/data/mods/public/gui/common/settings.js

     
    1 /**
    2  * The maximum number of players that the engine supports.
    3  * TODO: Maybe we can support more than 8 players sometime.
    4  */
    5 const g_MaxPlayers = 8;
    6 
    7 /**
    8  * The maximum number of teams allowed.
    9  */
    10 const g_MaxTeams = 4;
    11 
    12 /**
    13  * Directory containing all editable settings.
    14  */
    15 const g_SettingsDirectory = "simulation/data/settings/";
    16 
    17 /**
    18  * An object containing all values given by setting name.
    19  * Used by lobby, gamesetup, session, summary screen and replay menu.
    20  */
    21 const g_Settings = loadSettingsValues();
    22 
    23 /**
    24  * Loads and translates all values of all settings which
    25  * can be configured by dropdowns in the gamesetup.
    26  *
    27  * @returns {Object|undefined}
    28  */
    29 function loadSettingsValues()
    30 {
    31     var settings = {
    32         "AIDescriptions": loadAIDescriptions(),
    33         "AIDifficulties": loadAIDifficulties(),
    34         "Ceasefire": loadCeasefire(),
    35         "GameSpeeds": loadSettingValuesFile("game_speeds.json"),
    36         "MapTypes": loadMapTypes(),
    37         "MapSizes": loadSettingValuesFile("map_sizes.json"),
    38         "PlayerDefaults": loadPlayerDefaults(),
    39         "PopulationCapacities": loadPopulationCapacities(),
    40         "StartingResources": loadSettingValuesFile("starting_resources.json"),
    41         "VictoryConditions": loadVictoryConditions()
    42     };
    43 
    44     if (Object.keys(settings).some(key => settings[key] === undefined))
    45         return undefined;
    46 
    47     return settings;
    48 }
    49 
    50 /**
    51  * Returns an array of objects reflecting all possible values for a given setting.
    52  *
    53  * @param {string} filename
    54  * @see simulation/data/settings/
    55  * @returns {Array|undefined}
    56  */
    57 function loadSettingValuesFile(filename)
    58 {
    59     var json = Engine.ReadJSONFile(g_SettingsDirectory + filename);
    60 
    61     if (!json || !json.Data)
    62     {
    63         error("Could not load " + filename + "!");
    64         return undefined;
    65     }
    66 
    67     if (json.TranslatedKeys)
    68     {
    69         let keyContext = json.TranslatedKeys;
    70 
    71         if (json.TranslationContext)
    72         {
    73             keyContext = {};
    74             for (let key of json.TranslatedKeys)
    75                  keyContext[key] = json.TranslationContext;
    76         }
    77 
    78         translateObjectKeys(json.Data, keyContext);
    79     }
    80 
    81     return json.Data;
    82 }
    83 
    84 /**
    85  * Loads the descriptions as defined in simulation/ai/.../data.json and loaded by ICmpAIManager.cpp.
    86  *
    87  * @returns {Array}
    88  */
    89 function loadAIDescriptions()
    90 {
    91     var ais = Engine.GetAIs();
    92     translateObjectKeys(ais, ["name", "description"]);
    93     return ais.sort((a, b) => a.data.name.localeCompare(b.data.name));
    94 }
    95 
    96 /**
    97  * Hardcoded, as modding is not supported without major changes.
    98  * Notice the AI code parses the difficulty level by the index, not by name.
    99  *
    100  * @returns {Array}
    101  */
    102 function loadAIDifficulties()
    103 {
    104     return [
    105         {
    106             "Name": "sandbox",
    107             "Title": translateWithContext("aiDiff", "Sandbox")
    108         },
    109         {
    110             "Name": "very easy",
    111             "Title": translateWithContext("aiDiff", "Very Easy")
    112         },
    113         {
    114             "Name": "easy",
    115             "Title": translateWithContext("aiDiff", "Easy")
    116         },
    117         {
    118             "Name": "medium",
    119             "Title": translateWithContext("aiDiff", "Medium"),
    120             "Default": true
    121         },
    122         {
    123             "Name": "hard",
    124             "Title": translateWithContext("aiDiff", "Hard")
    125         },
    126         {
    127             "Name": "very hard",
    128             "Title": translateWithContext("aiDiff", "Very Hard")
    129         }
    130     ];
    131 }
    132 
    133 /**
    134  * Loads available ceasefire settings.
    135  *
    136  * @returns {Array|undefined}
    137  */
    138 function loadCeasefire()
    139 {
    140     var json = Engine.ReadJSONFile(g_SettingsDirectory + "ceasefire.json");
    141 
    142     if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
    143     {
    144         error("Could not load ceasefire.json");
    145         return undefined;
    146     }
    147 
    148     return json.Times.map(timeout => ({
    149         "Duration": timeout,
    150         "Default": timeout == json.Default,
    151         "Title": timeout == 0 ? translateWithContext("ceasefire", "No ceasefire") :
    152             sprintf(translatePluralWithContext("ceasefire", "%(minutes)s minute", "%(minutes)s minutes", timeout), { "minutes": timeout })
    153     }));
    154 }
    155 
    156 /**
    157  * Hardcoded, as modding is not supported without major changes.
    158  *
    159  * @returns {Array}
    160  */
    161 function loadMapTypes()
    162 {
    163     return [
    164         {
    165             "Name": "skirmish",
    166             "Title": translateWithContext("map", "Skirmish"),
    167             "Default": true
    168         },
    169         {
    170             "Name": "random",
    171             "Title": translateWithContext("map", "Random")
    172         },
    173         {
    174             "Name": "scenario",
    175             "Title": translateWithContext("map", "Scenario")
    176         }
    177     ];
    178 }
    179 
    180 /**
    181  * Loads available gametypes.
    182  *
    183  * @returns {Array|undefined}
    184  */
    185 function loadVictoryConditions()
    186 {
    187     const subdir = "victory_conditions/";
    188 
    189     const files = Engine.BuildDirEntList(g_SettingsDirectory + subdir, "*.json", false).map(
    190         file => file.substr(g_SettingsDirectory.length));
    191 
    192     var victoryConditions = files.map(file => {
    193         let vc = loadSettingValuesFile(file);
    194         if (vc)
    195             vc.Name = file.substr(subdir.length, file.length - (subdir + ".json").length);
    196         return vc;
    197     });
    198 
    199     if (victoryConditions.some(vc => vc == undefined))
    200         return undefined;
    201 
    202     // TODO: We might support enabling victory conditions separately sometime.
    203     // Until then, we supplement the endless gametype here.
    204     victoryConditions.push({
    205         "Name": "endless",
    206         "Title": translate("None"),
    207         "Description": translate("Endless Game"),
    208         "Scripts": []
    209     });
    210 
    211     return victoryConditions;
    212 }
    213 
    214 /**
    215  * Loads the default player settings (like civs and colors).
    216  *
    217  * @returns {Array|undefined}
    218  */
    219 function loadPlayerDefaults()
    220 {
    221     var json = Engine.ReadJSONFile(g_SettingsDirectory + "player_defaults.json");
    222     if (!json || !json.PlayerData)
    223     {
    224         error("Could not load player_defaults.json");
    225         return undefined;
    226     }
    227     return json.PlayerData;
    228 }
    229 
    230 /**
    231  * Loads available population capacities.
    232  *
    233  * @returns {Array|undefined}
    234  */
    235 function loadPopulationCapacities()
    236 {
    237     var json = Engine.ReadJSONFile(g_SettingsDirectory + "population_capacities.json");
    238 
    239     if (!json || json.Default === undefined || !json.PopulationCapacities || !Array.isArray(json.PopulationCapacities))
    240     {
    241         error("Could not load population_capacities.json");
    242         return undefined;
    243     }
    244 
    245     return json.PopulationCapacities.map(population => ({
    246         "Population": population,
    247         "Default": population == json.Default,
    248         "Title": population < 10000 ? population : translate("Unlimited")
    249     }));
    250 }
    251 
    252 /**
    253  * Creates an object with all values of that property of the given setting and
    254  * finds the index of the default value.
    255  *
    256  * This allows easy copying of setting values to dropdown lists.
    257  *
    258  * @param {Array} settingValues
    259  * @returns {Object|undefined}
    260  */
    261 function prepareForDropdown(settingValues)
    262 {
    263     if (!settingValues)
    264         return undefined;
    265 
    266     var settings = { "Default": 0 };
    267     for (let index in settingValues)
    268     {
    269         for (let property in settingValues[index])
    270         {
    271             if (property == "Default")
    272                 continue;
    273 
    274             if (!settings[property])
    275                 settings[property] = [];
    276 
    277             // Switch property and index
    278             settings[property][index] = settingValues[index][property];
    279         }
    280 
    281         // Copy default value
    282         if (settingValues[index].Default)
    283             settings.Default = +index;
    284     }
    285     return settings;
    286 }
    287 
    288 /**
    289  * Returns title or placeholder.
    290  *
    291  * @param {string} aiName - for example "petra"
    292  */
    293 function translateAIName(aiName)
    294 {
    295     var description = g_Settings.AIDescriptions.find(ai => ai.id == aiName);
    296     return description ? translate(description.data.name) : translate("Unknown");
    297 }
    298 
    299 /**
    300  * Returns title or placeholder.
    301  *
    302  * @param {Number} index - index of AIDifficulties
    303  */
    304 function translateAIDifficulty(index)
    305 {
    306     var difficulty = g_Settings.AIDifficulties[index];
    307     return difficulty ? difficulty.Title : translate("Unknown");
    308 }
    309 
    310 /**
    311  * Returns title or placeholder.
    312  *
    313  * @param {string} mapType - for example "skirmish"
    314  * @returns {string}
    315  */
    316 function translateMapType(mapType)
    317 {
    318     var type = g_Settings.MapTypes.find(t => t.Name == mapType);
    319     return type ? type.Title : translate("Unknown");
    320 }
    321 
    322 /**
    323  * Returns title or placeholder "Default".
    324  *
    325  * @param {Number} mapSize - tilecount
    326  * @returns {string}
    327  */
    328 function translateMapSize(tiles)
    329 {
    330     var mapSize = g_Settings.MapSizes.find(mapSize => mapSize.Tiles == +tiles);
    331     return mapSize ? mapSize.Name : translateWithContext("map size", "Default");
    332 }
    333 
    334 /**
    335  * Returns title or placeholder.
    336  *
    337  * @param {Number} population - for example 300
    338  * @returns {string}
    339  */
    340 function translatePopulationCapacity(population)
    341 {
    342     var popCap = g_Settings.PopulationCapacities.find(p => p.Population == population);
    343     return popCap ? popCap.Title : translate("Unknown");
    344 }
    345 
    346 /**
    347  * Returns title or placeholder.
    348  *
    349  * @param {string} gameType - for example "conquest"
    350  * @returns {string}
    351  */
    352 function translateVictoryCondition(gameType)
    353 {
    354     var vc = g_Settings.VictoryConditions.find(vc => vc.Name == gameType);
    355     return vc ? vc.Title : translate("Unknown");
    356 }
     1/**
     2 * The maximum number of players that the engine supports.
     3 * TODO: Maybe we can support more than 8 players sometime.
     4 */
     5const g_MaxPlayers = 8;
     6
     7/**
     8 * The maximum number of teams allowed.
     9 */
     10const g_MaxTeams = 4;
     11
     12/**
     13 * Directory containing all editable settings.
     14 */
     15const g_SettingsDirectory = "simulation/data/settings/";
     16
     17/**
     18 * An object containing all values given by setting name.
     19 * Used by lobby, gamesetup, session, summary screen and replay menu.
     20 */
     21const g_Settings = loadSettingsValues();
     22
     23/**
     24 * Loads and translates all values of all settings which
     25 * can be configured by dropdowns in the gamesetup.
     26 *
     27 * @returns {Object|undefined}
     28 */
     29function loadSettingsValues()
     30{
     31    var settings = {
     32        "AIDescriptions": loadAIDescriptions(),
     33        "AIDifficulties": loadAIDifficulties(),
     34        "Ceasefire": loadCeasefire(),
     35        "WonderDurations": loadWonderDuration(),
     36        "GameSpeeds": loadSettingValuesFile("game_speeds.json"),
     37        "MapTypes": loadMapTypes(),
     38        "MapSizes": loadSettingValuesFile("map_sizes.json"),
     39        "PlayerDefaults": loadPlayerDefaults(),
     40        "PopulationCapacities": loadPopulationCapacities(),
     41        "StartingResources": loadSettingValuesFile("starting_resources.json"),
     42        "VictoryConditions": loadVictoryConditions()
     43    };
     44
     45    if (Object.keys(settings).some(key => settings[key] === undefined))
     46        return undefined;
     47
     48    return settings;
     49}
     50
     51/**
     52 * Returns an array of objects reflecting all possible values for a given setting.
     53 *
     54 * @param {string} filename
     55 * @see simulation/data/settings/
     56 * @returns {Array|undefined}
     57 */
     58function loadSettingValuesFile(filename)
     59{
     60    var json = Engine.ReadJSONFile(g_SettingsDirectory + filename);
     61
     62    if (!json || !json.Data)
     63    {
     64        error("Could not load " + filename + "!");
     65        return undefined;
     66    }
     67
     68    if (json.TranslatedKeys)
     69    {
     70        let keyContext = json.TranslatedKeys;
     71
     72        if (json.TranslationContext)
     73        {
     74            keyContext = {};
     75            for (let key of json.TranslatedKeys)
     76                 keyContext[key] = json.TranslationContext;
     77        }
     78
     79        translateObjectKeys(json.Data, keyContext);
     80    }
     81
     82    return json.Data;
     83}
     84
     85/**
     86 * Loads the descriptions as defined in simulation/ai/.../data.json and loaded by ICmpAIManager.cpp.
     87 *
     88 * @returns {Array}
     89 */
     90function loadAIDescriptions()
     91{
     92    var ais = Engine.GetAIs();
     93    translateObjectKeys(ais, ["name", "description"]);
     94    return ais.sort((a, b) => a.data.name.localeCompare(b.data.name));
     95}
     96
     97/**
     98 * Hardcoded, as modding is not supported without major changes.
     99 * Notice the AI code parses the difficulty level by the index, not by name.
     100 *
     101 * @returns {Array}
     102 */
     103function loadAIDifficulties()
     104{
     105    return [
     106        {
     107            "Name": "sandbox",
     108            "Title": translateWithContext("aiDiff", "Sandbox")
     109        },
     110        {
     111            "Name": "very easy",
     112            "Title": translateWithContext("aiDiff", "Very Easy")
     113        },
     114        {
     115            "Name": "easy",
     116            "Title": translateWithContext("aiDiff", "Easy")
     117        },
     118        {
     119            "Name": "medium",
     120            "Title": translateWithContext("aiDiff", "Medium"),
     121            "Default": true
     122        },
     123        {
     124            "Name": "hard",
     125            "Title": translateWithContext("aiDiff", "Hard")
     126        },
     127        {
     128            "Name": "very hard",
     129            "Title": translateWithContext("aiDiff", "Very Hard")
     130        }
     131    ];
     132}
     133
     134/**
     135 * Loads available wonder survival times
     136 */
     137function loadWonderDuration()
     138{
     139    var jsonFile = "wonder_times.json";
     140    var json = Engine.ReadJSONFile(g_SettingsDirectory + jsonFile);
     141
     142    if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
     143    {
     144        error("Could not load " + jsonFile);
     145        return undefined;
     146    }
     147
     148    return json.Times.map(duration => ({
     149        "Duration": duration,
     150        "Default": duration == json.Default,
     151        "Title": sprintf(translatePluralWithContext("wonder", "%(minutes)s minute", "%(minutes)s minutes", duration), { "minutes": duration })
     152    }));
     153}
     154
     155/**
     156 * Loads available ceasefire settings.
     157 *
     158 * @returns {Array|undefined}
     159 */
     160function loadCeasefire()
     161{
     162    var json = Engine.ReadJSONFile(g_SettingsDirectory + "ceasefire.json");
     163
     164    if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
     165    {
     166        error("Could not load ceasefire.json");
     167        return undefined;
     168    }
     169
     170    return json.Times.map(timeout => ({
     171        "Duration": timeout,
     172        "Default": timeout == json.Default,
     173        "Title": timeout == 0 ? translateWithContext("ceasefire", "No ceasefire") :
     174            sprintf(translatePluralWithContext("ceasefire", "%(minutes)s minute", "%(minutes)s minutes", timeout), { "minutes": timeout })
     175    }));
     176}
     177
     178/**
     179 * Hardcoded, as modding is not supported without major changes.
     180 *
     181 * @returns {Array}
     182 */
     183function loadMapTypes()
     184{
     185    return [
     186        {
     187            "Name": "skirmish",
     188            "Title": translateWithContext("map", "Skirmish"),
     189            "Default": true
     190        },
     191        {
     192            "Name": "random",
     193            "Title": translateWithContext("map", "Random")
     194        },
     195        {
     196            "Name": "scenario",
     197            "Title": translateWithContext("map", "Scenario")
     198        }
     199    ];
     200}
     201
     202/**
     203 * Loads available gametypes.
     204 *
     205 * @returns {Array|undefined}
     206 */
     207function loadVictoryConditions()
     208{
     209    const subdir = "victory_conditions/";
     210
     211    const files = Engine.BuildDirEntList(g_SettingsDirectory + subdir, "*.json", false).map(
     212        file => file.substr(g_SettingsDirectory.length));
     213
     214    var victoryConditions = files.map(file => {
     215        let vc = loadSettingValuesFile(file);
     216        if (vc)
     217            vc.Name = file.substr(subdir.length, file.length - (subdir + ".json").length);
     218        return vc;
     219    });
     220
     221    if (victoryConditions.some(vc => vc == undefined))
     222        return undefined;
     223
     224    // TODO: We might support enabling victory conditions separately sometime.
     225    // Until then, we supplement the endless gametype here.
     226    victoryConditions.push({
     227        "Name": "endless",
     228        "Title": translate("None"),
     229        "Description": translate("Endless Game"),
     230        "Scripts": []
     231    });
     232
     233    return victoryConditions;
     234}
     235
     236/**
     237 * Loads the default player settings (like civs and colors).
     238 *
     239 * @returns {Array|undefined}
     240 */
     241function loadPlayerDefaults()
     242{
     243    var json = Engine.ReadJSONFile(g_SettingsDirectory + "player_defaults.json");
     244    if (!json || !json.PlayerData)
     245    {
     246        error("Could not load player_defaults.json");
     247        return undefined;
     248    }
     249    return json.PlayerData;
     250}
     251
     252/**
     253 * Loads available population capacities.
     254 *
     255 * @returns {Array|undefined}
     256 */
     257function loadPopulationCapacities()
     258{
     259    var json = Engine.ReadJSONFile(g_SettingsDirectory + "population_capacities.json");
     260
     261    if (!json || json.Default === undefined || !json.PopulationCapacities || !Array.isArray(json.PopulationCapacities))
     262    {
     263        error("Could not load population_capacities.json");
     264        return undefined;
     265    }
     266
     267    return json.PopulationCapacities.map(population => ({
     268        "Population": population,
     269        "Default": population == json.Default,
     270        "Title": population < 10000 ? population : translate("Unlimited")
     271    }));
     272}
     273
     274/**
     275 * Creates an object with all values of that property of the given setting and
     276 * finds the index of the default value.
     277 *
     278 * This allows easy copying of setting values to dropdown lists.
     279 *
     280 * @param {Array} settingValues
     281 * @returns {Object|undefined}
     282 */
     283function prepareForDropdown(settingValues)
     284{
     285    if (!settingValues)
     286        return undefined;
     287
     288    var settings = { "Default": 0 };
     289    for (let index in settingValues)
     290    {
     291        for (let property in settingValues[index])
     292        {
     293            if (property == "Default")
     294                continue;
     295
     296            if (!settings[property])
     297                settings[property] = [];
     298
     299            // Switch property and index
     300            settings[property][index] = settingValues[index][property];
     301        }
     302
     303        // Copy default value
     304        if (settingValues[index].Default)
     305            settings.Default = +index;
     306    }
     307    return settings;
     308}
     309
     310/**
     311 * Returns title or placeholder.
     312 *
     313 * @param {string} aiName - for example "petra"
     314 */
     315function translateAIName(aiName)
     316{
     317    var description = g_Settings.AIDescriptions.find(ai => ai.id == aiName);
     318    return description ? translate(description.data.name) : translate("Unknown");
     319}
     320
     321/**
     322 * Returns title or placeholder.
     323 *
     324 * @param {Number} index - index of AIDifficulties
     325 */
     326function translateAIDifficulty(index)
     327{
     328    var difficulty = g_Settings.AIDifficulties[index];
     329    return difficulty ? difficulty.Title : translate("Unknown");
     330}
     331
     332/**
     333 * Returns title or placeholder.
     334 *
     335 * @param {string} mapType - for example "skirmish"
     336 * @returns {string}
     337 */
     338function translateMapType(mapType)
     339{
     340    var type = g_Settings.MapTypes.find(t => t.Name == mapType);
     341    return type ? type.Title : translate("Unknown");
     342}
     343
     344/**
     345 * Returns title or placeholder "Default".
     346 *
     347 * @param {Number} mapSize - tilecount
     348 * @returns {string}
     349 */
     350function translateMapSize(tiles)
     351{
     352    var mapSize = g_Settings.MapSizes.find(mapSize => mapSize.Tiles == +tiles);
     353    return mapSize ? mapSize.Name : translateWithContext("map size", "Default");
     354}
     355
     356/**
     357 * Returns title or placeholder.
     358 *
     359 * @param {Number} population - for example 300
     360 * @returns {string}
     361 */
     362function translatePopulationCapacity(population)
     363{
     364    var popCap = g_Settings.PopulationCapacities.find(p => p.Population == population);
     365    return popCap ? popCap.Title : translate("Unknown");
     366}
     367
     368/**
     369 * Returns title or placeholder.
     370 *
     371 * @param {string} gameType - for example "conquest"
     372 * @returns {string}
     373 */
     374function translateVictoryCondition(gameType)
     375{
     376    var vc = g_Settings.VictoryConditions.find(vc => vc.Name == gameType);
     377    return vc ? vc.Title : translate("Unknown");
     378}
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

     
    88const g_PopulationCapacities = prepareForDropdown(g_Settings ? g_Settings.PopulationCapacities : undefined);
    99const g_StartingResources = prepareForDropdown(g_Settings ? g_Settings.StartingResources : undefined);
    1010const g_VictoryConditions = prepareForDropdown(g_Settings ? g_Settings.VictoryConditions : undefined);
     11const g_WonderDurations = prepareForDropdown(g_Settings ? g_Settings.WonderDurations : undefined);
    1112
    1213/**
    1314 * All selectable playercolors except gaia.
     
    237238        initPopulationCaps();
    238239        initStartingResources();
    239240        initCeasefire();
     241        initWonderDurations();
    240242        initVictoryConditions();
    241243        initMapSizes();
    242244        initRadioButtons();
     
    293295 */
    294296function resizeMoreOptionsWindow()
    295297{
    296     // For singleplayer reduce the size of more options dialog by three options (cheats, rated game, observer late join = 90px)
    297     if (!g_IsNetworked)
    298     {
    299         Engine.GetGUIObjectByName("moreOptions").size = "50%-200 50%-195 50%+200 50%+160";
    300         Engine.GetGUIObjectByName("hideMoreOptions").size = "50%-70 310 50%+70 336";
    301     }
    302     // For non-lobby multiplayergames reduce the size of the dialog by one option (rated game, 30px)
    303     else if (!Engine.HasXmppClient())
    304     {
    305         Engine.GetGUIObjectByName("moreOptions").size = "50%-200 50%-195 50%+200 50%+220";
    306         Engine.GetGUIObjectByName("hideMoreOptions").size = "50%-70 370 50%+70 396";
    307         Engine.GetGUIObjectByName("optionObserverLateJoin").size = "14 338 94% 366";
    308     }
     298    // For singleplayer reduce the size of more options dialog by three options (cheats, rated game, observer late join = 90px)
     299    if (!g_IsNetworked)
     300    {
     301        let victoryIdx = Math.max(0, g_VictoryConditions.Name.indexOf(g_GameAttributes.settings.GameType || ""));
     302        let victoryTitle = g_VictoryConditions.Title[victoryIdx];
     303        if (victoryTitle == "Wonder") {
     304            Engine.GetGUIObjectByName("wonderEntry").hidden = false;
     305        } else {
     306            Engine.GetGUIObjectByName("wonderEntry").hidden = true;
     307        }
     308        Engine.GetGUIObjectByName("moreOptions").size = "50%-200 50%-195 50%+200 50%+190";
     309        Engine.GetGUIObjectByName("hideMoreOptions").size = "50%-70 340 50%+70 366";
     310    }
     311        // For non-lobby multiplayergames reduce the size of the dialog by one option (rated game, 30px)
     312    else if (!Engine.HasXmppClient())
     313    {
     314        Engine.GetGUIObjectByName("moreOptions").size = "50%-200 50%-195 50%+200 50%+220";
     315        Engine.GetGUIObjectByName("hideMoreOptions").size = "50%-70 370 50%+70 396";
     316        Engine.GetGUIObjectByName("optionObserverLateJoin").size = "14 338 94% 366";
     317    }
    309318}
    310319
    311320function initNumberOfPlayers()
     
    379388    };
    380389}
    381390
     391function initWonderDurations()
     392{
     393    let wonderConditions = Engine.GetGUIObjectByName("wonder");
     394    wonderConditions.list = g_WonderDurations.Title;
     395    wonderConditions.list_data = g_WonderDurations.Duration;
     396    wonderConditions.selected = g_WonderDurations.Default
     397    wonderConditions.onSelectionChange = function() {
     398        if (this.selected != -1) {
     399            g_GameAttributes.settings.WonderDurations = g_WonderDurations.Duration[this.selected];
     400            resizeMoreOptionsWindow();
     401        }
     402
     403        updateGameAttributes();
     404    };
     405}
     406
    382407function initVictoryConditions()
    383408{
    384409    let victoryConditions = Engine.GetGUIObjectByName("victoryCondition");
     
    12211246 */
    12221247function updateGUIObjects()
    12231248{
    1224     g_IsInGuiUpdate = true;
     1249    g_IsInGuiUpdate = true;
    12251250
    1226     let mapSettings = g_GameAttributes.settings;
     1251    let mapSettings = g_GameAttributes.settings;
    12271252
    1228     // These dropdowns don't set values while g_IsInGuiUpdate
    1229     let mapName = g_GameAttributes.map || "";
    1230     let mapFilterIdx = g_MapFilters.findIndex(mapFilter => mapFilter.id == (g_GameAttributes.mapFilter || "default"));
    1231     let mapTypeIdx = g_GameAttributes.mapType !== undefined ? g_MapTypes.Name.indexOf(g_GameAttributes.mapType) : g_MapTypes.Default;
    1232     let gameSpeedIdx = g_GameAttributes.gameSpeed !== undefined ? g_GameSpeeds.Speed.indexOf(g_GameAttributes.gameSpeed) : g_GameSpeeds.Default;
     1253    // These dropdowns don't set values while g_IsInGuiUpdate
     1254    let mapName = g_GameAttributes.map || "";
     1255    let mapFilterIdx = g_MapFilters.findIndex(mapFilter => mapFilter.id == (g_GameAttributes.mapFilter || "default"));
     1256    let mapTypeIdx = g_GameAttributes.mapType !== undefined ? g_MapTypes.Name.indexOf(g_GameAttributes.mapType) : g_MapTypes.Default;
     1257    let gameSpeedIdx = g_GameAttributes.gameSpeed !== undefined ? g_GameSpeeds.Speed.indexOf(g_GameAttributes.gameSpeed) : g_GameSpeeds.Default;
    12331258
    1234     // These dropdowns might set the default (as they ignore g_IsInGuiUpdate)
    1235     let mapSizeIdx = mapSettings.Size !== undefined ? g_MapSizes.Tiles.indexOf(mapSettings.Size) : g_MapSizes.Default;
    1236     let victoryIdx = mapSettings.GameType !== undefined ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default;
    1237     let popIdx = mapSettings.PopulationCap !== undefined ? g_PopulationCapacities.Population.indexOf(mapSettings.PopulationCap) : g_PopulationCapacities.Default;
    1238     let startingResIdx = mapSettings.StartingResources !== undefined ? g_StartingResources.Resources.indexOf(mapSettings.StartingResources) : g_StartingResources.Default;
    1239     let ceasefireIdx = mapSettings.Ceasefire !== undefined ? g_Ceasefire.Duration.indexOf(mapSettings.Ceasefire) : g_Ceasefire.Default;
    1240     let numPlayers = mapSettings.PlayerData ? mapSettings.PlayerData.length : g_MaxPlayers;
     1259    // These dropdowns might set the default (as they ignore g_IsInGuiUpdate)
     1260    let mapSizeIdx = mapSettings.Size !== undefined ? g_MapSizes.Tiles.indexOf(mapSettings.Size) : g_MapSizes.Default;
     1261    let victoryIdx = mapSettings.GameType !== undefined ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default;
     1262    let popIdx = mapSettings.PopulationCap !== undefined ? g_PopulationCapacities.Population.indexOf(mapSettings.PopulationCap) : g_PopulationCapacities.Default;
     1263    let startingResIdx = mapSettings.StartingResources !== undefined ? g_StartingResources.Resources.indexOf(mapSettings.StartingResources) : g_StartingResources.Default;
     1264    let ceasefireIdx = mapSettings.Ceasefire !== undefined ? g_Ceasefire.Duration.indexOf(mapSettings.Ceasefire) : g_Ceasefire.Default;
     1265    let wonderIdx = mapSettings.WonderDurations !== undefined ? g_WonderDurations.Duration.indexOf(mapSettings.WonderDurations) : g_WonderDurations.Default;
     1266    let numPlayers = mapSettings.PlayerData ? mapSettings.PlayerData.length : g_MaxPlayers;
    12411267
    1242     if (g_IsController)
    1243     {
    1244         Engine.GetGUIObjectByName("mapTypeSelection").selected = mapTypeIdx;
    1245         Engine.GetGUIObjectByName("mapFilterSelection").selected = mapFilterIdx;
    1246         Engine.GetGUIObjectByName("mapSelection").selected = Engine.GetGUIObjectByName("mapSelection").list_data.indexOf(mapName);
    1247         Engine.GetGUIObjectByName("mapSize").selected = mapSizeIdx;
    1248         Engine.GetGUIObjectByName("numPlayersSelection").selected = numPlayers - 1;
    1249         Engine.GetGUIObjectByName("victoryCondition").selected = victoryIdx;
    1250         Engine.GetGUIObjectByName("populationCap").selected = popIdx;
    1251         Engine.GetGUIObjectByName("gameSpeed").selected = gameSpeedIdx;
    1252         Engine.GetGUIObjectByName("ceasefire").selected = ceasefireIdx;
    1253         Engine.GetGUIObjectByName("startingResources").selected = startingResIdx;
    1254     }
    1255     else
    1256     {
    1257         Engine.GetGUIObjectByName("mapTypeText").caption = g_MapTypes.Title[mapTypeIdx];
    1258         Engine.GetGUIObjectByName("mapFilterText").caption = g_MapFilters[mapFilterIdx].name;
    1259         Engine.GetGUIObjectByName("mapSelectionText").caption = mapName == "random" ? g_RandomMap : translate(getMapDisplayName(mapName));
    1260         initMapNameList();
    1261     }
     1268    if (g_IsController)
     1269    {
     1270        Engine.GetGUIObjectByName("mapTypeSelection").selected = mapTypeIdx;
     1271        Engine.GetGUIObjectByName("mapFilterSelection").selected = mapFilterIdx;
     1272        Engine.GetGUIObjectByName("mapSelection").selected = Engine.GetGUIObjectByName("mapSelection").list_data.indexOf(mapName);
     1273        Engine.GetGUIObjectByName("mapSize").selected = mapSizeIdx;
     1274        Engine.GetGUIObjectByName("numPlayersSelection").selected = numPlayers - 1;
     1275        Engine.GetGUIObjectByName("victoryCondition").selected = victoryIdx;
     1276        Engine.GetGUIObjectByName("populationCap").selected = popIdx;
     1277        Engine.GetGUIObjectByName("gameSpeed").selected = gameSpeedIdx;
     1278        Engine.GetGUIObjectByName("ceasefire").selected = ceasefireIdx;
     1279        Engine.GetGUIObjectByName("wonder").selected = wonderIdx;
     1280        Engine.GetGUIObjectByName("startingResources").selected = startingResIdx;
     1281    }
     1282    else
     1283    {
     1284        Engine.GetGUIObjectByName("mapTypeText").caption = g_MapTypes.Title[mapTypeIdx];
     1285        Engine.GetGUIObjectByName("mapFilterText").caption = g_MapFilters[mapFilterIdx].name;
     1286        Engine.GetGUIObjectByName("mapSelectionText").caption = mapName == "random" ? g_RandomMap : translate(getMapDisplayName(mapName));
     1287        initMapNameList();
     1288    }
    12621289
    1263     // Can be visible to both host and clients
    1264     Engine.GetGUIObjectByName("mapSizeText").caption = g_GameAttributes.mapType == "random" ? g_MapSizes.LongName[mapSizeIdx] : translate("Default");
    1265     Engine.GetGUIObjectByName("numPlayersText").caption = numPlayers;
    1266     Engine.GetGUIObjectByName("victoryConditionText").caption = g_VictoryConditions.Title[victoryIdx];
    1267     Engine.GetGUIObjectByName("populationCapText").caption = g_PopulationCapacities.Title[popIdx];
    1268     Engine.GetGUIObjectByName("startingResourcesText").caption = g_StartingResources.Title[startingResIdx];
    1269     Engine.GetGUIObjectByName("ceasefireText").caption = g_Ceasefire.Title[ceasefireIdx];
    1270     Engine.GetGUIObjectByName("gameSpeedText").caption = g_GameSpeeds.Title[gameSpeedIdx];
     1290    // Can be visible to both host and clients
     1291    Engine.GetGUIObjectByName("mapSizeText").caption = g_GameAttributes.mapType == "random" ? g_MapSizes.LongName[mapSizeIdx] : translate("Default");
     1292    Engine.GetGUIObjectByName("numPlayersText").caption = numPlayers;
     1293    Engine.GetGUIObjectByName("victoryConditionText").caption = g_VictoryConditions.Title[victoryIdx];
     1294    Engine.GetGUIObjectByName("wonderText").caption = g_WonderDurations.Title[wonderIdx];
     1295    Engine.GetGUIObjectByName("populationCapText").caption = g_PopulationCapacities.Title[popIdx];
     1296    Engine.GetGUIObjectByName("startingResourcesText").caption = g_StartingResources.Title[startingResIdx];
     1297    Engine.GetGUIObjectByName("ceasefireText").caption = g_Ceasefire.Title[ceasefireIdx];
     1298    Engine.GetGUIObjectByName("gameSpeedText").caption = g_GameSpeeds.Title[gameSpeedIdx];
    12711299
    1272     setGUIBoolean("enableCheats", "enableCheatsText", !!mapSettings.CheatsEnabled);
    1273     setGUIBoolean("disableTreasures", "disableTreasuresText", !!mapSettings.DisableTreasures);
    1274     setGUIBoolean("exploreMap", "exploreMapText", !!mapSettings.ExploreMap);
    1275     setGUIBoolean("revealMap", "revealMapText", !!mapSettings.RevealMap);
    1276     setGUIBoolean("lockTeams", "lockTeamsText", !!mapSettings.LockTeams);
    1277     setGUIBoolean("observerLateJoin", "observerLateJoinText", !!mapSettings.ObserverLateJoin);
    1278     setGUIBoolean("enableRating", "enableRatingText", !!mapSettings.RatingEnabled);
     1300    setGUIBoolean("enableCheats", "enableCheatsText", !!mapSettings.CheatsEnabled);
     1301    setGUIBoolean("disableTreasures", "disableTreasuresText", !!mapSettings.DisableTreasures);
     1302    setGUIBoolean("exploreMap", "exploreMapText", !!mapSettings.ExploreMap);
     1303    setGUIBoolean("revealMap", "revealMapText", !!mapSettings.RevealMap);
     1304    setGUIBoolean("lockTeams", "lockTeamsText", !!mapSettings.LockTeams);
     1305    setGUIBoolean("observerLateJoin", "observerLateJoinText", !!mapSettings.ObserverLateJoin);
     1306    setGUIBoolean("enableRating", "enableRatingText", !!mapSettings.RatingEnabled);
    12791307
    1280     Engine.GetGUIObjectByName("cheatWarningText").hidden = !g_IsNetworked || !mapSettings.CheatsEnabled;
     1308    Engine.GetGUIObjectByName("cheatWarningText").hidden = !g_IsNetworked || !mapSettings.CheatsEnabled;
    12811309
    1282     Engine.GetGUIObjectByName("enableCheats").enabled = !mapSettings.RatingEnabled;
    1283     Engine.GetGUIObjectByName("lockTeams").enabled = !mapSettings.RatingEnabled;
     1310    Engine.GetGUIObjectByName("enableCheats").enabled = !mapSettings.RatingEnabled;
     1311    Engine.GetGUIObjectByName("lockTeams").enabled = !mapSettings.RatingEnabled;
    12841312
    1285     // Mapsize completely hidden for non-random maps
    1286     let isRandom = g_GameAttributes.mapType == "random";
    1287     Engine.GetGUIObjectByName("mapSizeDesc").hidden = !isRandom;
    1288     Engine.GetGUIObjectByName("mapSize").hidden = !isRandom || !g_IsController;
    1289     Engine.GetGUIObjectByName("mapSizeText").hidden = !isRandom || g_IsController;
    1290     hideControl("numPlayersSelection", "numPlayersText", isRandom && g_IsController);
     1313    // Mapsize completely hidden for non-random maps
     1314    let isRandom = g_GameAttributes.mapType == "random";
     1315    Engine.GetGUIObjectByName("mapSizeDesc").hidden = !isRandom;
     1316    Engine.GetGUIObjectByName("mapSize").hidden = !isRandom || !g_IsController;
     1317    Engine.GetGUIObjectByName("mapSizeText").hidden = !isRandom || g_IsController;
     1318    hideControl("numPlayersSelection", "numPlayersText", isRandom && g_IsController);
    12911319
    1292     let notScenario = g_GameAttributes.mapType != "scenario" && g_IsController ;
    1293     hideControl("victoryCondition", "victoryConditionText", notScenario);
    1294     hideControl("populationCap", "populationCapText", notScenario);
    1295     hideControl("startingResources", "startingResourcesText", notScenario);
    1296     hideControl("ceasefire", "ceasefireText", notScenario);
    1297     hideControl("revealMap", "revealMapText", notScenario);
    1298     hideControl("exploreMap", "exploreMapText", notScenario);
    1299     hideControl("disableTreasures", "disableTreasuresText", notScenario);
    1300     hideControl("lockTeams", "lockTeamsText", notScenario);
     1320    let notScenario = g_GameAttributes.mapType != "scenario" && g_IsController ;
     1321    hideControl("victoryCondition", "victoryConditionText", notScenario);
     1322    hideControl("populationCap", "populationCapText", notScenario);
     1323    hideControl("startingResources", "startingResourcesText", notScenario);
     1324    hideControl("ceasefire", "ceasefireText", notScenario);
     1325    hideControl("wonder", "wonderText", notScenario);
     1326    hideControl("revealMap", "revealMapText", notScenario);
     1327    hideControl("exploreMap", "exploreMapText", notScenario);
     1328    hideControl("disableTreasures", "disableTreasuresText", notScenario);
     1329    hideControl("lockTeams", "lockTeamsText", notScenario);
    13011330
    1302     setMapDescription();
     1331    setMapDescription();
    13031332
    1304     for (let i = 0; i < g_MaxPlayers; ++i)
    1305     {
    1306         Engine.GetGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
     1333    for (let i = 0; i < g_MaxPlayers; ++i)
     1334    {
     1335        Engine.GetGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
    13071336
    1308         if (i >= numPlayers)
    1309             continue;
     1337        if (i >= numPlayers)
     1338            continue;
    13101339
    1311         let pName = Engine.GetGUIObjectByName("playerName["+i+"]");
    1312         let pAssignment = Engine.GetGUIObjectByName("playerAssignment["+i+"]");
    1313         let pAssignmentText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]");
    1314         let pCiv = Engine.GetGUIObjectByName("playerCiv["+i+"]");
    1315         let pCivText = Engine.GetGUIObjectByName("playerCivText["+i+"]");
    1316         let pTeam = Engine.GetGUIObjectByName("playerTeam["+i+"]");
    1317         let pTeamText = Engine.GetGUIObjectByName("playerTeamText["+i+"]");
    1318         let pColor = Engine.GetGUIObjectByName("playerColor["+i+"]");
     1340        let pName = Engine.GetGUIObjectByName("playerName["+i+"]");
     1341        let pAssignment = Engine.GetGUIObjectByName("playerAssignment["+i+"]");
     1342        let pAssignmentText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]");
     1343        let pCiv = Engine.GetGUIObjectByName("playerCiv["+i+"]");
     1344        let pCivText = Engine.GetGUIObjectByName("playerCivText["+i+"]");
     1345        let pTeam = Engine.GetGUIObjectByName("playerTeam["+i+"]");
     1346        let pTeamText = Engine.GetGUIObjectByName("playerTeamText["+i+"]");
     1347        let pColor = Engine.GetGUIObjectByName("playerColor["+i+"]");
    13191348
    1320         let pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
    1321         let pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
     1349        let pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
     1350        let pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
    13221351
    1323         let color = getSetting(pData, pDefs, "Color");
    1324         pColor.sprite = "color:" + rgbToGuiColor(color) + " 100";
    1325         pName.caption = translate(getSetting(pData, pDefs, "Name"));
     1352        let color = getSetting(pData, pDefs, "Color");
     1353        pColor.sprite = "color:" + rgbToGuiColor(color) + " 100";
     1354        pName.caption = translate(getSetting(pData, pDefs, "Name"));
    13261355
    1327         let team = getSetting(pData, pDefs, "Team");
    1328         let civ = getSetting(pData, pDefs, "Civ");
     1356        let team = getSetting(pData, pDefs, "Team");
     1357        let civ = getSetting(pData, pDefs, "Civ");
    13291358
    1330         pAssignmentText.caption = pAssignment.list[0] ? pAssignment.list[Math.max(0, pAssignment.selected)] : translate("Loading...");
    1331         pCivText.caption = civ == "random" ? g_RandomCiv : (g_CivData[civ] ? g_CivData[civ].Name : "Unknown");
    1332         pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
     1359        pAssignmentText.caption = pAssignment.list[0] ? pAssignment.list[Math.max(0, pAssignment.selected)] : translate("Loading...");
     1360        pCivText.caption = civ == "random" ? g_RandomCiv : (g_CivData[civ] ? g_CivData[civ].Name : "Unknown");
     1361        pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
    13331362
    1334         pCiv.selected = civ ? pCiv.list_data.indexOf(civ) : 0;
    1335         pTeam.selected = team !== undefined && team >= 0 ? team+1 : 0;
     1363        pCiv.selected = civ ? pCiv.list_data.indexOf(civ) : 0;
     1364        pTeam.selected = team !== undefined && team >= 0 ? team+1 : 0;
    13361365
    1337         hideControl("playerAssignment["+i+"]", "playerAssignmentText["+i+"]", g_IsController);
    1338         hideControl("playerCiv["+i+"]", "playerCivText["+i+"]", notScenario);
    1339         hideControl("playerTeam["+i+"]", "playerTeamText["+i+"]", notScenario);
     1366        hideControl("playerAssignment["+i+"]", "playerAssignmentText["+i+"]", g_IsController);
     1367        hideControl("playerCiv["+i+"]", "playerCivText["+i+"]", notScenario);
     1368        hideControl("playerTeam["+i+"]", "playerTeamText["+i+"]", notScenario);
    13401369
    1341         // Allow host to chose player colors on non-scenario maps
    1342         let pColorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]");
    1343         let pColorPickerHeading = Engine.GetGUIObjectByName("playerColorHeading");
    1344         let canChangeColors = g_IsController && g_GameAttributes.mapType != "scenario";
    1345         pColorPicker.hidden = !canChangeColors;
    1346         pColorPickerHeading.hidden = !canChangeColors;
    1347         if (canChangeColors)
    1348             pColorPicker.selected = g_PlayerColors.findIndex(col => sameColor(col, color));
    1349     }
     1370        // Allow host to chose player colors on non-scenario maps
     1371        let pColorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]");
     1372        let pColorPickerHeading = Engine.GetGUIObjectByName("playerColorHeading");
     1373        let canChangeColors = g_IsController && g_GameAttributes.mapType != "scenario";
     1374        pColorPicker.hidden = !canChangeColors;
     1375        pColorPickerHeading.hidden = !canChangeColors;
     1376        if (canChangeColors)
     1377            pColorPicker.selected = g_PlayerColors.findIndex(col => sameColor(col, color));
     1378    }
    13501379
    1351     g_IsInGuiUpdate = false;
     1380    g_IsInGuiUpdate = false;
    13521381
    1353     // Game attributes include AI settings, so update the player list
    1354     updatePlayerList();
     1382    // Game attributes include AI settings, so update the player list
     1383    updatePlayerList();
    13551384
    1356     // We should have everyone confirm that the new settings are acceptable.
    1357     resetReadyData();
     1385    // We should have everyone confirm that the new settings are acceptable.
     1386    resetReadyData();
    13581387}
    13591388
    13601389/**
     
    13621391 */
    13631392function setMapDescription()
    13641393{
    1365     let numPlayers = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData.length : 0;
    1366     let mapName = g_GameAttributes.map || "";
     1394    let numPlayers = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData.length : 0;
     1395    let mapName = g_GameAttributes.map || "";
    13671396
    1368     let victoryIdx = Math.max(0, g_VictoryConditions.Name.indexOf(g_GameAttributes.settings.GameType || ""));
    1369     let victoryTitle = g_VictoryConditions.Title[victoryIdx];
    1370     if (victoryIdx != g_VictoryConditions.Default)
    1371         victoryTitle = "[color=\"" + g_VictoryColor + "\"]" + victoryTitle + "[/color]";
     1397    let victoryIdx = Math.max(0, g_VictoryConditions.Name.indexOf(g_GameAttributes.settings.GameType || ""));
     1398    let victoryTitle = g_VictoryConditions.Title[victoryIdx];
    13721399
    1373     let mapDescription = g_GameAttributes.settings.Description ? translate(g_GameAttributes.settings.Description) : translate("Sorry, no description available.");
    1374     if (mapName == "random")
    1375         mapDescription = translate("Randomly selects a map from the list");
     1400    let mapDescription = g_GameAttributes.settings.Description ? translate(g_GameAttributes.settings.Description) : translate("Sorry, no description available.");
     1401    if (mapName == "random")
     1402        mapDescription = translate("Randomly selects a map from the list");
     1403   
     1404    if (victoryTitle == "Wonder") {
     1405        let time = g_GameAttributes.settings.WonderDurations;
     1406        victoryTitle += " (" + translate("Survival time") + ": ";
     1407        victoryTitle += sprintf(translatePlural("%(duration)s minute", "%(duration)s minutes", time), { "duration": time });
     1408        victoryTitle += ")";
     1409    }
    13761410
    1377     let gameDescription = sprintf(translatePlural("%(number)s player. ", "%(number)s players. ", numPlayers), { "number": numPlayers });
    1378     gameDescription += translate("Victory Condition:") + " " + victoryTitle + ".\n\n";
    1379     gameDescription += mapDescription;
     1411    if (victoryIdx != g_VictoryConditions.Default)
     1412        victoryTitle = "[color=\"" + g_VictoryColor + "\"]" + victoryTitle + "[/color]";
    13801413
    1381     Engine.GetGUIObjectByName("mapInfoName").caption = mapName == "random" ? translateWithContext("map", "Random") : translate(getMapDisplayName(mapName));
    1382     Engine.GetGUIObjectByName("mapInfoDescription").caption = gameDescription;
    1383     setMapPreviewImage("mapPreview", getMapPreview(mapName));
     1414    let gameDescription = sprintf(translatePlural("%(number)s player. ", "%(number)s players. ", numPlayers), { "number": numPlayers });
     1415    gameDescription += translate("Victory Condition:") + " " + victoryTitle + ".\n\n";
     1416    gameDescription += mapDescription;
     1417
     1418    Engine.GetGUIObjectByName("mapInfoName").caption = mapName == "random" ? translateWithContext("map", "Random") : translate(getMapDisplayName(mapName));
     1419    Engine.GetGUIObjectByName("mapInfoDescription").caption = gameDescription;
     1420    setMapPreviewImage("mapPreview", getMapPreview(mapName));
    13841421}
    13851422
    13861423/**
  • binaries/data/mods/public/gui/gamesetup/gamesetup.xml

     
    270270
    271271            <!-- More Options -->
    272272            <object hidden="true" name="moreOptionsFade" type="image" z="60" sprite="ModernFade"/>
    273             <object name="moreOptions" type="image" sprite="ModernDialog" size="50%-200 50%-195 50%+200 50%+250" z="70" hidden="true">
     273            <object name="moreOptions" type="image" sprite="ModernDialog" size="50%-200 50%-120 50%+200 50%+250" z="70" hidden="true">
    274274                <object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
    275275                    <translatableAttribute id="caption">More Options</translatableAttribute>
    276276                </object>
     
    325325                    </object>
    326326                </object>
    327327
    328                 <object size="14 188 94% 216">
     328        <object name="wonderEntry" size="14 188 94% 216">
     329          <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
     330            <translatableAttribute id="caption">Wonder:</translatableAttribute>
     331          </object>
     332          <object name="wonderText" size="40% 0 100% 100%" type="text" style="ModernLeftLabelText"/>
     333          <object name="wonder" size="40%+10 0 100% 28" type="dropdown" style="ModernDropDown" hidden="true" tooltip_style="onscreenToolTip">
     334            <translatableAttribute id="tooltip">Set the survival time after a wonder has build.</translatableAttribute>
     335          </object>
     336        </object>
     337       
     338                <object size="14 218 94% 246">
    329339                    <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
    330340                        <translatableAttribute id="caption" comment="Make sure to differentiate between the revealed map and explored map options!">Revealed Map:</translatableAttribute>
    331341                    </object>
     
    335345                    </object>
    336346                </object>
    337347
    338                 <object size="14 218 94% 246">
     348                <object size="14 248 94% 276">
    339349                    <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
    340350                        <translatableAttribute id="caption" comment="Make sure to differentiate between the revealed map and explored map options!">Explored Map:</translatableAttribute>
    341351                    </object>
     
    345355                    </object>
    346356                </object>
    347357
    348                 <object size="14 248 94% 276">
     358                <object size="14 278 94% 306">
    349359                    <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
    350360                        <translatableAttribute id="caption">Disable Treasures:</translatableAttribute>
    351361                    </object>
     
    355365                    </object>
    356366                </object>
    357367
    358                 <object size="14 278 94% 306">
     368                <object size="14 308 94% 336">
    359369                    <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
    360370                        <translatableAttribute id="caption">Teams Locked:</translatableAttribute>
    361371                    </object>
     
    365375                    </object>
    366376                </object>
    367377
    368                 <object name="optionCheats" size="14 308 94% 336" hidden="true">
     378                <object name="optionCheats" size="14 338 94% 366" hidden="true">
    369379                    <object size="0 0 40% 28" type="text" style="ModernRightLabelText">
    370380                        <translatableAttribute id="caption">Cheats:</translatableAttribute>
    371381                    </object>
     
    375385                    </object>
    376386                </object>
    377387
    378                 <object name="optionRating" size="14 338 94% 366" hidden="true">
     388                <object name="optionRating" size="14 368 94% 396" hidden="true">
    379389                    <object size="0 0 40% 28" hidden="false" type="text" style="ModernRightLabelText">
    380390                        <translatableAttribute id="caption">Rated Game:</translatableAttribute>
    381391                    </object>
     
    385395                    </object>
    386396                </object>
    387397
    388                 <object name="optionObserverLateJoin" size="14 368 94% 396" hidden="true">
     398                <object name="optionObserverLateJoin" size="14 398 94% 426" hidden="true">
    389399                    <object size="0 0 40% 28" type="text" hidden="false" style="ModernRightLabelText">
    390400                        <translatableAttribute id="caption">Late Observer Joins:</translatableAttribute>
    391401                    </object>
     
    400410                    name="hideMoreOptions"
    401411                    type="button"
    402412                    style="StoneButton"
    403                     size="50%-70 400 50%+70 426"
     413                    size="50%-70 430 50%+70 456"
    404414                    tooltip_style="onscreenToolTip"
    405415                    hotkey="cancel"
    406416                >
  • binaries/data/mods/public/maps/scripts/WonderVictory.js

     
    1 Trigger.prototype.CheckWonderVictory = function(data)
    2 {
    3     var ent = data.entity;
    4     var cmpWonder = Engine.QueryInterface(ent, IID_Wonder);
    5     if (!cmpWonder)
    6         return;
    7 
    8     var timer = this.wonderVictoryTimers[ent];
    9     var messages = this.wonderVictoryMessages[ent] || {};
    10 
    11     var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    12     var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    13     // Remove existing messages if any
    14     if (timer)
    15     {
    16         cmpTimer.CancelTimer(timer);
    17         cmpGuiInterface.DeleteTimeNotification(messages.ownMessage);
    18         cmpGuiInterface.DeleteTimeNotification(messages.otherMessage);
    19     }
    20 
    21     if (data.to <= 0)
    22         return;
    23 
    24     // Create new messages, and start timer to register defeat.
    25     var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    26     var numPlayers = cmpPlayerManager.GetNumPlayers();
    27     var cmpPlayer = QueryOwnerInterface(ent, IID_Player);
    28     // Add -1 to notify observers too
    29     var players = [-1];
    30     for (var i = 1; i < numPlayers; i++)
    31         if (i != data.to)
    32             players.push(i);
    33 
    34     var time = cmpWonder.GetTimeTillVictory()*1000;
    35     messages.otherMessage = cmpGuiInterface.AddTimeNotification({
    36         "message": markForTranslation("%(player)s will have won in %(time)s"),
    37         "players": players,
    38         "parameters": {"player": cmpPlayer.GetName()},
    39         "translateMessage": true,
    40         "translateParameters": [],
    41     }, time);
    42     messages.ownMessage = cmpGuiInterface.AddTimeNotification({
    43         "message": markForTranslation("You will have won in %(time)s"),
    44         "players": [data.to],
    45         "translateMessage": true,
    46     }, time);
    47     timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, "MarkPlayerAsWon", time, data.to);
    48 
    49     this.wonderVictoryTimers[ent] = timer;
    50     this.wonderVictoryMessages[ent] = messages;
    51 };
    52 
    53 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
    54 
    55 var data = {"enabled": true};
    56 cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", data);
    57 cmpTrigger.wonderVictoryTimers = {};
    58 cmpTrigger.wonderVictoryMessages = {};
     1Trigger.prototype.CheckWonderVictory = function (data) {
     2
     3    var ent = data.entity;
     4    var cmpWonder = Engine.QueryInterface(ent, IID_Wonder);
     5    if (!cmpWonder) {
     6        return;
     7    }
     8
     9    var timer = this.wonderVictoryTimers[ent];
     10    var messages = this.wonderVictoryMessages[ent] || {};
     11
     12    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     13    var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     14    // Remove existing messages if any
     15    if (timer) {
     16        cmpTimer.CancelTimer(timer);
     17        cmpGuiInterface.DeleteTimeNotification(messages.ownMessage);
     18        cmpGuiInterface.DeleteTimeNotification(messages.otherMessage);
     19    }
     20
     21    if (data.to <= 0)
     22        return;
     23
     24    // Create new messages, and start timer to register defeat.
     25    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     26    var numPlayers = cmpPlayerManager.GetNumPlayers();
     27    var cmpPlayer = QueryOwnerInterface(ent, IID_Player);
     28    // Add -1 to notify observers too
     29    var players = [-1];
     30    for (var i = 1; i < numPlayers; i++)
     31        if (i != data.to)
     32            players.push(i);
     33
     34    var time = cmpWonder.GetTimeTillVictory() * 1000 * 60; // minutes to millisecounds
     35    messages.otherMessage = cmpGuiInterface.AddTimeNotification({
     36        "message": markForTranslation("%(player)s will have won in %(time)s"),
     37        "players": players,
     38        "parameters": { "player": cmpPlayer.GetName () },
     39        "translateMessage": true,
     40        "translateParameters": [],
     41    }, time);
     42    messages.ownMessage = cmpGuiInterface.AddTimeNotification({
     43        "message": markForTranslation("You will have won in %(time)s"),
     44        "players": [data.to],
     45        "translateMessage": true,
     46    }, time);
     47    timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, "MarkPlayerAsWon", time, data.to);
     48
     49    this.wonderVictoryTimers[ent] = timer;
     50    this.wonderVictoryMessages[ent] = messages;
     51};
     52
     53var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     54
     55var data = { "enabled": true };
     56cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", data);
     57cmpTrigger.wonderVictoryTimers = {};
     58cmpTrigger.wonderVictoryMessages = {};
  • binaries/data/mods/public/simulation/components/EndGameManager.js

     
    1919    // Allied victory means allied players can win if victory conditions are met for each of them
    2020    // Would be false for a "last man standing" game (when diplomacy is fully implemented)
    2121    this.alliedVictory = true;
     22
     23    this.wonderDuration = 0;
    2224};
    2325
    2426EndGameManager.prototype.GetGameType = function()
     
    2830
    2931EndGameManager.prototype.SetGameType = function(newGameType)
    3032{
    31     this.gameType = newGameType;
     33    this.gameType = newGameType;
    3234    Engine.BroadcastMessage(MT_GameTypeChanged, {});
    3335};
    3436
     37EndGameManager.prototype.SetWonderDuration = function(wonderDuration)
     38{
     39    this.wonderDuration = wonderDuration;
     40}
     41
     42EndGameManager.prototype.GetWonderDuration = function()
     43{
     44    return this.wonderDuration;
     45}
     46
    3547EndGameManager.prototype.CheckGameType = function(type)
    3648{
    3749    return this.gameType == type;
  • binaries/data/mods/public/simulation/components/Wonder.js

     
    1313
    1414Wonder.prototype.GetTimeTillVictory = function()
    1515{
    16     return +this.template.TimeTillVictory;
     16    //return +this.template.TimeTillVictory;
     17    var cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
     18    return cmpEndGameManager.GetWonderDuration();
    1719};
    1820
    1921Engine.RegisterComponentType(IID_Wonder, "Wonder", Wonder);
  • binaries/data/mods/public/simulation/data/settings/wonder_times.json

     
     1{
     2    "Times": [1, 5, 10, 15, 20, 25, 30],
     3    "Default": 15
     4}
  • binaries/data/mods/public/simulation/helpers/Setup.js

     
    4848
    4949    var cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
    5050    if (settings.GameType)
    51         cmpEndGameManager.SetGameType(settings.GameType);
     51        cmpEndGameManager.SetGameType(settings.GameType);
     52    if (settings.WonderDurations)
     53        cmpEndGameManager.SetWonderDuration(settings.WonderDurations);
    5254
    5355    if (settings.Garrison)
    5456    {