Skip to content

Commit b390585

Browse files
authored
Merge pull request #112 from BadMagic100/colors-galore
Palette-based coloring support and ColorUtils implementation
2 parents 9ad04cd + 160c7e6 commit b390585

11 files changed

+427
-209
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Archipelago.MultiClient.Net.Models;
2+
using System.Collections.Generic;
3+
4+
namespace Archipelago.MultiClient.Net.Colors
5+
{
6+
/// <summary>
7+
/// Contains built-in palettes for color handling
8+
/// </summary>
9+
public static class BuiltInPalettes
10+
{
11+
/// <summary>
12+
/// Default dark palette based on terminal colors
13+
/// </summary>
14+
public static readonly Palette<Color> Dark = new Palette<Color>(Color.White, new Dictionary<PaletteColor, Color>()
15+
{
16+
[PaletteColor.White] = Color.White,
17+
[PaletteColor.Black] = Color.Black,
18+
[PaletteColor.Red] = Color.Red,
19+
[PaletteColor.Green] = Color.Green,
20+
[PaletteColor.Blue] = Color.Blue,
21+
[PaletteColor.Cyan] = Color.Cyan,
22+
[PaletteColor.Magenta] = Color.Magenta,
23+
[PaletteColor.Yellow] = Color.Yellow,
24+
[PaletteColor.SlateBlue] = Color.SlateBlue,
25+
[PaletteColor.Salmon] = Color.Salmon,
26+
[PaletteColor.Plum] = Color.Plum,
27+
});
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using Archipelago.MultiClient.Net.Enums;
2+
using Archipelago.MultiClient.Net.Helpers;
3+
using Archipelago.MultiClient.Net.Models;
4+
5+
namespace Archipelago.MultiClient.Net.Colors
6+
{
7+
/// <summary>
8+
/// Static utilities for getting the appropriate palette color from various protocol-provided information
9+
/// </summary>
10+
public static class ColorUtils
11+
{
12+
/// <summary>
13+
/// The color to be used to represent the currently connected player
14+
/// </summary>
15+
public const PaletteColor ActivePlayerColor = PaletteColor.Magenta;
16+
/// <summary>
17+
/// The color to be used to represent any player other the currently connected player
18+
/// </summary>
19+
public const PaletteColor NonActivePlayerColor = PaletteColor.Yellow;
20+
21+
/// <summary>
22+
/// Gets the palette color corresponding to a JsonMessagePartColor.
23+
/// </summary>
24+
/// <returns>The corresponding palette color, or null if not specified</returns>
25+
public static PaletteColor? GetColor(JsonMessagePartColor color)
26+
{
27+
switch (color)
28+
{
29+
case JsonMessagePartColor.Red:
30+
case JsonMessagePartColor.RedBg:
31+
return PaletteColor.Red;
32+
case JsonMessagePartColor.Green:
33+
case JsonMessagePartColor.GreenBg:
34+
return PaletteColor.Green;
35+
case JsonMessagePartColor.Yellow:
36+
case JsonMessagePartColor.YellowBg:
37+
return PaletteColor.Yellow;
38+
case JsonMessagePartColor.Blue:
39+
case JsonMessagePartColor.BlueBg:
40+
return PaletteColor.Blue;
41+
case JsonMessagePartColor.Magenta:
42+
case JsonMessagePartColor.MagentaBg:
43+
return PaletteColor.Magenta;
44+
case JsonMessagePartColor.Cyan:
45+
case JsonMessagePartColor.CyanBg:
46+
return PaletteColor.Cyan;
47+
case JsonMessagePartColor.Black:
48+
case JsonMessagePartColor.BlackBg:
49+
return PaletteColor.Black;
50+
case JsonMessagePartColor.White:
51+
case JsonMessagePartColor.WhiteBg:
52+
return PaletteColor.White;
53+
default:
54+
return null;
55+
}
56+
}
57+
58+
/// <summary>
59+
/// Gets the palette color corresponding to a HintStatus.
60+
/// </summary>
61+
/// <returns>The corresponding palette color, or null if not specified</returns>
62+
public static PaletteColor? GetColor(HintStatus status)
63+
{
64+
switch (status)
65+
{
66+
case HintStatus.Found:
67+
return PaletteColor.Green;
68+
case HintStatus.NoPriority:
69+
return PaletteColor.SlateBlue;
70+
case HintStatus.Avoid:
71+
return PaletteColor.Salmon;
72+
case HintStatus.Priority:
73+
return PaletteColor.Plum;
74+
default:
75+
return null;
76+
}
77+
}
78+
79+
/// <summary>
80+
/// Gets the palette color corresponding to a Hint's status.
81+
/// </summary>
82+
/// <returns>The corresponding palette color, or null if not specified</returns>
83+
public static PaletteColor? GetColor(Hint hint)
84+
{
85+
return GetColor(hint.Status);
86+
}
87+
88+
/// <summary>
89+
/// Gets the palette color corresponding to an ItemFlags.
90+
/// </summary>
91+
/// <returns>The corresponding palette color, or null if not specified</returns>
92+
public static PaletteColor? GetColor(ItemFlags flags)
93+
{
94+
if (HasFlag(flags, ItemFlags.Advancement))
95+
return PaletteColor.Plum;
96+
if (HasFlag(flags, ItemFlags.NeverExclude))
97+
return PaletteColor.SlateBlue;
98+
if (HasFlag(flags, ItemFlags.Trap))
99+
return PaletteColor.Salmon;
100+
101+
return PaletteColor.Cyan;
102+
}
103+
104+
/// <summary>
105+
/// Gets the palette color corresponding to an ItemInfo's flags.
106+
/// </summary>
107+
/// <returns>The corresponding palette color, or null if not specified</returns>
108+
public static PaletteColor? GetColor(ItemInfo item)
109+
{
110+
return GetColor(item.Flags);
111+
}
112+
113+
/// <summary>
114+
/// Gets the palette color corresponding to a PlayerInfo.
115+
/// </summary>
116+
/// <returns>The corresponding palette color, or null if not specified</returns>
117+
public static PaletteColor? GetColor(PlayerInfo player, IConnectionInfoProvider connectionInfo)
118+
{
119+
bool isActivePlayer = player.Slot == connectionInfo.Slot;
120+
return isActivePlayer ? ActivePlayerColor : NonActivePlayerColor;
121+
}
122+
123+
static bool HasFlag(ItemFlags flags, ItemFlags flag) =>
124+
#if NET35
125+
(flags & flag) > 0;
126+
#else
127+
flags.HasFlag(flag);
128+
#endif
129+
}
130+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Archipelago.MultiClient.Net.Colors
5+
{
6+
/// <summary>
7+
/// Palette/theme support for client colors
8+
/// </summary>
9+
/// <typeparam name="T">
10+
/// The type of actual color in the palette, typically an engine- or framework-specific Color struct
11+
/// </typeparam>
12+
public class Palette<T>
13+
{
14+
private readonly Dictionary<PaletteColor, T> palette;
15+
16+
/// <summary>
17+
/// The default color to use in the palette when color is not specified, or the requested color is not
18+
/// in the palette
19+
/// </summary>
20+
public T DefaultColor { get; private set; }
21+
22+
/// <summary>
23+
/// Retrieves a color from the palette
24+
/// </summary>
25+
/// <param name="color">The palette color to look up</param>
26+
/// <returns>
27+
/// The requested color. Returns the default color if the requested color was null or
28+
/// not defined in this palette.
29+
/// </returns>
30+
public T this[PaletteColor? color]
31+
{
32+
get
33+
{
34+
if (color.HasValue && palette.TryGetValue(color.Value, out T result))
35+
{
36+
return result;
37+
}
38+
return DefaultColor;
39+
}
40+
}
41+
42+
/// <summary>
43+
/// Creates a custom palette
44+
/// </summary>
45+
/// <param name="defaultColor">The default color for the palette</param>
46+
/// <param name="palette">
47+
/// The colors in the palette. Note that this does not need to define a mapping for every
48+
/// PaletteColor if it is not desired.
49+
/// </param>
50+
public Palette(T defaultColor, Dictionary<PaletteColor, T> palette)
51+
{
52+
this.DefaultColor = defaultColor;
53+
this.palette = new Dictionary<PaletteColor, T>(palette);
54+
}
55+
56+
/// <summary>
57+
/// Creates a copy of this palette with all values transformed to another type
58+
/// </summary>
59+
/// <typeparam name="U">The desired color type for the new palette</typeparam>
60+
/// <param name="transform">A transformation function to convert to the new type</param>
61+
public Palette<U> Transform<U>(Func<T, U> transform)
62+
{
63+
U newDefault = transform(DefaultColor);
64+
Dictionary<PaletteColor, U> newPalette = new Dictionary<PaletteColor, U>(palette.Count);
65+
foreach (KeyValuePair<PaletteColor, T> kv in palette)
66+
{
67+
newPalette[kv.Key] = transform(kv.Value);
68+
}
69+
return new Palette<U>(newDefault, newPalette);
70+
}
71+
72+
/// <summary>
73+
/// Creates a copy of this palette with the specified colors replaced
74+
/// </summary>
75+
/// <param name="newDefault">The new default color</param>
76+
/// <param name="paletteEdits">A mapping specifying colors to replace</param>
77+
public Palette<T> Edit(T newDefault, Dictionary<PaletteColor, T> paletteEdits)
78+
{
79+
Dictionary<PaletteColor, T> newPalette = new Dictionary<PaletteColor, T>(palette);
80+
foreach (KeyValuePair<PaletteColor, T> kv in paletteEdits)
81+
{
82+
newPalette[kv.Key] = kv.Value;
83+
}
84+
return new Palette<T>(newDefault, newPalette);
85+
}
86+
87+
/// <summary>
88+
/// Creates a copy of this palette with the specified colors replaced. The default color
89+
/// is preserved.
90+
/// </summary>
91+
/// <param name="paletteEdits">A mapping specifying colors to replace</param>
92+
public Palette<T> Edit(Dictionary<PaletteColor, T> paletteEdits)
93+
{
94+
return Edit(DefaultColor, paletteEdits);
95+
}
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Archipelago.MultiClient.Net.Colors
2+
{
3+
/// <summary>
4+
/// Colors used in client palettes; some by protocol and some by convention.
5+
/// </summary>
6+
public enum PaletteColor
7+
{
8+
White,
9+
Black,
10+
Red,
11+
Green,
12+
Blue,
13+
Cyan,
14+
Magenta,
15+
Yellow,
16+
SlateBlue,
17+
Salmon,
18+
Plum
19+
}
20+
}

Archipelago.MultiClient.Net/MessageLog/Parts/EntranceMessagePart.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Archipelago.MultiClient.Net.MessageLog.Parts
88
/// <seealso cref="T:Archipelago.MultiClient.Net.MessageLog.Parts.MessagePart"/>
99
public class EntranceMessagePart : MessagePart
1010
{
11-
internal EntranceMessagePart(JsonMessagePart messagePart) : base(MessagePartType.Entrance, messagePart, Color.Blue)
11+
internal EntranceMessagePart(JsonMessagePart messagePart) : base(MessagePartType.Entrance, messagePart, Colors.PaletteColor.Blue)
1212
{
1313
Text = messagePart.Text;
1414
}

Archipelago.MultiClient.Net/MessageLog/Parts/HintStatusMessagePart.cs

+3-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Archipelago.MultiClient.Net.Enums;
1+
using Archipelago.MultiClient.Net.Colors;
22
using Archipelago.MultiClient.Net.Models;
33

44
namespace Archipelago.MultiClient.Net.MessageLog.Parts
@@ -13,23 +13,9 @@ internal HintStatusMessagePart(JsonMessagePart messagePart) : base(MessagePartTy
1313
{
1414
Text = messagePart.Text;
1515

16-
switch (messagePart.HintStatus)
16+
if (messagePart.HintStatus.HasValue)
1717
{
18-
case HintStatus.Found:
19-
Color = Color.Green;
20-
break;
21-
case HintStatus.Unspecified:
22-
Color = Color.White;
23-
break;
24-
case HintStatus.NoPriority:
25-
Color = Color.SlateBlue;
26-
break;
27-
case HintStatus.Avoid:
28-
Color = Color.Salmon;
29-
break;
30-
case HintStatus.Priority:
31-
Color = Color.Plum;
32-
break;
18+
PaletteColor = ColorUtils.GetColor(messagePart.HintStatus.Value);
3319
}
3420
}
3521
}

Archipelago.MultiClient.Net/MessageLog/Parts/ItemMessagePart.cs

+3-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Archipelago.MultiClient.Net.DataPackage;
1+
using Archipelago.MultiClient.Net.Colors;
2+
using Archipelago.MultiClient.Net.DataPackage;
23
using Archipelago.MultiClient.Net.Enums;
34
using Archipelago.MultiClient.Net.Helpers;
45
using Archipelago.MultiClient.Net.Models;
@@ -29,7 +30,7 @@ public class ItemMessagePart : MessagePart
2930
internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part) : base(MessagePartType.Item, part)
3031
{
3132
Flags = part.Flags ?? ItemFlags.None;
32-
Color = GetColor(Flags);
33+
PaletteColor = ColorUtils.GetColor(Flags);
3334
Player = part.Player ?? 0;
3435

3536
var game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game;
@@ -46,24 +47,5 @@ internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMes
4647
break;
4748
}
4849
}
49-
50-
static Color GetColor(ItemFlags flags)
51-
{
52-
if (HasFlag(flags, ItemFlags.Advancement))
53-
return Color.Plum;
54-
if (HasFlag(flags, ItemFlags.NeverExclude))
55-
return Color.SlateBlue;
56-
if (HasFlag(flags, ItemFlags.Trap))
57-
return Color.Salmon;
58-
59-
return Color.Cyan;
60-
}
61-
62-
static bool HasFlag(ItemFlags flags, ItemFlags flag) =>
63-
#if NET35
64-
(flags & flag) > 0;
65-
#else
66-
flags.HasFlag(flag);
67-
#endif
6850
}
6951
}

Archipelago.MultiClient.Net/MessageLog/Parts/LocationMessagePart.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class LocationMessagePart : MessagePart
2222
public int Player { get; }
2323

2424
internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part)
25-
: base(MessagePartType.Location, part, Color.Green)
25+
: base(MessagePartType.Location, part, Colors.PaletteColor.Green)
2626
{
2727
Player = part.Player ?? 0;
2828

0 commit comments

Comments
 (0)