celeste-avali-skin/SourceCode/AvaliSkinSettings.cs

531 lines
22 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 (DashColorModeOptItem == null) { return; }
// disable all the normal options if the main toggle is disabled
DashColorModeOptItem.Disabled = !Enabled;
DashColorSubmenuItem.Disabled = !Enabled;
BodyColorModeOptItem.Disabled = !Enabled;
BodyColorSubmenuItem.Disabled = !Enabled;
// change up the colormode note based on the selection
string colorModeNote = "";
switch (DashColorModeOpt) {
case DashColorMode.ExternalDash: colorModeNote = "modoptions_avaliskin_dashcolormodeopt_ExternalDash_note"; break;
case DashColorMode.ManualPreset: colorModeNote = "modoptions_avaliskin_dashcolormodeopt_ManualPreset_note"; break;
case DashColorMode.ManualRGB: colorModeNote = "modoptions_avaliskin_dashcolormodeopt_ManualRGB_note"; break;
}
DashColorModeOptNote.Title = colorModeNote.DialogOrKey();
switch (BodyColorModeOpt) {
case ColorMode.ManualPreset: colorModeNote = "modoptions_avaliskin_bodycolormodeopt_ManualPreset_note"; break;
case ColorMode.ManualRGB: colorModeNote = "modoptions_avaliskin_bodycolormodeopt_ManualRGB_note"; break;
}
BodyColorModeOptNote.Title = colorModeNote.DialogOrKey();
// no point in showing the submenu at all for externaldash; there's
// nothing to configure!
DashColorSubmenuItem.Visible = DashColorModeOpt != DashColorMode.ExternalDash;
// disable and change item visibility based on main toggle or colormode
foreach (var item in RGBDashColorItems) {
item.Disabled = !Enabled;
item.Visible = DashColorModeOpt == DashColorMode.ManualRGB;
}
foreach (var item in PresetDashColorItems) {
item.Disabled = !Enabled;
item.Visible = DashColorModeOpt == DashColorMode.ManualPreset;
}
foreach (var item in RGBBodyColorItems) {
item.Disabled = !Enabled;
item.Visible = BodyColorModeOpt == ColorMode.ManualRGB;
}
foreach (var item in PresetBodyColorItems) {
item.Disabled = !Enabled;
item.Visible = BodyColorModeOpt == 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();
}
}
// private static TextMenu.Button NewStringSetting(
// string name, ref string value, int minLength, int maxLength,
// Action<string> onchange
// ) {
// return new TextMenu.Button(name + ": " + value)
// .Pressed(() => {
// Audio.Play(SFX.ui_main_savefile_rename_start);
// menu.SceneAs<Overworld>().Goto<OuiModOptionString>().Init<OuiModOptions>(
// (string) value,
// onchange,
// maxLength,
// minLength
// );
// });
// }
// 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 DashColorMode {
ExternalDash,
ManualPreset,
ManualRGB
}
public DashColorMode DashColorModeOpt { get; set; } = DashColorMode.ExternalDash;
private TextMenu.Item DashColorModeOptItem;
private TextMenu.SubHeader DashColorModeOptNote;
public void CreateDashColorModeOptEntry(TextMenu menu, bool inGame) {
DashColorModeOptItem = new TextMenuExt.EnumerableSlider<DashColorMode>(
"modoptions_avaliskin_dashcolormodeopt".DialogOrKey(),
// Enum.GetValues returns an Array, which is not enumerable.
// However, normal object vectors are. So...
((DashColorMode[]) Enum.GetValues(typeof(DashColorMode))).Select(
variant => new KeyValuePair<DashColorMode, string>(
variant,
$"modoptions_avaliskin_colormodeopt_{variant.ToString()}".DialogOrKey()
)
),
DashColorModeOpt
).Change(opt => {
DashColorModeOpt = opt;
updateOptions();
});
menu.Add(DashColorModeOptItem);
DashColorModeOptItem.AddDescription(menu, "modoptions_avaliskin_dashcolormodeopt_note".DialogOrKey());
List<TextMenu.Item> items = menu.GetItems();
DashColorModeOptNote = (TextMenu.SubHeader) items[items.IndexOf(DashColorModeOptItem) + 1];
}
// DashColorSubmenu 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 DashColorSubmenu { get; set; }
private TextMenuExt.OptionSubMenu DashColorSubmenuItem;
public void CreateDashColorSubmenuEntry(TextMenu menu, bool inGame) {
RGBDashColorItems.Clear();
PresetDashColorItems.Clear();
DashColorSubmenuItem = 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 RItem, GItem, BItem;
TextMenu.Option<ColorChoice> ColorItem;
int j = i; // C# lambda are wierd: capturing i directly mutates the captured variable
DashColorSubmenuItem.Add(
$"AVALI_SKIN_DASH{i}".DialogOrKey(),
new List<TextMenu.Item> {
(RItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_RED".DialogOrKey(),
0, 255, DashRGBColor[j].R
).Change(c => {
// C# is stupidly pendatic and doesn't support
// property assignment for value classes. So we have
// to do this ugly shit instead.
Color col = DashRGBColor[j];
col.R = (byte) c;
DashRGBColor[j] = col;
})),
(GItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_GREEN".DialogOrKey(),
0, 255, DashRGBColor[j].G
).Change(c => {
Color col = DashRGBColor[j];
col.G = (byte) c;
DashRGBColor[j] = col;
})),
(BItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_BLUE".DialogOrKey(),
0, 255, DashRGBColor[j].B
).Change(c => {
Color col = DashRGBColor[j];
col.B = (byte) c;
DashRGBColor[j] = col;
})),
(ColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
"AVALI_SKIN_COLOR".DialogOrKey(),
DashPreset[j]
).Change(c => DashPreset[j] = c)),
}
);
RGBDashColorItems.Add(RItem);
RGBDashColorItems.Add(GItem);
RGBDashColorItems.Add(BItem);
PresetDashColorItems.Add(ColorItem);
}
menu.Add(DashColorSubmenuItem);
}
public enum ColorMode {
ManualPreset,
ManualRGB
}
public ColorMode BodyColorModeOpt { get; set; } = ColorMode.ManualPreset;
private TextMenu.Item BodyColorModeOptItem;
private TextMenu.SubHeader BodyColorModeOptNote;
public void CreateBodyColorModeOptEntry(TextMenu menu, bool inGame) {
BodyColorModeOptItem = new TextMenuExt.EnumerableSlider<ColorMode>(
"modoptions_avaliskin_bodycolormodeopt".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()
)
),
BodyColorModeOpt
).Change(opt => {
BodyColorModeOpt = opt;
updateOptions();
});
menu.Add(BodyColorModeOptItem);
BodyColorModeOptItem.AddDescription(menu, "modoptions_avaliskin_bodycolormodeopt_note".DialogOrKey());
List<TextMenu.Item> items = menu.GetItems();
BodyColorModeOptNote = (TextMenu.SubHeader) items[items.IndexOf(BodyColorModeOptItem) + 1];
}
[YamlIgnore]
public Void BodyColorSubmenu { get; set; }
private TextMenuExt.OptionSubMenu BodyColorSubmenuItem;
public void CreateBodyColorSubmenuEntry(TextMenu menu, bool inGame) {
RGBBodyColorItems.Clear();
PresetBodyColorItems.Clear();
BodyColorSubmenuItem = new TextMenuExt.OptionSubMenu(
"AVALI_SKIN_BODY".DialogOrKey()
);
TextMenuExt.IntSlider RItem, GItem, BItem;
TextMenu.Option<ColorChoice> ColorItem;
BodyColorSubmenuItem.Add(
"AVALI_SKIN_LIGHTBODY".DialogOrKey(),
new List<TextMenu.Item> {
(RItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_RED".DialogOrKey(),
0, 255, LightBodyRGBColor.R
).Change(c => {
Color col = LightBodyRGBColor;
col.R = (byte) c;
LightBodyRGBColor = col;
})),
(GItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_GREEN".DialogOrKey(),
0, 255, LightBodyRGBColor.G
).Change(c => {
Color col = LightBodyRGBColor;
col.G = (byte) c;
LightBodyRGBColor = col;
})),
(BItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_BLUE".DialogOrKey(),
0, 255, LightBodyRGBColor.B
).Change(c => {
Color col = LightBodyRGBColor;
col.B = (byte) c;
LightBodyRGBColor = col;
})),
(ColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
"AVALI_SKIN_COLOR".DialogOrKey(),
LightBodyPreset
).Change(c => LightBodyPreset = c)),
}
);
RGBBodyColorItems.Add(RItem);
RGBBodyColorItems.Add(GItem);
RGBBodyColorItems.Add(BItem);
PresetBodyColorItems.Add(ColorItem);
BodyColorSubmenuItem.Add(
"AVALI_SKIN_DARKBODY".DialogOrKey(),
new List<TextMenu.Item> {
(RItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_RED".DialogOrKey(),
0, 255, DarkBodyRGBColor.R
).Change(c => {
Color col = DarkBodyRGBColor;
col.R = (byte) c;
DarkBodyRGBColor = col;
// we need to manually send the new body color over
updateOptions();
})),
(GItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_GREEN".DialogOrKey(),
0, 255, DarkBodyRGBColor.G
).Change(c => {
Color col = DarkBodyRGBColor;
col.G = (byte) c;
DarkBodyRGBColor = col;
updateOptions();
})),
(BItem = new TextMenuExt.IntSlider(
"AVALI_SKIN_BLUE".DialogOrKey(),
0, 255, DarkBodyRGBColor.B
).Change(c => {
Color col = DarkBodyRGBColor;
col.B = (byte) c;
DarkBodyRGBColor = col;
updateOptions();
})),
(ColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
"AVALI_SKIN_COLOR".DialogOrKey(),
DarkBodyPreset
).Change(c => {
DarkBodyPreset = c;
updateOptions();
})),
}
);
RGBBodyColorItems.Add(RItem);
RGBBodyColorItems.Add(GItem);
RGBBodyColorItems.Add(BItem);
PresetBodyColorItems.Add(ColorItem);
menu.Add(BodyColorSubmenuItem);
}
// 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 => c.ToHexA());
set {
DashRGBColor = value.Select(c => c.HexToColor()).ToList();
}
}
[SettingIgnore]
[YamlIgnore]
public Color LightBodyRGBColor { get; set; } = ColorUtil.HexToColor("a2885c");
[SettingIgnore]
public string LightBodyRGB {
get => LightBodyRGBColor.ToHexA();
set { LightBodyRGBColor = value.HexToColor(); }
}
[SettingIgnore]
[YamlIgnore]
public Color DarkBodyRGBColor { get; set; } = ColorUtil.HexToColor("4e4e4e");
[SettingIgnore]
public string DarkBodyRGB {
get => DarkBodyRGBColor.ToHexA();
set { DarkBodyRGBColor = value.HexToColor(); }
}
[SettingIgnore]
public List<ColorChoice> DashPreset { get; set; } = new List<ColorChoice> {
ColorChoice.BlueLight,
ColorChoice.Turquoise,
ColorChoice.Pink,
ColorChoice.BlueDark,
ColorChoice.Yellow,
ColorChoice.Red,
};
[SettingIgnore]
public ColorChoice LightBodyPreset { get; set; } = ColorChoice.Beige;
[SettingIgnore]
public ColorChoice DarkBodyPreset { get; set; } = ColorChoice.GreyDark;
// Stores submenu dash items that are enabled/disabled when dashcolormode is RGB
private List<TextMenuExt.IntSlider> RGBDashColorItems = new List<TextMenuExt.IntSlider>();
// Stores submenu dash items that are enabled/disabled when dashcolormode is preset
private List<TextMenu.Option<ColorChoice>> PresetDashColorItems = new List<TextMenu.Option<ColorChoice>>();
// Stores submenu body items that are enabled/disabled when bodycolormode is RGB
private List<TextMenuExt.IntSlider> RGBBodyColorItems = new List<TextMenuExt.IntSlider>();
// Stores submenu body items that are enabled/disabled when bodycolormode is preset
private List<TextMenu.Option<ColorChoice>> PresetBodyColorItems = 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();
}
public enum ColorChoice {
Beige,
Black,
BlueDark,
BlueLight,
Brown,
Cyan,
GreenDark,
GreenLight,
GreyDark,
GreyLight,
Magenta,
Orange,
Pink,
Purple,
Red,
Turquoise,
White,
Yellow,
}
}
}