Ticket #1580: 1580.14.patch

File 1580.14.patch, 9.9 KB (added by elexis, 9 years ago)

Ensures player colors are unique after matching to player_defaults.json.

  • binaries/data/mods/public/gui/common/functions_utility.js

     
    144144    if (alpha)
    145145        ret += " " + alpha;
    146146    return ret;
    147147}
    148148
     149function sameColor(color1, color2)
     150{
     151    return color1.r === color2.r && color1.g === color2.g && color1.b === color2.b;
     152}
     153
     154/**
     155 * Computes the euclidian distance between the two colors.
     156 * The smaller the return value, the close the colors. Zero if identical.
     157 */
     158function colorDistance(color1, color2)
     159{
     160    return Math.sqrt(Math.pow(color2.r - color1.r, 2) + Math.pow(color2.g - color1.g, 2) + Math.pow(color2.b - color1.b, 2));
     161}
     162
    149163// ====================================================================
    150164
    151165/**
    152166 * Convert time in milliseconds to [hh:]mm:ss string representation.
    153167 * @param time Time period in milliseconds (integer)
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

     
    88const g_MapTypes = prepareForDropdown(g_Settings ? g_Settings.MapTypes : undefined);
    99const g_PopulationCapacities = prepareForDropdown(g_Settings ? g_Settings.PopulationCapacities : undefined);
    1010const g_StartingResources = prepareForDropdown(g_Settings ? g_Settings.StartingResources : undefined);
    1111const g_VictoryConditions = prepareForDropdown(g_Settings ? g_Settings.VictoryConditions : undefined);
    1212
     13// All colors except gaia
     14const g_PlayerColors = initPlayerDefaults().slice(1).map(pData => pData.Color);
     15
    1316////////////////////////////////////////////////////////////////////////////////////////////////
    1417
    1518// Is this is a networked game, or offline
    1619var g_IsNetworked;
    1720
     
    364367                g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
    365368
    366369            updateGameAttributes();
    367370        };
    368371
     372        // Populate color drop-down lists.
     373        var colorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]");
     374        colorPicker.list = g_PlayerColors.map(color => '[color="' + color.r + ' ' + color.g + ' ' + color.b + '"] ■[/color]');
     375        colorPicker.list_data = g_PlayerColors.map((color, index) => index);
     376        colorPicker.selected = -1;
     377        colorPicker.onSelectionChange = function() { selectPlayerColor(playerSlot, this.selected); };
     378
    369379        // Set events
    370380        var civ = Engine.GetGUIObjectByName("playerCiv["+i+"]");
    371381        civ.onSelectionChange = function() {
    372382            if ((this.selected != -1)&&(g_GameAttributes.mapType !== "scenario"))
    373383                g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];
     
    875885    }
    876886
    877887    updateGameAttributes();
    878888}
    879889
     890/**
     891 *  Assigns the given color to that player.
     892 */
     893function selectPlayerColor(playerSlot, colorIndex)
     894{
     895    if (colorIndex == -1)
     896        return;
     897
     898    var playerData = g_GameAttributes.settings.PlayerData;
     899
     900    // If someone else has that color, give that player the old color
     901    var pData = playerData.find(pData => sameColor(g_PlayerColors[colorIndex], pData.Color));
     902    if (pData)
     903        pData.Color = playerData[playerSlot].Color;
     904
     905    // Assign the new color
     906    playerData[playerSlot].Color = g_PlayerColors[colorIndex];
     907
     908    // Ensure colors are not used twice after increasing the number of players
     909    ensureUniquePlayerColors(playerData);
     910
     911    if (!g_IsInGuiUpdate)
     912        updateGameAttributes();
     913}
     914
     915function ensureUniquePlayerColors(playerData)
     916{
     917    for (let i = playerData.length - 1; i >= 0; --i)
     918    {
     919        // If someone else has that color, assign an unused color
     920        if (playerData.some((pData, j) => i != j && sameColor(playerData[i].Color, pData.Color)))
     921            playerData[i].Color = g_PlayerColors.find(color => playerData.every(pData => !sameColor(color, pData.Color)));
     922    }
     923}
     924
    880925// Called when the user selects a map type from the list
    881926function selectMapType(type)
    882927{
    883928    // Avoid recursion
    884929    if (g_IsInGuiUpdate)
     
    9811026        let victoryIdx = mapSettings.GameType !== undefined && g_VictoryConditions.Name.indexOf(mapSettings.GameType) != -1 ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default;
    9821027        g_GameAttributes.settings.GameType = g_VictoryConditions.Name[victoryIdx];
    9831028        g_GameAttributes.settings.VictoryScripts = g_VictoryConditions.Scripts[victoryIdx];
    9841029    }
    9851030
     1031    // Sanitize player data
     1032    if (mapSettings.PlayerData)
     1033    {
     1034        // Ignore gaia
     1035        if (mapSettings.PlayerData.length && !mapSettings.PlayerData[0])
     1036            mapSettings.PlayerData.shift();
     1037
     1038        // Set default color if no color present
     1039        mapSettings.PlayerData.forEach((pData, index) => {
     1040            if (pData && !pData.Color)
     1041                pData.Color = g_PlayerColors[index];
     1042        });
     1043
     1044        // Replace colors with the best matching color of PlayerDefaults
     1045        mapSettings.PlayerData.forEach((pData, index) => {
     1046            let colorDistances = g_PlayerColors.map(color => colorDistance(color, pData.Color));
     1047            let smallestDistance = colorDistances.find(distance => colorDistances.every(distance2 => (distance2 >= distance)));
     1048            pData.Color = g_PlayerColors.find(color => colorDistance(color, pData.Color) == smallestDistance);
     1049        });
     1050       
     1051        ensureUniquePlayerColors(mapSettings.PlayerData);
     1052    }
     1053
    9861054    // Copy any new settings
    9871055    g_GameAttributes.map = name;
    9881056    g_GameAttributes.script = mapSettings.Script;
    9891057    if (g_GameAttributes.map !== "random")
    9901058        for (var prop in mapSettings)
    9911059            g_GameAttributes.settings[prop] = mapSettings[prop];
    9921060
    993     // Ignore gaia
    994     if (g_GameAttributes.settings.PlayerData.length && !g_GameAttributes.settings.PlayerData[0])
    995         g_GameAttributes.settings.PlayerData.shift();
    996 
    9971061    // Use default AI if the map doesn't specify any explicitly
    9981062    for (var i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i)
    9991063    {
    10001064        if (!('AI' in g_GameAttributes.settings.PlayerData[i]))
    10011065            g_GameAttributes.settings.PlayerData[i].AI = g_DefaultPlayerData[i].AI;
     
    14151479        // Player data / defaults
    14161480        var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
    14171481        var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
    14181482
    14191483        // Common to all game types
    1420         var color = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
    1421         pColor.sprite = "color:" + color + " 100";
     1484        var color = getSetting(pData, pDefs, "Color");
     1485        pColor.sprite = "color:" + rgbToGuiColor(color) + " 100";
    14221486        pName.caption = translate(getSetting(pData, pDefs, "Name"));
    14231487
    14241488        var team = getSetting(pData, pDefs, "Team");
    14251489        var civ = getSetting(pData, pDefs, "Civ");
    14261490
     
    14551519            pTeam.hidden = false;
    14561520            // Set dropdown values
    14571521            pCiv.selected = (civ ? pCiv.list_data.indexOf(civ) : 0);
    14581522            pTeam.selected = (team !== undefined && team >= 0) ? team+1 : 0;
    14591523        }
     1524
     1525        // Allow host to chose player colors on non-scenario maps
     1526        const pColorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]");
     1527        const pColorPickerHeading = Engine.GetGUIObjectByName("playerColorHeading");
     1528        const canChangeColors = g_IsController && g_GameAttributes.mapType != "scenario";
     1529        pColorPicker.hidden = !canChangeColors;
     1530        pColorPickerHeading.hidden = !canChangeColors;
     1531        if (canChangeColors)
     1532            pColorPicker.selected = g_PlayerColors.findIndex(col => sameColor(col, color));
    14601533    }
    14611534
    14621535    Engine.GetGUIObjectByName("mapInfoDescription").caption = playerString;
    14631536
    14641537    g_IsInGuiUpdate = false;
  • binaries/data/mods/public/gui/gamesetup/gamesetup.xml

     
    4040            <object size="24 49 100%-440 345" type="image" sprite="ModernDarkBoxGold" name="playerAssignmentsPanel">
    4141                <object size="0 6 100% 30">
    4242                    <object name="playerNameHeading" type="text" style="ModernLabelText" size="0 0 22% 100%">
    4343                        <translatableAttribute id="caption">Player Name</translatableAttribute>
    4444                    </object>
    45                     <object name="playerPlacementHeading" type="text" style="ModernLabelText" size="22%+5 0 50%+35 100%">
     45                    <object name="playerColorHeading" type="text" style="ModernLabelText" size="22%+5 0 22%+40 100%">
     46                        <translatableAttribute id="caption">Color</translatableAttribute>
     47                    </object>
     48                    <object name="playerPlacementHeading" type="text" style="ModernLabelText" size="22%+45 0 50%+35 100%">
    4649                        <translatableAttribute id="caption">Player Placement</translatableAttribute>
    4750                    </object>
    4851                    <object name="playerCivHeading" type="text" style="ModernLabelText" size="50%+65 0 85%-26 100%">
    4952                        <translatableAttribute id="caption">Civilization</translatableAttribute>
    5053                    </object>
     
    6770                <object size="1 36 100%-1 100%">
    6871                    <repeat count="8">
    6972                        <object name="playerBox[n]" size="0 0 100% 32" hidden="true">
    7073                            <object name="playerColor[n]" type="image" size="0 0 100% 100%"/>
    7174                            <object name="playerName[n]" type="text" style="ModernLabelText" size="0 2 22% 30"/>
    72                             <object name="playerAssignment[n]" type="dropdown" style="ModernDropDown" size="22%+5 2 50%+35 30" tooltip_style="onscreenToolTip">
     75                            <object name="playerColorPicker[n]" type="dropdown" style="ModernDropDown" size="22%+5 2 22%+33 30" sprite="" scrollbar="false" button_width="22" font="sans-stroke-14" tooltip_style="onscreenToolTip">
     76                                <translatableAttribute id="tooltip">Pick a color.</translatableAttribute>
     77                            </object>
     78                            <object name="playerAssignment[n]" type="dropdown" style="ModernDropDown" size="22%+37 2 50%+35 30" tooltip_style="onscreenToolTip">
    7379                                <translatableAttribute id="tooltip">Select player.</translatableAttribute>
    7480                            </object>
    7581                            <object name="playerAssignmentText[n]" type="text" style="ModernLabelText" size="22%+5 0 50%+35 30"/>
    7682                            <object name="playerConfig[n]" type="button" style="StoneButton" size="50%+40 4 50%+64 28"
    7783                                tooltip_style="onscreenToolTip"