531 lines
22 KiB
C#
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,
|
|
}
|
|
}
|
|
|
|
|
|
}
|