From 1832a9c60fe09b081c4d34fd7396a6cdc923c551 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 25 Jan 2016 21:55:20 +0000 Subject: [PATCH 1/5] Increase the size of the color picker. --- mods/cnc/chrome/color-picker.yaml | 43 +++++++++++++++---------------- mods/d2k/chrome/color-picker.yaml | 42 ++++++++++++++++-------------- mods/ra/chrome/color-picker.yaml | 41 +++++++++++++++-------------- mods/ts/chrome/color-picker.yaml | 42 ++++++++++++++++-------------- 4 files changed, 86 insertions(+), 82 deletions(-) diff --git a/mods/cnc/chrome/color-picker.yaml b/mods/cnc/chrome/color-picker.yaml index 50c7ef19eb..1685118d81 100644 --- a/mods/cnc/chrome/color-picker.yaml +++ b/mods/cnc/chrome/color-picker.yaml @@ -1,45 +1,44 @@ Background@COLOR_CHOOSER: Logic: ColorPickerLogic Background: panel-black - Width: 234 - Height: 105 + Width: 311 + Height: 140 Children: - LogicTicker@ANIMATE_PREVIEW: + Button@RANDOM_BUTTON: + Key: tab + X: 235 + Y: 109 + Width: 70 + Height: 25 + Text: Random Background@HUEBG: Background: panel-black X: 5 Y: 5 - Width: 148 - Height: 13 + Width: 225 + Height: 17 Children: HueSlider@HUE: X: 2 Y: 2 - Width: 144 - Height: 9 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 Ticks: 5 Background@MIXERBG: Background: panel-black X: 5 - Y: 23 - Width: 148 - Height: 76 + Y: 27 + Width: 225 + Height: 107 Children: ColorMixer@MIXER: X: 2 Y: 2 - Width: 144 - Height: 72 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 ActorPreview@PREVIEW: - X: 153 - Y: 1 + X: 230 + Y: 21 Width: 80 Height: 73 - Animate: true - Button@RANDOM_BUTTON: - Key: tab - X: 158 - Y: 74 - Width: 70 - Height: 25 - Text: Random + Animate: true \ No newline at end of file diff --git a/mods/d2k/chrome/color-picker.yaml b/mods/d2k/chrome/color-picker.yaml index b66da7fae3..00ef277e5e 100644 --- a/mods/d2k/chrome/color-picker.yaml +++ b/mods/d2k/chrome/color-picker.yaml @@ -1,43 +1,45 @@ Background@COLOR_CHOOSER: Logic: ColorPickerLogic Background: dialog2 - Width: 234 - Height: 105 + Width: 326 + Height: 140 Children: + Button@RANDOM_BUTTON: + Key: tab + X: 250 + Y: 109 + Width: 70 + Height: 25 + Text: Random + Font: Bold Background@HUEBG: Background: dialog3 X: 5 Y: 5 - Width: 148 - Height: 13 + Width: 240 + Height: 17 Children: HueSlider@HUE: X: 2 Y: 2 - Width: 144 - Height: 9 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 Ticks: 5 Background@MIXERBG: Background: dialog3 X: 5 - Y: 23 - Width: 148 - Height: 76 + Y: 27 + Width: 240 + Height: 107 Children: ColorMixer@MIXER: X: 2 Y: 2 - Width: 144 - Height: 72 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 ActorPreview@PREVIEW: - X: 153 - Y: 1 + X: 245 + Y: 21 Width: 80 Height: 73 - Button@RANDOM_BUTTON: - Key: tab - X: 158 - Y: 74 - Width: 70 - Height: 25 - Text: Random + diff --git a/mods/ra/chrome/color-picker.yaml b/mods/ra/chrome/color-picker.yaml index b66da7fae3..4f3dc00b6a 100644 --- a/mods/ra/chrome/color-picker.yaml +++ b/mods/ra/chrome/color-picker.yaml @@ -1,43 +1,44 @@ Background@COLOR_CHOOSER: Logic: ColorPickerLogic Background: dialog2 - Width: 234 - Height: 105 + Width: 326 + Height: 140 Children: + Button@RANDOM_BUTTON: + Key: tab + X: 250 + Y: 109 + Width: 70 + Height: 25 + Text: Random + Font: Bold Background@HUEBG: Background: dialog3 X: 5 Y: 5 - Width: 148 - Height: 13 + Width: 240 + Height: 17 Children: HueSlider@HUE: X: 2 Y: 2 - Width: 144 - Height: 9 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 Ticks: 5 Background@MIXERBG: Background: dialog3 X: 5 - Y: 23 - Width: 148 - Height: 76 + Y: 27 + Width: 240 + Height: 107 Children: ColorMixer@MIXER: X: 2 Y: 2 - Width: 144 - Height: 72 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 ActorPreview@PREVIEW: - X: 153 - Y: 1 + X: 245 + Y: 21 Width: 80 Height: 73 - Button@RANDOM_BUTTON: - Key: tab - X: 158 - Y: 74 - Width: 70 - Height: 25 - Text: Random diff --git a/mods/ts/chrome/color-picker.yaml b/mods/ts/chrome/color-picker.yaml index 5c3dbcaeec..4ea4b8a81a 100644 --- a/mods/ts/chrome/color-picker.yaml +++ b/mods/ts/chrome/color-picker.yaml @@ -1,43 +1,45 @@ Background@COLOR_CHOOSER: Logic: ColorPickerLogic Background: dialog2 - Width: 234 - Height: 105 + Width: 326 + Height: 140 Children: + Button@RANDOM_BUTTON: + Key: tab + X: 250 + Y: 109 + Width: 70 + Height: 25 + Text: Random + Font: Bold Background@HUEBG: Background: dialog3 X: 5 Y: 5 - Width: 148 - Height: 13 + Width: 240 + Height: 17 Children: HueSlider@HUE: X: 2 Y: 2 - Width: 144 - Height: 9 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 Ticks: 5 Background@MIXERBG: Background: dialog3 X: 5 - Y: 23 - Width: 148 - Height: 76 + Y: 27 + Width: 240 + Height: 107 Children: ColorMixer@MIXER: X: 2 Y: 2 - Width: 144 - Height: 72 + Width: PARENT_RIGHT - 4 + Height: PARENT_BOTTOM - 4 ActorPreview@PREVIEW: - X: 163 + X: 245 + Y: 11 Width: 80 Height: 74 - Animate: true - Button@RANDOM_BUTTON: - Key: tab - X: 158 - Y: 74 - Width: 70 - Height: 25 - Text: Random + Animate: true \ No newline at end of file From 617295cc9178d3908d329e57a8cfbbe084cc1b0f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 26 Jan 2016 22:35:40 +0000 Subject: [PATCH 2/5] Overhaul color validator logic. --- OpenRA.Mods.Common/ColorValidator.cs | 151 +++++++++++++ OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 2 +- .../ServerTraits/ColorValidator.cs | 206 ------------------ .../ServerTraits/LobbyCommands.cs | 69 +++--- mods/cnc/mod.yaml | 3 +- mods/d2k/mod.yaml | 3 +- mods/ra/mod.yaml | 3 +- mods/ts/mod.yaml | 3 +- 8 files changed, 203 insertions(+), 237 deletions(-) create mode 100644 OpenRA.Mods.Common/ColorValidator.cs delete mode 100644 OpenRA.Mods.Common/ServerTraits/ColorValidator.cs diff --git a/OpenRA.Mods.Common/ColorValidator.cs b/OpenRA.Mods.Common/ColorValidator.cs new file mode 100644 index 0000000000..43e6d7d149 --- /dev/null +++ b/OpenRA.Mods.Common/ColorValidator.cs @@ -0,0 +1,151 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Support; + +namespace OpenRA.Mods.Common +{ + public class ColorValidator : IGlobalModData + { + // The bigger the color threshold, the less permissive is the algorithm + public readonly int Threshold = 0x50; + + double GetColorDelta(Color colorA, Color colorB) + { + var rmean = (colorA.R + colorB.R) / 2.0; + var r = colorA.R - colorB.R; + var g = colorA.G - colorB.G; + var b = colorA.B - colorB.B; + var weightR = 2.0 + rmean / 256; + var weightG = 4.0; + var weightB = 2.0 + (255 - rmean) / 256; + return Math.Sqrt(weightR * r * r + weightG * g * g + weightB * b * b); + } + + bool IsValid(Color askedColor, IEnumerable forbiddenColors, out Color forbiddenColor) + { + var blockingColors = forbiddenColors + .Where(playerColor => GetColorDelta(askedColor, playerColor) < Threshold) + .Select(playerColor => new { Delta = GetColorDelta(askedColor, playerColor), Color = playerColor }); + + // Return the player that holds with the lowest difference + if (blockingColors.Any()) + { + forbiddenColor = blockingColors.MinBy(aa => aa.Delta).Color; + return false; + } + + forbiddenColor = default(Color); + return true; + } + + + public bool IsValid(Color askedColor, out Color forbiddenColor, IEnumerable terrainColors, IEnumerable playerColors, Action onError) + { + // Validate color against the current map tileset + if (!IsValid(askedColor, terrainColors, out forbiddenColor)) + { + onError("Color was adjusted to be less similar to the terrain."); + return false; + } + + // Validate color against other clients + if (!IsValid(askedColor, playerColors, out forbiddenColor)) + { + onError("Color was adjusted to be less similar to another player."); + return false; + } + + // Color is valid + forbiddenColor = default(Color); + + return true; + } + + public HSLColor RandomValidColor(MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors) + { + HSLColor color; + Color forbidden; + Action ignoreError = _ => { }; + do + { + var hue = (byte)random.Next(255); + var sat = (byte)random.Next(255); + var lum = (byte)random.Next(129, 255); + color = new HSLColor(hue, sat, lum); + } while (!IsValid(color.RGB, out forbidden, terrainColors, playerColors, ignoreError)); + + return color; + } + + public HSLColor MakeValid(Color askedColor, MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors, Action onError) + { + Color forbiddenColor; + if (IsValid(askedColor, out forbiddenColor, terrainColors, playerColors, onError)) + return new HSLColor(askedColor); + + // Vector between the 2 colors + var vector = new double[] + { + askedColor.R - forbiddenColor.R, + askedColor.G - forbiddenColor.G, + askedColor.B - forbiddenColor.B + }; + + // Reduce vector by it's biggest value (more calculations, but more accuracy too) + var vectorMax = vector.Max(vv => Math.Abs(vv)); + if (vectorMax == 0) + vectorMax = 1; // Avoid division by 0 + + vector[0] /= vectorMax; + vector[1] /= vectorMax; + vector[2] /= vectorMax; + + // Color weights + var rmean = (double)(askedColor.R + forbiddenColor.R) / 2; + var weightVector = new[] + { + 2.0 + rmean / 256, + 4.0, + 2.0 + (255 - rmean) / 256, + }; + + var attempt = 1; + var allForbidden = terrainColors.Concat(playerColors); + HSLColor color; + do + { + // If we reached the limit (The ii >= 255 prevents too much calculations) + if (attempt >= 255) + { + color = RandomValidColor(random, terrainColors, playerColors); + break; + } + + // Apply vector to forbidden color + var r = (forbiddenColor.R + (int)(vector[0] * weightVector[0] * attempt)).Clamp(0, 255); + var g = (forbiddenColor.G + (int)(vector[1] * weightVector[1] * attempt)).Clamp(0, 255); + var b = (forbiddenColor.B + (int)(vector[2] * weightVector[2] * attempt)).Clamp(0, 255); + + // Get the alternative color attempt + color = new HSLColor(Color.FromArgb(r, g, b)); + + attempt++; + } while (!IsValid(color.RGB, allForbidden, out forbiddenColor)); + + return color; + } + } +} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index b0c42ec9f9..a6952e89d7 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -212,7 +212,6 @@ - @@ -733,6 +732,7 @@ + diff --git a/OpenRA.Mods.Common/ServerTraits/ColorValidator.cs b/OpenRA.Mods.Common/ServerTraits/ColorValidator.cs deleted file mode 100644 index d6f4ef4814..0000000000 --- a/OpenRA.Mods.Common/ServerTraits/ColorValidator.cs +++ /dev/null @@ -1,206 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation. For more information, - * see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using OpenRA.Graphics; -using OpenRA.Server; -using S = OpenRA.Server.Server; - -namespace OpenRA.Mods.Common.Server -{ - public class ColorValidator : ServerTrait, IClientJoined - { - // The bigger the color threshold, the less permissive is the algorithm - const int ColorThreshold = 0x70; - const byte ColorLowerBound = 0x80; - const byte ColorHigherBound = 0xFF; - - static bool ValidateColorAgainstForbidden(Color askedColor, IEnumerable forbiddenColors, out Color forbiddenColor) - { - var blockingColors = - forbiddenColors - .Where(playerColor => GetColorDelta(askedColor, playerColor) < ColorThreshold) - .Select(playerColor => new { Delta = GetColorDelta(askedColor, playerColor), Color = playerColor }); - - // Return the player that holds with the lowest difference - if (blockingColors.Any()) - { - forbiddenColor = blockingColors.MinBy(aa => aa.Delta).Color; - return false; - } - - forbiddenColor = default(Color); - return true; - } - - public static Color? GetColorAlternative(Color askedColor, Color forbiddenColor) - { - Color? color = null; - - // Vector between the 2 colors - var vector = new double[] - { - askedColor.R - forbiddenColor.R, - askedColor.G - forbiddenColor.G, - askedColor.B - forbiddenColor.B - }; - - // Reduce vector by it's biggest value (more calculations, but more accuracy too) - var vectorMax = vector.Max(vv => Math.Abs(vv)); - if (vectorMax == 0) - vectorMax = 1; // Avoid division by 0 - - vector[0] /= vectorMax; - vector[1] /= vectorMax; - vector[2] /= vectorMax; - - // Color weights - var rmean = (double)(askedColor.R + forbiddenColor.R) / 2; - var weightVector = new[] - { - 2.0 + rmean / 256, - 4.0, - 2.0 + (255 - rmean) / 256, - }; - - var ii = 1; - var alternativeColor = new int[3]; - - do - { - // If we reached the limit (The ii >= 255 prevents too much calculations) - if ((alternativeColor[0] == ColorLowerBound && alternativeColor[1] == ColorLowerBound && alternativeColor[2] == ColorLowerBound) - || (alternativeColor[0] == ColorHigherBound && alternativeColor[1] == ColorHigherBound && alternativeColor[2] == ColorHigherBound) - || ii >= 255) - { - color = null; - break; - } - - // Apply vector to forbidden color - alternativeColor[0] = forbiddenColor.R + (int)(vector[0] * weightVector[0] * ii); - alternativeColor[1] = forbiddenColor.G + (int)(vector[1] * weightVector[1] * ii); - alternativeColor[2] = forbiddenColor.B + (int)(vector[2] * weightVector[2] * ii); - - // Be sure it doesn't go out of bounds (0x33 is the lower limit for HSL picker) - alternativeColor[0] = alternativeColor[0].Clamp(ColorLowerBound, ColorHigherBound); - alternativeColor[1] = alternativeColor[1].Clamp(ColorLowerBound, ColorHigherBound); - alternativeColor[2] = alternativeColor[2].Clamp(ColorLowerBound, ColorHigherBound); - - // Get the alternative color attempt - color = Color.FromArgb(alternativeColor[0], alternativeColor[1], alternativeColor[2]); - - ++ii; - } while (GetColorDelta(color.Value, forbiddenColor) < ColorThreshold); - - return color; - } - - public static double GetColorDelta(Color colorA, Color colorB) - { - var rmean = (colorA.R + colorB.R) / 2.0; - var r = colorA.R - colorB.R; - var g = colorA.G - colorB.G; - var b = colorA.B - colorB.B; - var weightR = 2.0 + rmean / 256; - var weightG = 4.0; - var weightB = 2.0 + (255 - rmean) / 256; - return Math.Sqrt(weightR * r * r + weightG * g * g + weightB * b * b); - } - - public static HSLColor ValidatePlayerColorAndGetAlternative(S server, HSLColor askedColor, int playerIndex, Connection connectionToEcho = null) - { - var askColor = askedColor; - - Color invalidColor; - if (!ValidatePlayerNewColor(server, askColor.RGB, playerIndex, out invalidColor, connectionToEcho)) - { - var altColor = GetColorAlternative(askColor.RGB, invalidColor); - if (altColor == null || !ValidatePlayerNewColor(server, altColor.Value, playerIndex)) - { - // Pick a random color - do - { - var hue = (byte)server.Random.Next(255); - var sat = (byte)server.Random.Next(255); - var lum = (byte)server.Random.Next(129, 255); - askColor = new HSLColor(hue, sat, lum); - } while (!ValidatePlayerNewColor(server, askColor.RGB, playerIndex)); - } - else - askColor = HSLColor.FromRGB(altColor.Value.R, altColor.Value.G, altColor.Value.B); - } - - return askColor; - } - - public static bool ValidatePlayerNewColor(S server, Color askedColor, int playerIndex, out Color forbiddenColor, Connection connectionToEcho = null) - { - // Validate color against the current map tileset - var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; - var forbiddenColors = tileset.TerrainInfo.Select(terrainInfo => terrainInfo.Color); - - if (!ValidateColorAgainstForbidden(askedColor, forbiddenColors, out forbiddenColor)) - { - if (connectionToEcho != null) - server.SendOrderTo(connectionToEcho, "Message", "Color was too similar to the terrain, and has been adjusted."); - - return false; - } - - // Validate color against other clients - var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color.RGB); - if (!ValidateColorAgainstForbidden(askedColor, playerColors, out forbiddenColor)) - { - if (connectionToEcho != null) - server.SendOrderTo(connectionToEcho, "Message", "Color was too similar to another player's color, and has been adjusted."); - - return false; - } - - var mapPlayerColors = server.MapPlayers.Players.Values.Select(p => p.Color.RGB); - - if (!ValidateColorAgainstForbidden(askedColor, mapPlayerColors, out forbiddenColor)) - { - if (connectionToEcho != null) - server.SendOrderTo(connectionToEcho, "Message", "Color was too similar to a non-combatant player, and has been adjusted."); - - return false; - } - - // Color is valid - forbiddenColor = default(Color); - - return true; - } - - public static bool ValidatePlayerNewColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null) - { - Color forbiddenColor; - return ValidatePlayerNewColor(server, askedColor, playerIndex, out forbiddenColor, connectionToEcho); - } - - #region IClientJoined - - public void ClientJoined(S server, Connection conn) - { - var client = server.GetClient(conn); - - // Validate whether color is allowed and get an alternative if it isn't - if (client.Slot == null || !server.LobbyInfo.Slots[client.Slot].LockColor) - client.Color = ValidatePlayerColorAndGetAlternative(server, client.Color, client.Index); - } - - #endregion - } -} diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index eda67f531b..62b5a02d36 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -19,7 +20,7 @@ using S = OpenRA.Server.Server; namespace OpenRA.Mods.Common.Server { - public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart + public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart, IClientJoined { static bool ValidateSlotCommand(S server, Connection conn, Session.Client client, string arg, bool requiresHost) { @@ -136,10 +137,7 @@ namespace OpenRA.Mods.Common.Server S.SyncClientToPlayerReference(client, server.MapPlayers.Players[s]); if (!slot.LockColor) - { - var validatedColor = ColorValidator.ValidatePlayerColorAndGetAlternative(server, client.Color, client.Index, conn); - client.PreferredColor = client.Color = validatedColor; - } + client.PreferredColor = client.Color = SanitizePlayerColor(server, client.Color, client.Index, conn); server.SyncLobbyClients(); CheckAutoStart(server); @@ -291,16 +289,12 @@ namespace OpenRA.Mods.Common.Server }; // Pick a random color for the bot - HSLColor botColor; - do - { - var hue = (byte)server.Random.Next(255); - var sat = (byte)server.Random.Next(255); - var lum = (byte)server.Random.Next(51, 255); - botColor = new HSLColor(hue, sat, lum); - } while (!ColorValidator.ValidatePlayerNewColor(server, botColor.RGB, bot.Index)); - - bot.Color = bot.PreferredColor = botColor; + var validator = server.ModData.Manifest.Get(); + var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; + var terrainColors = tileset.TerrainInfo.Select(ti => ti.Color); + var playerColors = server.LobbyInfo.Clients.Select(c => c.Color.RGB) + .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)); + bot.Color = bot.PreferredColor = validator.RandomValidColor(server.Random, terrainColors, playerColors); server.LobbyInfo.Clients.Add(bot); } @@ -367,12 +361,10 @@ namespace OpenRA.Mods.Common.Server server.LobbyInfo.Clients.Remove(c); } + // Validate if color is allowed and get an alternative it isn't foreach (var c in server.LobbyInfo.Clients) - { - // Validate if color is allowed and get an alternative it isn't if (c.Slot == null || (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor)) - c.Color = c.PreferredColor = ColorValidator.ValidatePlayerColorAndGetAlternative(server, c.Color, c.Index, conn); - } + c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn); server.SyncLobbyInfo(); @@ -875,16 +867,13 @@ namespace OpenRA.Mods.Common.Server if (targetClient.Slot == null || server.LobbyInfo.Slots[targetClient.Slot].LockColor) return true; - var newHslColor = FieldLoader.GetValue("(value)", parts[1]); - // Validate if color is allowed and get an alternative it isn't - var altHslColor = ColorValidator.ValidatePlayerColorAndGetAlternative(server, newHslColor, targetClient.Index, conn); - - targetClient.Color = altHslColor; + var newColor = FieldLoader.GetValue("(value)", parts[1]); + targetClient.Color = SanitizePlayerColor(server, newColor, targetClient.Index, conn); // Only update player's preferred color if new color is valid - if (newHslColor == altHslColor) - targetClient.PreferredColor = altHslColor; + if (newColor == targetClient.Color) + targetClient.PreferredColor = targetClient.Color; server.SyncLobbyClients(); return true; @@ -973,5 +962,33 @@ namespace OpenRA.Mods.Common.Server if (!server.Map.Options.Difficulties.Contains(server.LobbyInfo.GlobalSettings.Difficulty)) server.LobbyInfo.GlobalSettings.Difficulty = server.Map.Options.Difficulties.First(); } + + static HSLColor SanitizePlayerColor(S server, HSLColor askedColor, int playerIndex, Connection connectionToEcho = null) + { + var validator = server.ModData.Manifest.Get(); + var askColor = askedColor; + + Action onError = message => + { + if (connectionToEcho != null) + server.SendOrderTo(connectionToEcho, "Message", message); + }; + + var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; + var terrainColors = tileset.TerrainInfo.Select(ti => ti.Color).ToList(); + var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color.RGB) + .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)).ToList(); + + return validator.MakeValid(askColor.RGB, server.Random, terrainColors, playerColors, onError); + } + + public void ClientJoined(S server, Connection conn) + { + var client = server.GetClient(conn); + + // Validate whether color is allowed and get an alternative if it isn't + if (client.Slot == null || !server.LobbyInfo.Slots[client.Slot].LockColor) + client.Color = SanitizePlayerColor(server, client.Color, client.Index); + } } } diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 17177eae48..73735f3f6e 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -164,7 +164,6 @@ ServerTraits: PlayerPinger MasterServerPinger LobbySettingsNotification - ColorValidator LobbyDefaults: AllowCheats: false @@ -242,3 +241,5 @@ GameSpeeds: Name: Fastest Timestep: 20 OrderLatency: 6 + +ColorValidator: diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 2bace6adeb..27c8b39dc3 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -149,7 +149,6 @@ ServerTraits: PlayerPinger MasterServerPinger LobbySettingsNotification - ColorValidator LobbyDefaults: AllowCheats: false @@ -217,3 +216,5 @@ GameSpeeds: Name: Fastest Timestep: 20 OrderLatency: 6 + +ColorValidator: diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 4b188744e5..20674cec31 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -165,7 +165,6 @@ ServerTraits: PlayerPinger MasterServerPinger LobbySettingsNotification - ColorValidator LobbyDefaults: AllowCheats: false @@ -242,3 +241,5 @@ GameSpeeds: Name: Fastest Timestep: 20 OrderLatency: 6 + +ColorValidator: diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 363ff8ea88..9e2eb7b762 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -211,7 +211,6 @@ ServerTraits: PlayerPinger MasterServerPinger LobbySettingsNotification - ColorValidator LobbyDefaults: AllowCheats: true @@ -281,3 +280,5 @@ GameSpeeds: Name: Fastest Timestep: 20 OrderLatency: 6 + +ColorValidator: From 76387460694823b08252998be4ad34730a414dd6 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 26 Jan 2016 23:04:08 +0000 Subject: [PATCH 3/5] Define additional constraints in HSV space. --- OpenRA.Mods.Common/ColorValidator.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.Common/ColorValidator.cs b/OpenRA.Mods.Common/ColorValidator.cs index 43e6d7d149..f459818e98 100644 --- a/OpenRA.Mods.Common/ColorValidator.cs +++ b/OpenRA.Mods.Common/ColorValidator.cs @@ -21,6 +21,8 @@ namespace OpenRA.Mods.Common { // The bigger the color threshold, the less permissive is the algorithm public readonly int Threshold = 0x50; + public readonly float[] HsvSaturationRange = new[] { 0.25f, 1f }; + public readonly float[] HsvValueRange = new[] { 0.2f, 1.0f }; double GetColorDelta(Color colorA, Color colorB) { @@ -51,9 +53,18 @@ namespace OpenRA.Mods.Common return true; } - public bool IsValid(Color askedColor, out Color forbiddenColor, IEnumerable terrainColors, IEnumerable playerColors, Action onError) { + // Validate color against HSV + float h, s, v; + new HSLColor(askedColor).ToHSV(out h, out s, out v); + if (s < HsvSaturationRange[0] || s > HsvSaturationRange[1] || v < HsvValueRange[0] || v > HsvValueRange[1]) + { + onError("Color was adjusted to be inside the allowed range."); + forbiddenColor = askedColor; + return false; + } + // Validate color against the current map tileset if (!IsValid(askedColor, terrainColors, out forbiddenColor)) { @@ -81,10 +92,10 @@ namespace OpenRA.Mods.Common Action ignoreError = _ => { }; do { - var hue = (byte)random.Next(255); - var sat = (byte)random.Next(255); - var lum = (byte)random.Next(129, 255); - color = new HSLColor(hue, sat, lum); + var h = random.Next(255) / 255f; + var s = float2.Lerp(HsvSaturationRange[0], HsvSaturationRange[1], random.NextFloat()); + var v = float2.Lerp(HsvValueRange[0], HsvValueRange[1], random.NextFloat()); + color = HSLColor.FromHSV(h, s, v); } while (!IsValid(color.RGB, out forbidden, terrainColors, playerColors, ignoreError)); return color; From cc4ca839d997bebd8fc8d5ede94c82913c68848f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 26 Jan 2016 23:46:28 +0000 Subject: [PATCH 4/5] Bind color mixer to validator range. --- .../Widgets/ColorMixerWidget.cs | 48 ++++++++++++++----- .../Widgets/Logic/ColorPickerLogic.cs | 3 ++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/OpenRA.Mods.Common/Widgets/ColorMixerWidget.cs b/OpenRA.Mods.Common/Widgets/ColorMixerWidget.cs index f9ba46404f..a83ff43c71 100644 --- a/OpenRA.Mods.Common/Widgets/ColorMixerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ColorMixerWidget.cs @@ -18,8 +18,9 @@ namespace OpenRA.Mods.Common.Widgets { public class ColorMixerWidget : Widget { - public float[] SRange = { 0.0f, 1.0f }; - public float[] VRange = { 0.2f, 1.0f }; + public float STrim = 0.025f; + public float VTrim = 0.025f; + public event Action OnChange = () => { }; public float H { get; private set; } @@ -36,6 +37,9 @@ namespace OpenRA.Mods.Common.Widgets Thread workerThread; bool workerAlive; + float[] sRange = { 0.0f, 1.0f }; + float[] vRange = { 0.0f, 1.0f }; + public ColorMixerWidget() { } public ColorMixerWidget(ColorMixerWidget other) : base(other) @@ -44,17 +48,39 @@ namespace OpenRA.Mods.Common.Widgets H = other.H; S = other.S; V = other.V; + + sRange = (float[])other.sRange.Clone(); + vRange = (float[])other.vRange.Clone(); + + STrim = other.STrim; + VTrim = other.VTrim; + } + + public void SetPaletteRange(float sMin, float sMax, float vMin, float vMax) + { + sRange[0] = sMin + STrim; + sRange[1] = sMax - STrim; + vRange[0] = vMin + VTrim; + vRange[1] = vMax - VTrim; + + var rect = new Rectangle((int)(255 * sRange[0]), (int)(255 * (1 - vRange[1])), (int)(255 * (sRange[1] - sRange[0])) + 1, (int)(255 * (vRange[1] - vRange[0])) + 1); + mixerSprite = new Sprite(mixerSprite.Sheet, rect, TextureChannel.Alpha); } public override void Initialize(WidgetArgs args) { base.Initialize(args); + sRange[0] += STrim; + sRange[1] -= STrim; + vRange[0] += VTrim; + vRange[1] -= VTrim; + // Bitmap data is generated in a background thread and then flipped front = new byte[4 * 256 * 256]; back = new byte[4 * 256 * 256]; - var rect = new Rectangle((int)(255 * SRange[0]), (int)(255 * (1 - VRange[1])), (int)(255 * (SRange[1] - SRange[0])) + 1, (int)(255 * (VRange[1] - VRange[0])) + 1); + var rect = new Rectangle((int)(255 * sRange[0]), (int)(255 * (1 - vRange[1])), (int)(255 * (sRange[1] - sRange[0])) + 1, (int)(255 * (vRange[1] - vRange[0])) + 1); var mixerSheet = new Sheet(SheetType.BGRA, new Size(256, 256)); mixerSheet.GetTexture().SetData(front, 256, 256); mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha); @@ -145,17 +171,17 @@ namespace OpenRA.Mods.Common.Widgets void SetValueFromPx(int2 xy) { var rb = RenderBounds; - var s = SRange[0] + xy.X * (SRange[1] - SRange[0]) / rb.Width; - var v = SRange[1] - xy.Y * (VRange[1] - VRange[0]) / rb.Height; - S = s.Clamp(SRange[0], SRange[1]); - V = v.Clamp(VRange[0], VRange[1]); + var s = sRange[0] + xy.X * (sRange[1] - sRange[0]) / rb.Width; + var v = sRange[1] - xy.Y * (vRange[1] - vRange[0]) / rb.Height; + S = s.Clamp(sRange[0], sRange[1]); + V = v.Clamp(vRange[0], vRange[1]); } int2 PxFromValue() { var rb = RenderBounds; - var x = RenderBounds.Width * (S - SRange[0]) / (SRange[1] - SRange[0]); - var y = RenderBounds.Height * (1 - (V - VRange[0]) / (VRange[1] - VRange[0])); + var x = RenderBounds.Width * (S - sRange[0]) / (sRange[1] - sRange[0]); + var y = RenderBounds.Height * (1 - (V - vRange[0]) / (vRange[1] - vRange[0])); return new int2((int)x.Clamp(0, rb.Width), (int)y.Clamp(0, rb.Height)); } @@ -219,8 +245,8 @@ namespace OpenRA.Mods.Common.Widgets GenerateBitmap(); } - S = s.Clamp(SRange[0], SRange[1]); - V = v.Clamp(VRange[0], VRange[1]); + S = s.Clamp(sRange[0], sRange[1]); + V = v.Clamp(vRange[0], vRange[1]); OnChange(); } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs index a9c97f4cf2..ea73ee9bee 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs @@ -56,7 +56,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; // Set the initial state + var validator = Game.ModData.Manifest.Get(); + mixer.SetPaletteRange(validator.HsvSaturationRange[0], validator.HsvSaturationRange[1], validator.HsvValueRange[0], validator.HsvValueRange[1]); mixer.Set(initialColor); + hueSlider.Value = initialColor.H / 255f; onChange(mixer.Color); } From dd963b75f56f9ec6d9494685f3f19ad4a8eb9d46 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 25 Jan 2016 22:38:37 +0000 Subject: [PATCH 5/5] Per-terrain color validation. --- OpenRA.Game/Map/TileSet.cs | 1 + OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs | 4 ++-- mods/cnc/tilesets/desert.yaml | 4 ++++ mods/cnc/tilesets/jungle.yaml | 4 ++++ mods/cnc/tilesets/snow.yaml | 4 ++++ mods/cnc/tilesets/temperat.yaml | 4 ++++ mods/cnc/tilesets/winter.yaml | 4 ++++ mods/d2k/tilesets/arrakis.yaml | 3 +++ mods/ra/tilesets/desert.yaml | 4 ++++ mods/ra/tilesets/interior.yaml | 3 +++ mods/ra/tilesets/snow.yaml | 4 ++++ mods/ra/tilesets/temperat.yaml | 4 ++++ mods/ts/tilesets/snow.yaml | 4 ++++ mods/ts/tilesets/temperate.yaml | 4 ++++ 14 files changed, 49 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index c7b9ff6fe8..4d0a47bd90 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -53,6 +53,7 @@ namespace OpenRA public readonly HashSet AcceptsSmudgeType = new HashSet(); public readonly bool IsWater = false; // TODO: Remove this public readonly Color Color; + public readonly bool RestrictPlayerColor = false; public readonly string CustomCursor; // Private default ctor for serialization comparison diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 62b5a02d36..86dc76dfb8 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -291,7 +291,7 @@ namespace OpenRA.Mods.Common.Server // Pick a random color for the bot var validator = server.ModData.Manifest.Get(); var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; - var terrainColors = tileset.TerrainInfo.Select(ti => ti.Color); + var terrainColors = tileset.TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); var playerColors = server.LobbyInfo.Clients.Select(c => c.Color.RGB) .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)); bot.Color = bot.PreferredColor = validator.RandomValidColor(server.Random, terrainColors, playerColors); @@ -975,7 +975,7 @@ namespace OpenRA.Mods.Common.Server }; var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; - var terrainColors = tileset.TerrainInfo.Select(ti => ti.Color).ToList(); + var terrainColors = tileset.TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color).ToList(); var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color.RGB) .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)).ToList(); diff --git a/mods/cnc/tilesets/desert.yaml b/mods/cnc/tilesets/desert.yaml index 3e102cd34b..334dee8689 100644 --- a/mods/cnc/tilesets/desert.yaml +++ b/mods/cnc/tilesets/desert.yaml @@ -14,11 +14,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 54FCFC + RestrictPlayerColor: true TerrainType@Clear: Type: Clear TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 865F45 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -42,6 +44,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: A1E21C + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -56,6 +59,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5DA5CE + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/cnc/tilesets/jungle.yaml b/mods/cnc/tilesets/jungle.yaml index fc2817de41..2fb453596d 100644 --- a/mods/cnc/tilesets/jungle.yaml +++ b/mods/cnc/tilesets/jungle.yaml @@ -14,11 +14,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 54FCFC + RestrictPlayerColor: true TerrainType@Clear: Type: Clear TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 285C30 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -42,6 +44,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: A1E21C + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -56,6 +59,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/cnc/tilesets/snow.yaml b/mods/cnc/tilesets/snow.yaml index 7e40c6f4eb..3c41c3257b 100644 --- a/mods/cnc/tilesets/snow.yaml +++ b/mods/cnc/tilesets/snow.yaml @@ -14,11 +14,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 54FCFC + RestrictPlayerColor: true TerrainType@Clear: Type: Clear TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: C4C4C4 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -42,6 +44,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: A1E21C + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -56,6 +59,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/cnc/tilesets/temperat.yaml b/mods/cnc/tilesets/temperat.yaml index 6c4b7f20b6..c2baa7180d 100644 --- a/mods/cnc/tilesets/temperat.yaml +++ b/mods/cnc/tilesets/temperat.yaml @@ -14,11 +14,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 54FCFC + RestrictPlayerColor: true TerrainType@Clear: Type: Clear TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 284428 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -42,6 +44,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: A1E21C + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -56,6 +59,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/cnc/tilesets/winter.yaml b/mods/cnc/tilesets/winter.yaml index 5eda5cc96d..b53bbb07a0 100644 --- a/mods/cnc/tilesets/winter.yaml +++ b/mods/cnc/tilesets/winter.yaml @@ -14,11 +14,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 54FCFC + RestrictPlayerColor: true TerrainType@Clear: Type: Clear TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 284428 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -42,6 +44,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: A1E21C + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -56,6 +59,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/d2k/tilesets/arrakis.yaml b/mods/d2k/tilesets/arrakis.yaml index d581f961df..834b64c6bb 100644 --- a/mods/d2k/tilesets/arrakis.yaml +++ b/mods/d2k/tilesets/arrakis.yaml @@ -33,6 +33,7 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: RockCrater Color: CE8C42 + RestrictPlayerColor: true TerrainType@Rough: Type: Rough TargetTypes: Ground @@ -43,10 +44,12 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: SandCrater Color: D0C0A0 + RestrictPlayerColor: true TerrainType@Spice: Type: Spice TargetTypes: Ground Color: EF944A + RestrictPlayerColor: true TerrainType@Transition: Type: Transition TargetTypes: Ground diff --git a/mods/ra/tilesets/desert.yaml b/mods/ra/tilesets/desert.yaml index 4b9363911f..2be95e1601 100644 --- a/mods/ra/tilesets/desert.yaml +++ b/mods/ra/tilesets/desert.yaml @@ -24,16 +24,19 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 865F45 + RestrictPlayerColor: true TerrainType@Gems: Type: Gems TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 8470FF + RestrictPlayerColor: true TerrainType@Ore: Type: Ore TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 948060 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -66,6 +69,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5DA5CE + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/ra/tilesets/interior.yaml b/mods/ra/tilesets/interior.yaml index ed75844330..1a152a9b13 100644 --- a/mods/ra/tilesets/interior.yaml +++ b/mods/ra/tilesets/interior.yaml @@ -19,11 +19,13 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 8470FF + RestrictPlayerColor: true TerrainType@Ore: Type: Ore TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 948060 + RestrictPlayerColor: true TerrainType@Tree: Type: Tree TargetTypes: Ground @@ -32,6 +34,7 @@ Terrain: Type: Wall TargetTypes: Ground Color: D0C0A0 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/ra/tilesets/snow.yaml b/mods/ra/tilesets/snow.yaml index 6b42b5e27d..e13e8f81c8 100644 --- a/mods/ra/tilesets/snow.yaml +++ b/mods/ra/tilesets/snow.yaml @@ -19,16 +19,19 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: C4C4C4 + RestrictPlayerColor: true TerrainType@Gems: Type: Gems TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 8470FF + RestrictPlayerColor: true TerrainType@Ore: Type: Ore TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 948060 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -61,6 +64,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/ra/tilesets/temperat.yaml b/mods/ra/tilesets/temperat.yaml index e5b72644cf..502f5cb98c 100644 --- a/mods/ra/tilesets/temperat.yaml +++ b/mods/ra/tilesets/temperat.yaml @@ -19,16 +19,19 @@ Terrain: TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 284428 + RestrictPlayerColor: true TerrainType@Gems: Type: Gems TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 8470FF + RestrictPlayerColor: true TerrainType@Ore: Type: Ore TargetTypes: Ground AcceptsSmudgeType: Crater, Scorch Color: 948060 + RestrictPlayerColor: true TerrainType@River: Type: River TargetTypes: Ground @@ -61,6 +64,7 @@ Terrain: TargetTypes: Water IsWater: True Color: 5C74A4 + RestrictPlayerColor: true Templates: Template@255: diff --git a/mods/ts/tilesets/snow.yaml b/mods/ts/tilesets/snow.yaml index 413ea358a1..37666db14d 100644 --- a/mods/ts/tilesets/snow.yaml +++ b/mods/ts/tilesets/snow.yaml @@ -13,6 +13,7 @@ Terrain: AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: C7C9FA TargetTypes: Ground + RestrictPlayerColor: true TerrainType@Road: Type: Road Color: 2D2B28 @@ -30,6 +31,7 @@ Terrain: Color: 3D4148 TargetTypes: Water IsWater: True + RestrictPlayerColor: true TerrainType@DirtRoad: Type: DirtRoad Color: 82838F @@ -47,11 +49,13 @@ Terrain: AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: 009000 TargetTypes: Ground + RestrictPlayerColor: true TerrainType@BlueTiberium: Type: BlueTiberium AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: 202080 TargetTypes: Ground + RestrictPlayerColor: true TerrainType@Veins: Type: Veins Color: 000000 diff --git a/mods/ts/tilesets/temperate.yaml b/mods/ts/tilesets/temperate.yaml index 5e3600d56b..7bdfdc40cb 100644 --- a/mods/ts/tilesets/temperate.yaml +++ b/mods/ts/tilesets/temperate.yaml @@ -13,6 +13,7 @@ Terrain: AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: 745537 TargetTypes: Ground + RestrictPlayerColor: true TerrainType@Road: Type: Road Color: 745537 @@ -30,6 +31,7 @@ Terrain: Color: 745537 TargetTypes: Water IsWater: True + RestrictPlayerColor: true TerrainType@DirtRoad: Type: DirtRoad Color: 745537 @@ -47,11 +49,13 @@ Terrain: AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: 009000 TargetTypes: Ground + RestrictPlayerColor: true TerrainType@BlueTiberium: Type: BlueTiberium AcceptsSmudgeType: SmallCrater, MediumCrater, LargeCrater, SmallScorch, MediumScorch, LargeScorch Color: 202080 TargetTypes: Ground + RestrictPlayerColor: true TerrainType@Veins: Type: Veins Color: 000000