diff --git a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs index b19d8fee55..b32bff242d 100644 --- a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Support; using OpenRA.Traits; @@ -37,9 +36,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("List of saturation components for the preset colors in the palette tab. Each entry must have a corresponding PresetHues definition.")] public readonly float[] PresetSaturations = { }; - [PaletteReference] - public readonly string PaletteName = "colorpicker"; - [ActorReference] [Desc("Actor type to show in the color picker. This can be overriden for specific factions with FactionPreviewActors.")] public readonly string PreviewActor = null; @@ -49,10 +45,6 @@ namespace OpenRA.Mods.Common.Traits "A dictionary of [faction name]: [actor name].")] public readonly Dictionary FactionPreviewActors = new Dictionary(); - [FieldLoader.Require] - [Desc("Remap these indices to player colors.")] - public readonly int[] RemapIndices = { }; - public void RulesetLoaded(Ruleset rules, ActorInfo ai) { if (PresetHues.Length != PresetSaturations.Length) @@ -65,17 +57,7 @@ namespace OpenRA.Mods.Common.Traits yield return Color.FromAhsv(PresetHues[i], PresetSaturations[i], V); } - public Color Color { get; private set; } - - public void Update(WorldRenderer worldRenderer, Color color) - { - Color = color; - - var (_, h, s, _) = Color.ToAhsv(); - var newPalette = new MutablePalette(worldRenderer.Palette(PaletteName).Palette); - newPalette.ApplyRemap(new PlayerColorRemap(RemapIndices, h, s)); - worldRenderer.ReplacePalette(PaletteName, newPalette); - } + public Color Color; bool TryGetBlockingColor((float R, float G, float B) color, List<(float R, float G, float B)> candidateBlockers, out (float R, float G, float B) closestBlocker) { diff --git a/OpenRA.Mods.Common/Traits/World/ColorPickerPalette.cs b/OpenRA.Mods.Common/Traits/World/ColorPickerPalette.cs new file mode 100644 index 0000000000..1f621db089 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/ColorPickerPalette.cs @@ -0,0 +1,78 @@ +#region Copyright & License Information +/* + * Copyright 2007-2021 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] + [Desc("Create a color picker palette from another palette.")] + class ColorPickerPaletteInfo : TraitInfo + { + [PaletteDefinition] + [FieldLoader.Require] + [Desc("Internal palette name.")] + public readonly string Name = null; + + [PaletteReference] + [FieldLoader.Require] + [Desc("The name of the palette to base off.")] + public readonly string BasePalette = null; + + [FieldLoader.Require] + [Desc("Remap these indices to player colors.")] + public readonly int[] RemapIndex = { }; + + [Desc("Allow palette modifiers to change the palette.")] + public readonly bool AllowModifiers = true; + + public override object Create(ActorInitializer init) { return new ColorPickerPalette(init.World, this); } + } + + class ColorPickerPalette : ILoadsPalettes, IProvidesAssetBrowserColorPickerPalettes, ITickRender + { + readonly ColorPickerPaletteInfo info; + readonly ColorPickerManagerInfo colorManager; + Color color; + + public ColorPickerPalette(World world, ColorPickerPaletteInfo info) + { + // All users need to use the same TraitInfo instance, chosen as the default mod rules + colorManager = Game.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo(); + this.info = info; + } + + void ILoadsPalettes.LoadPalettes(WorldRenderer wr) + { + color = colorManager.Color; + var (_, h, s, _) = color.ToAhsv(); + var remap = new PlayerColorRemap(info.RemapIndex, h, s); + wr.AddPalette(info.Name, new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap), info.AllowModifiers); + } + + IEnumerable IProvidesAssetBrowserColorPickerPalettes.ColorPickerPaletteNames { get { yield return info.Name; } } + + void ITickRender.TickRender(WorldRenderer wr, Actor self) + { + if (color == colorManager.Color) + return; + + color = colorManager.Color; + var (_, h, s, _) = color.ToAhsv(); + var remap = new PlayerColorRemap(info.RemapIndex, h, s); + wr.ReplacePalette(info.Name, new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap)); + } + } +} diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 644f70f9c6..668a641872 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -270,6 +270,12 @@ namespace OpenRA.Mods.Common.Traits IEnumerable PaletteNames { get; } } + [RequireExplicitImplementation] + public interface IProvidesAssetBrowserColorPickerPalettes + { + IEnumerable ColorPickerPaletteNames { get; } + } + public interface ICallForTransport { WDist MinimumDistance { get; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs index 6ecf42238f..e6b574fe6c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { readonly string[] allowedExtensions; readonly IEnumerable acceptablePackages; - + readonly string[] palettes; readonly World world; readonly ModData modData; @@ -58,6 +58,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic this.modData = modData; panel = widget; + var colorPickerPalettes = world.WorldActor.TraitsImplementing() + .SelectMany(p => p.ColorPickerPaletteNames) + .ToArray(); + + palettes = world.WorldActor.TraitsImplementing() + .SelectMany(p => p.PaletteNames) + .Concat(colorPickerPalettes) + .ToArray(); + var ticker = panel.GetOrNull("ANIMATION_TICKER"); if (ticker != null) { @@ -112,12 +121,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic } var colorManager = modData.DefaultRules.Actors[SystemActors.World].TraitInfo(); - colorManager.Update(worldRenderer, Game.Settings.Player.Color); + colorManager.Color = Game.Settings.Player.Color; var colorDropdown = panel.GetOrNull("COLOR"); if (colorDropdown != null) { - colorDropdown.IsDisabled = () => currentPalette != colorManager.PaletteName; + colorDropdown.IsDisabled = () => !colorPickerPalettes.Contains(currentPalette); colorDropdown.OnMouseDown = _ => ColorPickerLogic.ShowColorDropDown(colorDropdown, colorManager, worldRenderer); panel.Get("COLORBLOCK").GetColor = () => colorManager.Color; } @@ -489,8 +498,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic return item; }; - var palettes = world.WorldActor.TraitsImplementing() - .SelectMany(p => p.PaletteNames); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, palettes, setupItem); return true; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs index 1a5e53b5bf..1139dffa6c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs @@ -31,7 +31,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic var mixer = widget.Get("MIXER"); // Set the initial state - var colorManager = world.WorldActor.Info.TraitInfo(); + // All users need to use the same TraitInfo instance, chosen as the default mod rules + var colorManager = modData.DefaultRules.Actors[SystemActors.World].TraitInfo(); mixer.SetColorLimits(colorManager.HsvSaturationRange[0], colorManager.HsvSaturationRange[1], colorManager.V); mixer.OnChange += () => onChange(mixer.Color); mixer.Set(initialColor); @@ -192,11 +193,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic { color.RemovePanel(); - Action onChange = c => colorManager.Update(worldRenderer, c); - var colorChooser = Game.LoadWidget(worldRenderer.World, "COLOR_CHOOSER", null, new WidgetArgs() { - { "onChange", onChange }, + { "onChange", (Action)(c => colorManager.Color = c) }, { "initialColor", colorManager.Color }, { "initialFaction", null } }); diff --git a/OpenRA.Mods.Common/Widgets/Logic/IntroductionPromptLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/IntroductionPromptLogic.cs index 19bdb23779..4c9d8d4695 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/IntroductionPromptLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/IntroductionPromptLogic.cs @@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; var colorManager = modData.DefaultRules.Actors[SystemActors.World].TraitInfo(); - colorManager.Update(worldRenderer, ps.Color); + colorManager.Color = ps.Color; var mouseControlDescClassic = widget.Get("MOUSE_CONTROL_DESC_CLASSIC"); mouseControlDescClassic.IsVisible = () => gs.UseClassicMouseStyle; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index e1c6e4f1e4..455c5bdc44 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -159,7 +159,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic nonEditableSpectatorTemplate = players.Get("TEMPLATE_NONEDITABLE_SPECTATOR"); newSpectatorTemplate = players.Get("TEMPLATE_NEW_SPECTATOR"); colorManager = modRules.Actors[SystemActors.World].TraitInfo(); - colorManager.Update(worldRenderer, Game.Settings.Player.Color); + colorManager.Color = Game.Settings.Player.Color; foreach (var f in modRules.Actors[SystemActors.World].TraitInfos()) factions.Add(f.InternalName, new LobbyFaction { Selectable = f.Selectable, Name = f.Name, Side = f.Side, Description = f.Description }); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs index 6fae28fcf4..9c0925bdfc 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs @@ -235,11 +235,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic orderManager.IssueOrder(Order.Command($"color {client.Index} {colorManager.Color}")); }; - Action onChange = c => colorManager.Update(worldRenderer, c); - var colorChooser = Game.LoadWidget(worldRenderer.World, "COLOR_CHOOSER", null, new WidgetArgs() { - { "onChange", onChange }, + { "onChange", (Action)(c => colorManager.Color = c) }, { "initialColor", client.Color }, { "initialFaction", client.Faction } }); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Settings/DisplaySettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Settings/DisplaySettingsLogic.cs index f83ef136ca..88014f3305 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Settings/DisplaySettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Settings/DisplaySettingsLogic.cs @@ -181,7 +181,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; var colorManager = modData.DefaultRules.Actors[SystemActors.World].TraitInfo(); - colorManager.Update(worldRenderer, ps.Color); + colorManager.Color = ps.Color; var colorDropdown = panel.Get("PLAYERCOLOR"); colorDropdown.IsDisabled = () => worldRenderer.World.Type != WorldType.Shellmap; diff --git a/mods/cnc/rules/palettes.yaml b/mods/cnc/rules/palettes.yaml index af2ff8244f..103bb91e91 100644 --- a/mods/cnc/rules/palettes.yaml +++ b/mods/cnc/rules/palettes.yaml @@ -67,10 +67,10 @@ Name: effect Filename: temperat.pal ShadowIndex: 4 - PaletteFromFile@colorpicker: + ColorPickerPalette@colorpicker: Name: colorpicker - Filename: temperat.pal - ShadowIndex: 4 + BasePalette: terrain + RemapIndex: 176, 178, 180, 182, 184, 186, 189, 191, 177, 179, 181, 183, 185, 187, 188, 190 AllowModifiers: false PaletteFromRGBA@cloak: Name: cloak diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index 245cd3cf68..3d449b0d65 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -256,7 +256,6 @@ World: TimeLimitManager: ColorPickerManager: PreviewActor: fact.colorpicker - RemapIndices: 176, 178, 180, 182, 184, 186, 189, 191, 177, 179, 181, 183, 185, 187, 188, 190 PresetHues: 0, 0.125, 0.185, 0.4, 0.54, 0.66, 0.79, 0.875, 0, 0.14, 0.23, 0.43, 0.54, 0.625, 0.77, 0.85 PresetSaturations: 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5 diff --git a/mods/d2k/rules/palettes.yaml b/mods/d2k/rules/palettes.yaml index 31ccb28368..2ae100dd4a 100644 --- a/mods/d2k/rules/palettes.yaml +++ b/mods/d2k/rules/palettes.yaml @@ -17,10 +17,10 @@ Filename: PALETTE.BIN ShadowIndex: 1 AllowModifiers: false - PaletteFromFile@colorpicker: + ColorPickerPalette@colorpicker: Name: colorpicker - Filename: PALETTE.BIN - ShadowIndex: 4 + BasePalette: d2k + RemapIndex: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240 AllowModifiers: false PaletteFromEmbeddedSpritePalette@moveflash-base: Name: moveflash-base diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index d986d0fdd3..150d1142d7 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -237,7 +237,6 @@ World: TimeLimitManager: ColorPickerManager: PreviewActor: carryall.colorpicker - RemapIndices: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240 PresetHues: 0, 0.13, 0.18, 0.3, 0.475, 0.625, 0.82, 0.89, 0.97, 0.05, 0.23, 0.375, 0.525, 0.6, 0.75, 0.85 PresetSaturations: 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.5, 0.35, 0.4, 0.4, 0.5, 0.5, 0.4, 0.35 diff --git a/mods/ra/rules/palettes.yaml b/mods/ra/rules/palettes.yaml index 9ea18cd780..19d21dff4d 100644 --- a/mods/ra/rules/palettes.yaml +++ b/mods/ra/rules/palettes.yaml @@ -41,10 +41,10 @@ Name: effect Filename: temperat.pal ShadowIndex: 4 - PaletteFromFile@colorpicker: + ColorPickerPalette@colorpicker: Name: colorpicker - Filename: temperat.pal - ShadowIndex: 4 + BasePalette: player + RemapIndex: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 AllowModifiers: false PaletteFromFile@desert: Name: desert diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 181eb5ebfa..ac41064cb1 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -282,7 +282,6 @@ World: 1: WarningOneMinuteRemaining ColorPickerManager: PreviewActor: fact.colorpicker - RemapIndices: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 PresetHues: 0, 0.125, 0.22, 0.375, 0.5, 0.56, 0.8, 0.88, 0, 0.15, 0.235, 0.4, 0.47, 0.55, 0.75, 0.85 PresetSaturations: 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5 diff --git a/mods/ts/rules/palettes.yaml b/mods/ts/rules/palettes.yaml index 4f98a75c15..aa424dadf4 100644 --- a/mods/ts/rules/palettes.yaml +++ b/mods/ts/rules/palettes.yaml @@ -101,11 +101,11 @@ BasePalette: sidebar Alpha: 0.5 AllowModifiers: false - PaletteFromFile@colorpicker: + ColorPickerPalette@colorpicker: Name: colorpicker - Filename: unittem.pal + BasePalette: player + RemapIndex: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 AllowModifiers: false - ShadowIndex: 1 PaletteFromFile@alpha: Name: alpha Filename: alpha.pal diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index d1b84a84f5..470a46a6d5 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -382,7 +382,6 @@ World: TimeLimitManager: ColorPickerManager: PreviewActor: mmch.colorpicker - RemapIndices: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 PresetHues: 0, 0.125, 0.185, 0.4, 0.54, 0.66, 0.79, 0.875, 0, 0.14, 0.23, 0.43, 0.54, 0.625, 0.77, 0.85 PresetSaturations: 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5, 0.4, 0.5