Ticket #3205: t3205_enlighten_too_dark_chat_colors_v2.patch
File t3205_enlighten_too_dark_chat_colors_v2.patch, 16.0 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/gui/common/color.js
1 //Some names are special and should always appear in certain colors. 2 var g_FixedPlayerColors = { 3 "system": repeatString(7, "255.0.0."), 4 "@WFGbot": repeatString(7, "255.24.24."), 5 "pyrogenesis": repeatString(2, "97.0.0.") + repeatString(2, "124.0.0.") + "138.0.0." + 6 repeatString(2, "174.0.0.") + repeatString(2, "229.40.0.") + repeatString(2, "243.125.15.") 7 }; 8 9 function colorizePlayername(playername) 10 { 11 let color = g_FixedPlayerColors[playername]; 12 if (color) { 13 color = color.split("."); 14 return ('[color="' + playername.split("").map(function (c, i) color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="') 15 .join("") + '"]').slice(0, -10); 16 } 17 return '[color="' + getPlayerColor(playername.replace(g_modPrefix, "")) + '"]' + playername + '[/color]'; 18 } 19 20 // Generate a (mostly) unique color for this player based on their name. 21 // See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript 22 function getPlayerColor(playername) 23 { 24 // Generate a probably-unique hash for the player name and use that to create a color. 25 let hash = 0; 26 for (let i = 0; i < playername.length; i++) 27 hash = playername.charCodeAt(i) + ((hash << 5) - hash); 28 29 // First create the color in RGB then HSL, clamp the lightness so it's not too dark to read, and then convert back to RGB to display. 30 // The reason for this roundabout method is this algorithm can generate values from 0 to 255 for RGB but only 0 to 100 for HSL; this gives 31 // us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so 32 // we convert to HSL to do the computation. Since our GUI code only displays RGB colors, we have to convert back. 33 let [h, s, l] = ensureMinimumLightness(rgbToHsl(hash >> 24 & 0xFF, hash >> 16 & 0xFF, hash >> 8 & 0xFF)); 34 return hslToRgb(h, s, l).join(" "); 35 } 36 37 function ensureMinimumLightness(hsl) 38 { 39 let [h, s, l] = hsl; 40 return [h, s, Math.max(0.7, l)]; 41 } 42 43 //Ensure `value` is between 0 and 1. 44 function clampColorValue(value) 45 { 46 return Math.abs(1 - Math.abs(value - 1)); 47 } 48 49 // See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion 50 function rgbToHsl(r, g, b) 51 { 52 r /= 255; 53 g /= 255; 54 b /= 255; 55 let max = Math.max(r, g, b), min = Math.min(r, g, b); 56 let h, s, l = (max + min) / 2; 57 58 if (max == min) 59 h = s = 0; // achromatic 60 else 61 { 62 let d = max - min; 63 s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 64 switch (max) 65 { 66 case r: h = (g - b) / d + (g < b ? 6 : 0); break; 67 case g: h = (b - r) / d + 2; break; 68 case b: h = (r - g) / d + 4; break; 69 } 70 h /= 6; 71 } 72 73 return [h, s, l]; 74 } 75 76 function hslToRgb(h, s, l) 77 { 78 function hue2rgb(p, q, t) 79 { 80 if (t < 0) t += 1; 81 if (t > 1) t -= 1; 82 if (t < 1/6) return p + (q - p) * 6 * t; 83 if (t < 1/2) return q; 84 if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; 85 return p; 86 } 87 88 [h, s, l] = [h, s, l].map(clampColorValue); 89 let r, g, b; 90 91 if (s == 0) 92 r = g = b = l; // achromatic 93 else { 94 let q = l < 0.5 ? l * (1 + s) : l + s - l * s; 95 let p = 2 * l - q; 96 r = hue2rgb(p, q, h + 1/3); 97 g = hue2rgb(p, q, h); 98 b = hue2rgb(p, q, h - 1/3); 99 } 100 101 return [r, g, b].map(function (n) Math.round(n * 255)); 102 } 103 104 function repeatString(times, string) { 105 return Array(times + 1).join(string); 106 } -
binaries/data/mods/public/gui/gamesetup/gamesetup.js
1712 1712 var mapData = loadMapData(mapName); 1713 1713 var mapSettings = (mapData && mapData.settings ? mapData.settings : {}); 1714 1714 var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {}; 1715 1715 var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {}; 1716 1716 1717 color = rgbToGuiColor(getSetting(pData, pDefs, "Color")); 1717 color = getSetting(pData, pDefs, "Color"); 1718 1719 // enlighten colors to improve readability 1720 let [h, s, l] = rgbToHsl(color.r, color.g, color.b); 1721 let [r, g, b] = ensureMinimumLightness(hslToRgb(h, s, l)); 1722 color = rgbToGuiColor({"r": r, "g": g, "b": b}); 1718 1723 } 1719 1724 1720 1725 var formatted; 1721 1726 switch (msg.type) 1722 1727 { -
binaries/data/mods/public/gui/gamesetup/gamesetup.xml
1 1 <?xml version="1.0" encoding="utf-8"?> 2 2 3 3 <objects> 4 4 5 <script file="gui/common/color.js"/> 5 6 <script file="gui/common/network.js"/> 6 7 <script file="gui/common/functions_civinfo.js"/> 7 8 <script file="gui/common/functions_global_object.js"/> 8 9 <script file="gui/common/functions_utility.js"/> 9 10 <script file="gui/gamesetup/gamesetup.js"/> -
binaries/data/mods/public/gui/lobby/lobby.js
16 16 var g_modPrefix = "@"; 17 17 var g_joined = false; 18 18 // Block spammers for 30 seconds. 19 19 var SPAM_BLOCK_LENGTH = 30; 20 20 21 var g_ColorPresence = { 22 "playing": "125 0 0", 23 "gone": "229 76 13", 24 "away": "229 76 13", 25 "available": "0 219 0", 26 "offline": "0 0 0", 27 "unknown": "178 178 178" 28 }; 29 //'waiting' games are highlighted in orange, 'running' in red, and 'init' in green. 30 var g_ColorGame = { 31 "init": "0 219 0", 32 "waiting": "255 127 0", 33 "running": "219 0 0" 34 }; 35 var g_ColorModerator = "0 219 0"; 36 var g_ColorSystemMessage = "150 0 0"; 37 21 38 //////////////////////////////////////////////////////////////////////////////////////////////// 22 39 23 40 function init(attribs) 24 41 { 25 42 // Play menu music … … 273 290 if (role && caller == "lobbylist") 274 291 { 275 292 // Make the role uppercase. 276 293 role = role.charAt(0).toUpperCase() + role.slice(1); 277 294 if (role == "Moderator") 278 role = '[color=" 0 125 0"]' + translate(role) + '[/color]';295 role = '[color="' + g_ColorModerator + '"]' + translate(role) + '[/color]'; 279 296 } 280 297 else 281 298 role = ""; 282 299 283 300 Engine.GetGUIObjectByName("usernameText").caption = user; … … 457 474 var c = 0; 458 475 for (var g of gameList) 459 476 { 460 477 if (!filterGame(g)) 461 478 { 462 // 'waiting' games are highlighted in orange, 'running' in red, and 'init' in green. 463 let name = escapeText(g.name); 464 if (g.state == 'init') 465 name = '[color="0 125 0"]' + name + '[/color]'; 466 else if (g.state == 'waiting') 467 name = '[color="255 127 0"]' + name + '[/color]'; 468 else 469 name = '[color="255 0 0"]' + name + '[/color]'; 479 let name = '[color="' + g_ColorGame[g.state] + '"]' + escapeText(g.name) + '[/color]'; 470 480 list_name.push(name); 471 481 list_ip.push(g.ip); 472 482 list_mapName.push(translate(g.niceMapName)); 473 483 list_mapSize.push(translatedMapSize(g.mapSize)); 474 484 let idx = g_mapTypes.indexOf(g.mapType); … … 504 514 * @return Colorized versions of name, status, and rating. 505 515 */ 506 516 function formatPlayerListEntry(nickname, presence, rating) 507 517 { 508 518 // Set colors based on player status 509 var color; 510 var status; 519 let color = presence in g_ColorPresence ? g_ColorPresence[presence] : g_ColorPresence["unknown"]; 520 let status; 521 511 522 switch (presence) 512 523 { 513 524 case "playing": 514 color = "125 0 0";515 525 status = translate("Busy"); 516 526 break; 517 527 case "gone": 518 528 case "away": 519 color = "229 76 13";520 529 status = translate("Away"); 521 530 break; 522 531 case "available": 523 color = "0 125 0";524 532 status = translate("Online"); 525 533 break; 526 534 case "offline": 527 color = "0 0 0";528 535 status = translate("Offline"); 529 536 break; 530 537 default: 531 538 warn(sprintf("Unknown presence '%(presence)s'", { presence: presence })); 532 color = "178 178 178";533 539 status = translateWithContext("lobby presence", "Unknown"); 534 540 break; 535 541 } 536 542 // Center the unrated symbol. 537 543 if (rating == "-") … … 539 545 var formattedStatus = '[color="' + color + '"]' + status + "[/color]"; 540 546 var formattedRating = '[color="' + color + '"]' + rating + "[/color]"; 541 547 var role = Engine.LobbyGetPlayerRole(nickname); 542 548 if (role == "moderator") 543 549 nickname = g_modPrefix + nickname; 544 var formattedName = colorPlayerName(nickname);550 let formattedName = colorizePlayername(nickname); 545 551 546 552 // Push this player's name and status onto the list 547 553 return [formattedName, formattedStatus, formattedRating]; 548 554 } 549 555 … … 768 774 break; 769 775 case "system": 770 776 switch (message.level) 771 777 { 772 778 case "standard": 773 addChatMessage({ "from": "system", "text": text, "color": "150 0 0"});779 addChatMessage({ "from": "system", "text": text, "color": g_ColorSystemMessage }); 774 780 if (message.text == "disconnected") 775 781 { 776 782 // Clear the list of games and the list of players 777 783 updateGameList(); 778 784 updateLeaderboard(); … … 784 790 { 785 791 Engine.GetGUIObjectByName("hostButton").enabled = true; 786 792 } 787 793 break; 788 794 case "error": 789 addChatMessage({ "from": "system", "text": text, "color": "150 0 0"});795 addChatMessage({ "from": "system", "text": text, "color": g_ColorSystemMessage }); 790 796 break; 791 797 case "internal": 792 798 switch (message.text) 793 799 { 794 800 case "gamelist updated": … … 897 903 if (!msg.datetime) 898 904 msg.datetime = null; 899 905 900 906 // Highlight local user's nick 901 907 if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from) 902 msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), color PlayerName(g_Name));908 msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorizePlayername(g_Name)); 903 909 904 910 // Run spam test if it's not a historical message 905 911 if (!msg.datetime) 906 912 updateSpamMonitor(msg.from); 907 913 if (isSpam(msg.text, msg.from)) … … 938 944 */ 939 945 function ircFormat(text, from, color, key, datetime) 940 946 { 941 947 // Generate and apply color to uncolored names, 942 948 if (!color && from) 943 var coloredFrom = color PlayerName(from);949 var coloredFrom = colorizePlayername(from); 944 950 else if (color && from) 945 951 var coloredFrom = '[color="' + color + '"]' + from + "[/color]"; 946 952 947 953 // Handle commands allowed past handleSpecialCommand. 948 954 if (text[0] == '/') … … 1087 1093 stats[0] = 0; 1088 1094 } 1089 1095 } 1090 1096 1091 1097 } 1092 1093 /* Utilities */1094 // Generate a (mostly) unique color for this player based on their name.1095 // See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript1096 function getPlayerColor(playername)1097 {1098 // Generate a probably-unique hash for the player name and use that to create a color.1099 var hash = 0;1100 for (var i = 0; i < playername.length; i++)1101 hash = playername.charCodeAt(i) + ((hash << 5) - hash);1102 1103 // First create the color in RGB then HSL, clamp the lightness so it's not too dark to read, and then convert back to RGB to display.1104 // The reason for this roundabout method is this algorithm can generate values from 0 to 255 for RGB but only 0 to 100 for HSL; this gives1105 // us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so1106 // we convert to HSL to do the computation. Since our GUI code only displays RGB colors, we have to convert back.1107 var [h, s, l] = rgbToHsl(hash >> 24 & 0xFF, hash >> 16 & 0xFF, hash >> 8 & 0xFF);1108 return hslToRgb(h, s, Math.max(0.4, l)).join(" ");1109 }1110 1111 function repeatString(times, string) {1112 return Array(times + 1).join(string);1113 }1114 1115 // Some names are special and should always appear in certain colors.1116 var fixedColors = { "system": repeatString(7, "255.0.0."), "@WFGbot": repeatString(7, "255.24.24."),1117 "pyrogenesis": repeatString(2, "97.0.0.") + repeatString(2, "124.0.0.") + "138.0.0." +1118 repeatString(2, "174.0.0.") + repeatString(2, "229.40.0.") + repeatString(2, "243.125.15.") };1119 function colorPlayerName(playername)1120 {1121 var color = fixedColors[playername];1122 if (color) {1123 color = color.split(".");1124 return ('[color="' + playername.split("").map(function (c, i) color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="')1125 .join("") + '"]').slice(0, -10);1126 }1127 return '[color="' + getPlayerColor(playername.replace(g_modPrefix, "")) + '"]' + playername + '[/color]';1128 }1129 1130 // Ensure `value` is between 0 and 1.1131 function clampColorValue(value)1132 {1133 return Math.abs(1 - Math.abs(value - 1));1134 }1135 1136 // See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion1137 function rgbToHsl(r, g, b)1138 {1139 r /= 255;1140 g /= 255;1141 b /= 255;1142 var max = Math.max(r, g, b), min = Math.min(r, g, b);1143 var h, s, l = (max + min) / 2;1144 1145 if (max == min)1146 h = s = 0; // achromatic1147 else1148 {1149 var d = max - min;1150 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);1151 switch (max)1152 {1153 case r: h = (g - b) / d + (g < b ? 6 : 0); break;1154 case g: h = (b - r) / d + 2; break;1155 case b: h = (r - g) / d + 4; break;1156 }1157 h /= 6;1158 }1159 1160 return [h, s, l];1161 }1162 1163 function hslToRgb(h, s, l)1164 {1165 function hue2rgb(p, q, t)1166 {1167 if (t < 0) t += 1;1168 if (t > 1) t -= 1;1169 if (t < 1/6) return p + (q - p) * 6 * t;1170 if (t < 1/2) return q;1171 if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;1172 return p;1173 }1174 1175 [h, s, l] = [h, s, l].map(clampColorValue);1176 var r, g, b;1177 1178 if (s == 0)1179 r = g = b = l; // achromatic1180 else {1181 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;1182 var p = 2 * l - q;1183 r = hue2rgb(p, q, h + 1/3);1184 g = hue2rgb(p, q, h);1185 b = hue2rgb(p, q, h - 1/3);1186 }1187 1188 return [r, g, b].map(function (n) Math.round(n * 255));1189 }1190 1191 (function () {1192 function hexToRgb(hex) {1193 return parseInt(hex.slice(0, 2), 16) + "." + parseInt(hex.slice(2, 4), 16) + "." + parseInt(hex.slice(4, 6), 16) + ".";1194 }1195 function r(times, hex) {1196 return repeatString(times, hexToRgb(hex));1197 }1198 1199 fixedColors["Twilight_Sparkle"] = r(2, "d19fe3") + r(2, "b689c8") + r(2, "a76bc2") +1200 r(4, "263773") + r(2, "131f46") + r(2, "662d8a") + r(2, "ed438a");1201 fixedColors["Applejack"] = r(3, "ffc261") + r(3, "efb05d") + r(3, "f26f31");1202 fixedColors["Rarity"] = r(1, "ebeff1") + r(1, "dee3e4") + r(1, "bec2c3") +1203 r(1, "83509f") + r(1, "4b2568") + r(1, "4917d6");1204 fixedColors["Rainbow_Dash"] = r(2, "ee4144") + r(1, "f37033") + r(1, "fdf6af") +1205 r(1, "62bc4d") + r(1, "1e98d3") + r(2, "672f89") + r(1, "9edbf9") +1206 r(1, "88c4eb") + r(1, "77b0e0") + r(1, "1e98d3");1207 fixedColors["Pinkie_Pie"] = r(2, "f3b6cf") + r(2, "ec9dc4") + r(4, "eb81b4") +1208 r(1, "ed458b") + r(1, "be1d77");1209 fixedColors["Fluttershy"] = r(2, "fdf6af") + r(2, "fee78f") + r(2, "ead463") +1210 r(2, "f3b6cf") + r(2, "eb81b4");1211 fixedColors["Sweetie_Belle"] = r(2, "efedee") + r(3, "e2dee3") + r(3, "cfc8d1") +1212 r(2, "b28dc0") + r(2, "f6b8d2") + r(1, "795b8a");1213 fixedColors["Apple_Bloom"] = r(2, "f4f49b") + r(2, "e7e793") + r(2, "dac582") +1214 r(2, "f46091") + r(2, "f8415f") + r(1, "c52451");1215 fixedColors["Scootaloo"] = r(2, "fbba64") + r(2, "f2ab56") + r(2, "f37003") +1216 r(2, "bf5d95") + r(1, "bf1f79");1217 fixedColors["Luna"] = r(1, "7ca7fa") + r(1, "5d6fc1") + r(1, "656cb9") + r(1, "393993");1218 fixedColors["Celestia"] = r(1, "fdfafc") + r(1, "f7eaf2") + r(1, "d99ec5") +1219 r(1, "00aec5") + r(1, "f7c6dc") + r(1, "98d9ef") + r(1, "ced7ed") + r(1, "fed17b");1220 })(); -
binaries/data/mods/public/gui/lobby/lobby.xml
1 1 <?xml version="1.0" encoding="utf-8"?> 2 2 3 3 <objects> 4 4 <script file="gui/common/functions_global_object.js"/> 5 5 <script file="gui/common/functions_utility.js"/> 6 <script file="gui/common/color.js"/> 6 7 <script file="gui/common/timer.js"/> 7 8 <script file="gui/common/music.js"/> 8 9 9 <script file="gui/lobby/lobby.js"/> 10 10 11 11 <object type="image" style="ModernWindow" size="0 0 100% 100%" name="lobbyWindow"> 12 12 13 13 <object style="ModernLabelText" type="text" size="50%-128 0%+4 50%+128 36">