celeste-avali-skin/SourceCode/AvaliSkinSettings.cs

327 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Monocle;
using YamlDotNet.Serialization;
namespace Celeste.Mod.AvaliSkin {
[SettingName("AVALI_SKIN_TITLE")]
public class AvaliSkinSettings : EverestModuleSettings {
private void updateOptions() {
bool loadedCelestenet = Everest.Loader.DependencyLoaded(AvaliSkinModule.CelesteNetMeta);
// send the new settings to other celestenet players if we're connected
if (loadedCelestenet) { updateSkinCelestenet(); }
// menu is not loaded; don't bother
if (ColorModeOptItem == null) { return; }
// disable all the normal options if the main toggle is disabled
ColorModeOptItem.Disabled = !Enabled;
ColorSubmenuItem.Disabled = !Enabled;
// change up the colormode note based on the selection
string colorModeNote = "";
switch (ColorModeOpt) {
case ColorMode.ExternalDash: colorModeNote = "modoptions_avaliskin_colormodeopt_ExternalDash_note"; break;
case ColorMode.ManualPreset: colorModeNote = "modoptions_avaliskin_colormodeopt_ManualPreset_note"; break;
case ColorMode.ManualRGB: colorModeNote = "modoptions_avaliskin_colormodeopt_ManualRGB_note"; break;
}
ColorModeOptNote.Title = colorModeNote.DialogOrKey();
// no point in showing the submenu at all for externaldash; there's
// nothing to configure!
ColorSubmenuItem.Visible = ColorModeOpt != ColorMode.ExternalDash;
// disable and change item visibility based on main toggle or colormode
foreach (var item in DashRGBItems) {
item.Disabled = !Enabled;
item.Visible = ColorModeOpt == ColorMode.ManualRGB;
}
foreach (var item in DashPresetItems) {
item.Disabled = !Enabled;
item.Visible = ColorModeOpt == ColorMode.ManualPreset;
}
// Disable if Celestenet is not installed/enabled.
EnableCelesteNetItem.Disabled = !loadedCelestenet;
// Independently disable the celestenet options
// The user might want the skin disabled, but not for others!
// Also disable these options if Celestenet is not installed/enabled.
CelesteNetSyncItem.Disabled = !EnableCelesteNet || !loadedCelestenet;
CelesteNetEveryoneHasSkinItem.Disabled = !EnableCelesteNet || !loadedCelestenet;
}
// CelesteNet must be loaded when calling this function.
private void updateSkinCelestenet() {
// this needs to be in a seperate function because the entire component
// relies on an assembly reference
if (CelesteNetAvaliComponent.Instance != null) {
CelesteNetAvaliComponent.Instance.SendAvaliSkin();
}
}
// note: YamlDotNet ignores all private member variables
private bool enabled = true;
public bool Enabled {
get => enabled;
set {
enabled = value;
if (Engine.Scene is Level level) {
// we're in a map: reset sprite the same way the Other Self toggle does.
// the hook on PlayerSprite will decide if we should use the Avali skin or not.
Player player = level.Tracker.GetEntity<Player>();
if (player != null) {
PlayerSpriteMode mode = (SaveData.Instance.Assists.PlayAsBadeline ? PlayerSpriteMode.MadelineAsBadeline : player.DefaultSpriteMode);
if (player.Active) {
player.ResetSpriteNextFrame(mode);
} else {
player.ResetSprite(mode);
}
}
}
updateOptions();
}
}
public enum ColorMode {
ExternalDash,
ManualPreset,
ManualRGB
}
public ColorMode ColorModeOpt { get; set; } = ColorMode.ExternalDash;
private TextMenu.Item ColorModeOptItem;
private TextMenu.SubHeader ColorModeOptNote;
public void CreateColorModeOptEntry(TextMenu menu, bool inGame) {
ColorModeOptItem = new TextMenuExt.EnumerableSlider<ColorMode>(
"modoptions_avaliskin_colormodeopt".DialogOrKey(),
// Enum.GetValues returns an Array, which is not enumerable.
// However, normal object vectors are. So...
((ColorMode[]) Enum.GetValues(typeof(ColorMode))).Select(
variant => new KeyValuePair<ColorMode, string>(
variant,
$"modoptions_avaliskin_colormodeopt_{variant.ToString()}".DialogOrKey()
)
),
ColorModeOpt
).Change(opt => {
ColorModeOpt = opt;
updateOptions();
});
menu.Add(ColorModeOptItem);
ColorModeOptItem.AddDescription(menu, "modoptions_avaliskin_colormodeopt_note".DialogOrKey());
List<TextMenu.Item> items = menu.GetItems();
ColorModeOptNote = (TextMenu.SubHeader) items[items.IndexOf(ColorModeOptItem) + 1];
}
#region AvaliColorSettings
// ColorSubmenu is a dummy setting that is only used to position the
// Submenu. This setting is never used, hence why it is Void!
[YamlIgnore]
public Void ColorSubmenu { get; set; }
private TextMenuExt.OptionSubMenu ColorSubmenuItem;
public void CreateColorSubmenuEntry(TextMenu menu, bool inGame) {
DashRGBItems.Clear();
DashPresetItems.Clear();
ColorSubmenuItem = new TextMenuExt.OptionSubMenu(
"AVALI_SKIN_COLORS".DialogOrKey()
);
// This generates n submenus, one for each dash
for (int i = 0; i < DashRGBColor.Count; i++) {
TextMenuExt.IntSlider DashRItem, DashGItem, DashBItem;
TextMenu.Option<ColorChoice> DashColorItem;
int j = i; // C# lambda are wierd: capturing i directly mutates the captured variable
ColorSubmenuItem.Add(
$"AVALI_SKIN_DASH{i}".DialogOrKey(),
new List<TextMenu.Item> {
(DashRItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_RED".DialogOrKey(),
0, 255, DashRGBColor[j].R
).Change(
// C# is stupidly pendatic and doesn't support property assignment in List elements
// so we have to do this ugly shit to avoid breaking up this expression into two
c => DashRGBColor[j] = new Color((byte) c, DashRGBColor[j].B, DashRGBColor[j].G)
)),
(DashGItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_BLUE".DialogOrKey(),
0, 255, DashRGBColor[j].G
).Change(
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, (byte) c, DashRGBColor[j].G)
)),
(DashBItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_GREEN".DialogOrKey(),
0, 255, DashRGBColor[j].B
).Change(
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, DashRGBColor[j].B, (byte) c)
)),
(DashColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
"AVALI_SKIN_COLOR".DialogOrKey(),
DashPreset[j]
).Change(c => DashPreset[j] = c)),
}
);
DashRGBItems.Add(DashRItem);
DashRGBItems.Add(DashGItem);
DashRGBItems.Add(DashBItem);
DashPresetItems.Add(DashColorItem);
}
menu.Add(ColorSubmenuItem);
}
// The following two settings DashRGB & DashPreset are not shown, but
// stored. They are set in the above submenu.
[SettingIgnore]
[YamlIgnore]
public List<Color> DashRGBColor { get; set; } = new List<Color> {
ColorUtil.SettingToColor(ColorChoice.BlueLight),
ColorUtil.SettingToColor(ColorChoice.Turquoise),
ColorUtil.SettingToColor(ColorChoice.Pink),
ColorUtil.SettingToColor(ColorChoice.BlueDark),
ColorUtil.SettingToColor(ColorChoice.Yellow),
ColorUtil.SettingToColor(ColorChoice.Red),
};
// this will get (de)serialized from/into a yaml list
[SettingIgnore]
public IEnumerable<string> DashRGB {
get => DashRGBColor.Select(c => ColorUtil.ColorToHex(c));
set {
DashRGBColor = value.Select(c => ColorUtil.HexToColor(c)).ToList();
}
}
// Stores submenu items that are enabled/disabled when colormode is RGB
private List<TextMenuExt.IntSlider> DashRGBItems = new List<TextMenuExt.IntSlider>();
[SettingIgnore]
public List<ColorChoice> DashPreset { get; set; } = new List<ColorChoice> {
ColorChoice.BlueLight,
ColorChoice.Turquoise,
ColorChoice.Pink,
ColorChoice.BlueDark,
ColorChoice.Yellow,
ColorChoice.Red,
};
// Stores submenu items that are enabled/disabled when colormode is preset
private List<TextMenu.Option<ColorChoice>> DashPresetItems = new List<TextMenu.Option<ColorChoice>>();
public bool EnableCelesteNet { get; set; } = true;
[YamlIgnore]
private TextMenu.Item EnableCelesteNetItem;
public void CreateEnableCelesteNetEntry(TextMenu menu, bool inGame) {
menu.Add(new TextMenu.SubHeader(
"modoptions_avaliskin_celestenet_header".DialogOrKey(), false
));
EnableCelesteNetItem = new TextMenu.OnOff(
"modoptions_avaliskin_celestenet".DialogOrKey(),
EnableCelesteNet
).Change(enabled => {
EnableCelesteNet = enabled;
updateOptions();
});
menu.Add(EnableCelesteNetItem);
if (!Everest.Loader.DependencyLoaded(AvaliSkinModule.CelesteNetMeta)) {
menu.Add(new TextMenu.SubHeader(
"modoptions_avaliskin_celestenet_nocelestenet".DialogOrKey(), false
));
}
}
public enum SendReceive {
SendReceive,
Send,
Receive,
None
}
public SendReceive CelesteNetSync { get; set; } = SendReceive.SendReceive;
[YamlIgnore]
private TextMenu.Item CelesteNetSyncItem;
public void CreateCelesteNetSyncEntry(TextMenu menu, bool inGame) {
CelesteNetSyncItem = new TextMenuExt.EnumerableSlider<SendReceive>(
"modoptions_avaliskin_celestenetsync".DialogOrKey(),
((SendReceive[]) Enum.GetValues(typeof(SendReceive))).Select(
variant => new KeyValuePair<SendReceive, string>(
variant,
$"modoptions_avaliskin_celestenetsync_{variant.ToString()}".DialogOrKey()
)
),
CelesteNetSync
).Change(opt => CelesteNetSync = opt);
menu.Add(CelesteNetSyncItem);
CelesteNetSyncItem.AddDescription(
menu,
"modoptions_avaliskin_celestenetsync_note".DialogOrKey()
);
}
public bool CelesteNetEveryoneHasSkin { get; set; } = false;
[YamlIgnore]
private TextMenu.Item CelesteNetEveryoneHasSkinItem;
public void CreateCelesteNetEveryoneHasSkinEntry(TextMenu menu, bool inGame) {
CelesteNetEveryoneHasSkinItem = new TextMenu.OnOff(
"modoptions_avaliskin_celesteneteveryonehasskin".DialogOrKey(),
CelesteNetEveryoneHasSkin
).Change(enabled => CelesteNetEveryoneHasSkin = enabled);
menu.Add(CelesteNetEveryoneHasSkinItem);
CelesteNetEveryoneHasSkinItem.AddDescription(
menu,
"modoptions_avaliskin_celesteneteveryonehasskin_note".DialogOrKey()
);
// call update options once the entire menu is loaded: this item is
// added last
updateOptions();
}
#endregion
public enum ColorChoice
{
Beige,
Black,
BlueDark,
BlueLight,
Brown,
Cyan,
GreenDark,
GreenLight,
GreyDark,
GreyLight,
Magenta,
Orange,
Pink,
Purple,
Red,
Turquoise,
White,
Yellow,
}
}
}