Exemplo n.º 1
0
        protected virtual void CreateModMenuSectionHeader(TextMenu menu, bool inGame, EventInstance snapshot)
        {
            Type type = SettingsType;
            EverestModuleSettings settings = _Settings;

            if (type == null || settings == null)
            {
                return;
            }

            string typeName = type.Name.ToLowerInvariant();

            if (typeName.EndsWith("settings"))
            {
                typeName = typeName.Substring(0, typeName.Length - 8);
            }
            string nameDefaultPrefix = $"modoptions_{typeName}_";

            string name; // We lazily reuse this field for the props later on.

            name = type.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}title";
            name = name.DialogCleanOrNull() ?? Metadata.Name.SpacedPascalCase();

            menu.Add(new patch_TextMenu.patch_SubHeader(name + " | v." + Metadata.VersionString));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Load the mod settings. Loads the settings from {UserIO.GetSavePath("Saves")}/modsettings-{Metadata.Name}.celeste by default.
        /// </summary>
        public virtual void LoadSettings()
        {
            if (SettingsType == null)
            {
                return;
            }

            _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);

            string path = patch_UserIO.GetSaveFilePath("modsettings-" + Metadata.Name);

            // Temporary fallback to help migrate settings from their old location.
            if (!File.Exists(path))
            {
                path = Path.Combine(Everest.PathEverest, "ModSettings-OBSOLETE", Metadata.Name + ".yaml");
            }

            if (!File.Exists(path))
            {
                return;
            }

            try {
                using (Stream stream = File.OpenRead(path)) {
                    if (_Settings is EverestModuleBinarySettings)
                    {
                        using (BinaryReader reader = new BinaryReader(stream))
                            ((EverestModuleBinarySettings)_Settings).Read(reader);
                    }
                    else
                    {
                        IDeserializer deserializer = new DeserializerBuilder()
                                                     .IgnoreUnmatchedProperties()
                                                     .WithObjectFactory(t => _Settings)
                                                     .Build();
                        using (StreamReader reader = new StreamReader(path))
                            _Settings = (EverestModuleSettings)deserializer.Deserialize(reader, SettingsType);
                    }
                }
            } catch {
            }

            if (_Settings == null)
            {
                _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Load the mod settings. Loads the settings from {Everest.PathSettings}/{Metadata.Name}.yaml by default.
        /// </summary>
        public virtual void LoadSettings()
        {
            if (SettingsType == null)
            {
                return;
            }

            _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);

            string extension = ".yaml";

            if (_Settings is EverestModuleBinarySettings)
            {
                extension = ".bin";
            }

            string path = Path.Combine(Everest.PathSettings, Metadata.Name + extension);

            if (!File.Exists(path))
            {
                return;
            }

            try {
                using (Stream stream = File.OpenRead(path)) {
                    if (_Settings is EverestModuleBinarySettings)
                    {
                        // .bin
                        using (BinaryReader reader = new BinaryReader(stream))
                            ((EverestModuleBinarySettings)_Settings).Read(reader);
                    }
                    else
                    {
                        // .yaml
                        using (StreamReader reader = new StreamReader(path)) {
                            _Settings = (EverestModuleSettings)YamlHelper.Deserializer.Deserialize(reader, SettingsType);
                        }
                    }
                }
            } catch {
            }

            if (_Settings == null)
            {
                _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Load the mod settings. Loads the settings from {Everest.PathSettings}/{Metadata.Name}.yaml by default.
        /// </summary>
        public virtual void LoadSettings()
        {
            if (SettingsType == null)
            {
                return;
            }
            string path = Path.Combine(Everest.PathSettings, Metadata.Name + ".yaml");

            if (!File.Exists(path))
            {
                _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);
                return;
            }
            using (Stream stream = File.OpenRead(path))
                using (StreamReader reader = new StreamReader(path))
                    _Settings = (EverestModuleSettings)YamlHelper.Deserializer.Deserialize(reader, SettingsType);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Load the mod settings. Loads the settings from {UserIO.GetSavePath("Saves")}/modsettings-{Metadata.Name}.celeste by default.
        /// </summary>
        public virtual void LoadSettings()
        {
            if (SettingsType == null)
            {
                return;
            }

            _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);

            string path = patch_UserIO.GetSaveFilePath("modsettings-" + Metadata.Name);

            // Temporary fallback to help migrate settings from their old location.
            if (!File.Exists(path))
            {
                path = Path.Combine(Everest.PathEverest, "ModSettings-OBSOLETE", Metadata.Name + ".yaml");
            }

            if (!File.Exists(path))
            {
                return;
            }

            try {
                using (Stream stream = File.OpenRead(path)) {
                    if (_Settings is EverestModuleBinarySettings)
                    {
                        using (BinaryReader reader = new BinaryReader(stream))
                            ((EverestModuleBinarySettings)_Settings).Read(reader);
                    }
                    else
                    {
                        using (StreamReader reader = new StreamReader(stream))
                            YamlHelper.DeserializerUsing(_Settings).Deserialize(reader, SettingsType);
                    }
                }
            } catch (Exception e) {
                Logger.Log(LogLevel.Warn, "EverestModule", $"Failed to load the settings of {Metadata.Name}!");
                Logger.LogDetailed(e);
            }

            if (_Settings == null)
            {
                _Settings = (EverestModuleSettings)SettingsType.GetConstructor(Everest._EmptyTypeArray).Invoke(Everest._EmptyObjectArray);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Create the mod menu subsection including the section header in the given menu.
        /// The default implementation uses reflection to attempt creating a menu.
        /// </summary>
        /// <param name="menu">Menu to add the section to.</param>
        /// <param name="inGame">Whether we're in-game (paused) or in the main menu.</param>
        /// <param name="snapshot">The Level.PauseSnapshot</param>
        public virtual void CreateModMenuSection(TextMenu menu, bool inGame, EventInstance snapshot)
        {
            Type type = SettingsType;
            EverestModuleSettings settings = _Settings;

            if (type == null || settings == null)
            {
                return;
            }

            // The default name prefix.
            string typeName = type.Name.ToLowerInvariant();

            if (typeName.EndsWith("settings"))
            {
                typeName = typeName.Substring(0, typeName.Length - 8);
            }
            string nameDefaultPrefix = $"modoptions_{typeName}_";

            // Any attributes we may want to get and read from later.
            SettingInGameAttribute      attribInGame;
            SettingRangeAttribute       attribRange;
            SettingNumberInputAttribute attribNumber;

            // If the settings type has got the InGame attrib, only show it in the matching situation.
            if ((attribInGame = type.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                attribInGame.InGame != inGame)
            {
                return;
            }

            bool headerCreated = false;

            if (GetType().GetMethod("CreateModMenuSection").DeclaringType != typeof(EverestModule))
            {
                CreateModMenuSectionHeader(menu, inGame, snapshot);
                headerCreated = true;
            }

            PropertyInfo[] props;
            if (type == _PrevSettingsType)
            {
                props = _PrevSettingsProps;
            }
            else
            {
                _PrevSettingsProps = props = type.GetProperties();
                _PrevSettingsType  = type;
            }

            foreach (PropertyInfo prop in props)
            {
                MethodInfo creator = type.GetMethod(
                    $"Create{prop.Name}Entry",
                    BindingFlags.Public | BindingFlags.Instance,
                    null,
                    new Type[] { typeof(TextMenu), typeof(bool) },
                    new ParameterModifier[0]
                    );

                if (creator != null)
                {
                    if (!headerCreated)
                    {
                        CreateModMenuSectionHeader(menu, inGame, snapshot);
                        headerCreated = true;
                    }

                    creator.GetFastDelegate()(settings, menu, inGame);
                    continue;
                }

                if ((attribInGame = prop.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                    attribInGame.InGame != inGame)
                {
                    continue;
                }

                if (prop.GetCustomAttribute <SettingIgnoreAttribute>() != null)
                {
                    continue;
                }

                if (!prop.CanRead || !prop.CanWrite)
                {
                    continue;
                }

                string name = prop.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}{prop.Name.ToLowerInvariant()}";
                name = name.DialogCleanOrNull() ?? prop.Name.SpacedPascalCase();

                bool needsRelaunch = prop.GetCustomAttribute <SettingNeedsRelaunchAttribute>() != null;

                string description = prop.GetCustomAttribute <SettingSubTextAttribute>()?.Description;

                TextMenu.Item item     = null;
                Type          propType = prop.PropertyType;
                object        value    = prop.GetValue(settings);

                // Create the matching item based off of the type and attributes.

                if (propType == typeof(bool))
                {
                    item =
                        new TextMenu.OnOff(name, (bool)value)
                        .Change(v => prop.SetValue(settings, v))
                    ;
                }
                else if (
                    propType == typeof(int) &&
                    (attribRange = prop.GetCustomAttribute <SettingRangeAttribute>()) != null
                    )
                {
                    if (attribRange.LargeRange)
                    {
                        item =
                            new TextMenuExt.IntSlider(name, attribRange.Min, attribRange.Max, (int)value)
                            .Change(v => prop.SetValue(settings, v))
                        ;
                    }
                    else
                    {
                        item =
                            new TextMenu.Slider(name, i => i.ToString(), attribRange.Min, attribRange.Max, (int)value)
                            .Change(v => prop.SetValue(settings, v))
                        ;
                    }
                }
                else if ((propType == typeof(int) || propType == typeof(float)) &&
                         (attribNumber = prop.GetCustomAttribute <SettingNumberInputAttribute>()) != null)
                {
                    float          currentValue;
                    Action <float> valueSetter;
                    if (propType == typeof(int))
                    {
                        currentValue = (int)value;
                        valueSetter  = v => prop.SetValue(settings, (int)v);
                    }
                    else
                    {
                        currentValue = (float)value;
                        valueSetter  = v => prop.SetValue(settings, v);
                    }
                    int  maxLength      = attribNumber.MaxLength;
                    bool allowNegatives = attribNumber.AllowNegatives;

                    item =
                        new TextMenu.Button(name + ": " + currentValue.ToString($"F{maxLength}").TrimEnd('0').TrimEnd('.'))
                        .Pressed(() => {
                        Audio.Play(SFX.ui_main_savefile_rename_start);
                        menu.SceneAs <Overworld>().Goto <OuiNumberEntry>().Init <OuiModOptions>(
                            currentValue,
                            valueSetter,
                            maxLength,
                            propType == typeof(float),
                            allowNegatives
                            );
                    })
                    ;
                }
                else if (propType.IsEnum)
                {
                    Array enumValues = Enum.GetValues(propType);
                    Array.Sort((int[])enumValues);
                    string enumNamePrefix = $"{nameDefaultPrefix}{prop.Name.ToLowerInvariant()}_";
                    item =
                        new TextMenu.Slider(name, (i) => {
                        string enumName = enumValues.GetValue(i).ToString();
                        return
                        ($"{enumNamePrefix}{enumName.ToLowerInvariant()}".DialogCleanOrNull() ??
                         $"modoptions_{propType.Name.ToLowerInvariant()}_{enumName.ToLowerInvariant()}".DialogCleanOrNull() ??
                         enumName);
                    }, 0, enumValues.Length - 1, (int)value)
                        .Change(v => prop.SetValue(settings, v))
                    ;
                }
                else if (!inGame && propType == typeof(string))
                {
                    int maxValueLength = prop.GetCustomAttribute <SettingMaxLengthAttribute>()?.Max ?? 12;
                    int minValueLength = prop.GetCustomAttribute <SettingMinLengthAttribute>()?.Min ?? 1;

                    item =
                        new TextMenu.Button(name + ": " + value)
                        .Pressed(() => {
                        Audio.Play(SFX.ui_main_savefile_rename_start);
                        menu.SceneAs <Overworld>().Goto <OuiModOptionString>().Init <OuiModOptions>(
                            (string)value,
                            v => prop.SetValue(settings, v),
                            maxValueLength,
                            minValueLength
                            );
                    })
                    ;
                }

                if (item == null)
                {
                    continue;
                }

                if (!headerCreated)
                {
                    CreateModMenuSectionHeader(menu, inGame, snapshot);
                    headerCreated = true;
                }

                menu.Add(item);

                if (needsRelaunch)
                {
                    item = item.NeedsRelaunch(menu);
                }

                if (description != null)
                {
                    item = item.AddDescription(menu, description.DialogCleanOrNull() ?? description);
                }
            }

            foreach (PropertyInfo prop in type.GetProperties())
            {
                if ((attribInGame = prop.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                    attribInGame.InGame != inGame)
                {
                    continue;
                }

                if (prop.GetCustomAttribute <SettingIgnoreAttribute>() != null)
                {
                    continue;
                }

                if (!prop.CanRead || !prop.CanWrite)
                {
                    continue;
                }

                if (!typeof(ButtonBinding).IsAssignableFrom(prop.PropertyType))
                {
                    continue;
                }

                if (!headerCreated)
                {
                    CreateModMenuSectionHeader(menu, inGame, snapshot);
                    headerCreated = true;
                }

                CreateModMenuSectionKeyBindings(menu, inGame, snapshot);
                break;
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Create the mod menu subsection including the section header in the given menu.
        /// The default implementation uses reflection to attempt creating a menu.
        /// </summary>
        /// <param name="menu">Menu to add the section to.</param>
        /// <param name="inGame">Whether we're in-game (paused) or in the main menu.</param>
        /// <param name="snapshot">The Level.PauseSnapshot</param>
        public virtual void CreateModMenuSection(TextMenu menu, bool inGame, EventInstance snapshot)
        {
            Type type = SettingsType;
            EverestModuleSettings settings = _Settings;

            if (type == null || settings == null)
            {
                return;
            }

            // The default name prefix.
            string typeName = type.Name.ToLowerInvariant();

            if (typeName.EndsWith("settings"))
            {
                typeName = typeName.Substring(0, typeName.Length - 8);
            }
            string nameDefaultPrefix = $"modoptions_{typeName}_";

            // Any attributes we may want to get and read from later.
            SettingInGameAttribute attribInGame;
            SettingRangeAttribute  attribRange;

            // If the settings type has got the InGame attrib, only show it in the matching situation.
            if ((attribInGame = type.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                attribInGame.InGame != inGame)
            {
                return;
            }

            // The settings subheader.
            string name; // We lazily reuse this field for the props later on.

            name = type.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}title";
            name = name.DialogCleanOrNull() ?? Metadata.Name.SpacedPascalCase();

            menu.Add(new TextMenu.SubHeader(name + " | v." + Metadata.VersionString));

            PropertyInfo[] props;
            if (type == _PrevSettingsType)
            {
                props = _PrevSettingsProps;
            }
            else
            {
                _PrevSettingsProps = props = type.GetProperties();
                _PrevSettingsType  = type;
            }

            foreach (PropertyInfo prop in props)
            {
                MethodInfo creator = type.GetMethod(
                    $"Create{prop.Name}Entry",
                    BindingFlags.Public | BindingFlags.Instance,
                    null,
                    new Type[] { typeof(TextMenu), typeof(bool) },
                    new ParameterModifier[0]
                    );

                if (creator != null)
                {
                    creator.GetFastDelegate()(settings, menu, inGame);
                    continue;
                }

                if ((attribInGame = prop.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                    attribInGame.InGame != inGame)
                {
                    continue;
                }

                if (prop.GetCustomAttribute <SettingIgnoreAttribute>() != null)
                {
                    continue;
                }

                if (!prop.CanRead || !prop.CanWrite)
                {
                    continue;
                }

                name = prop.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}{prop.Name.ToLowerInvariant()}";
                name = name.DialogCleanOrNull() ?? prop.Name.SpacedPascalCase();

                bool needsRelaunch = prop.GetCustomAttribute <SettingNeedsRelaunchAttribute>() != null;

                TextMenu.Item item     = null;
                Type          propType = prop.PropertyType;
                object        value    = prop.GetValue(settings);

                // Create the matching item based off of the type and attributes.

                if (propType == typeof(bool))
                {
                    item =
                        new TextMenu.OnOff(name, (bool)value)
                        .Change(v => prop.SetValue(settings, v))
                    ;
                }
                else if (
                    propType == typeof(int) &&
                    (attribRange = prop.GetCustomAttribute <SettingRangeAttribute>()) != null
                    )
                {
                    item =
                        new TextMenu.Slider(name, i => i.ToString(), attribRange.Min, attribRange.Max, (int)value)
                        .Change(v => prop.SetValue(settings, v))
                    ;
                }
                else if (propType.IsEnum)
                {
                    Array enumValues = Enum.GetValues(propType);
                    Array.Sort((int[])enumValues);
                    string enumNamePrefix = $"{nameDefaultPrefix}{prop.Name.ToLowerInvariant()}_";
                    item =
                        new TextMenu.Slider(name, (i) => {
                        string enumName = enumValues.GetValue(i).ToString();
                        string fullName = $"{enumNamePrefix}{enumName.ToLowerInvariant()}";
                        return(fullName.DialogCleanOrNull() ?? enumName);
                    }, 0, enumValues.Length - 1, (int)value)
                        .Change(v => prop.SetValue(settings, v))
                    ;
                }
                else if (!inGame && propType == typeof(string))
                {
                    item =
                        new TextMenu.Button(name + ": " + value)
                        .Pressed(() => {
                        Audio.Play(Sfxs.ui_main_savefile_rename_start);
                        menu.SceneAs <Overworld>().Goto <OuiModOptionString>().Init <OuiModOptions>(
                            (string)value,
                            v => prop.SetValue(settings, v)
                            );
                    })
                    ;
                }

                if (item == null)
                {
                    continue;
                }

                if (needsRelaunch)
                {
                    item = item.NeedsRelaunch();
                }

                menu.Add(item);
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Create the mod menu subsection including the section header in the given menu.
        /// The default implementation uses reflection to attempt creating a menu.
        /// </summary>
        /// <param name="menu">Menu to add the section to.</param>
        /// <param name="inGame">Whether we're in-game (paused) or in the main menu.</param>
        /// <param name="snapshot">The Level.PauseSnapshot</param>
        public virtual void CreateModMenuSection(TextMenu menu, bool inGame, EventInstance snapshot)
        {
            Type type = SettingsType;
            EverestModuleSettings settings = _Settings;

            if (type == null || settings == null)
            {
                return;
            }

            // The default name prefix.
            string nameDefaultPrefix = $"modoptions_{type.Name.ToLowerInvariant()}_";

            if (nameDefaultPrefix.EndsWith("Settings"))
            {
                nameDefaultPrefix = nameDefaultPrefix.Substring(0, nameDefaultPrefix.Length - 8);
            }

            // Any attributes we may want to get and read from later.
            SettingInGameAttribute attribInGame;
            SettingRangeAttribute  attribRange;

            // If the settings type has got the InGame attrib, only show it in the matching situation.
            if ((attribInGame = type.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                attribInGame.InGame != inGame)
            {
                return;
            }

            // The settings subheader.
            string name; // We lazily reuse this field for the props later on.

            name = type.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}title";
            name = name.DialogCleanOrNull() ?? Metadata.Name.SpacedPascalCase();

            menu.Add(new TextMenu.SubHeader(name));

            PropertyInfo[] props;
            if (type == _PrevSettingsType)
            {
                props = _PrevSettingsProps;
            }
            else
            {
                _PrevSettingsProps = props = type.GetProperties();
                _PrevSettingsType  = type;
            }

            foreach (PropertyInfo prop in props)
            {
                if ((attribInGame = prop.GetCustomAttribute <SettingInGameAttribute>()) != null &&
                    attribInGame.InGame != inGame)
                {
                    continue;
                }

                if (prop.GetCustomAttribute <SettingIgnoreAttribute>() != null)
                {
                    continue;
                }

                if (!prop.CanRead || !prop.CanWrite)
                {
                    continue;
                }

                name = prop.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}{prop.Name.ToLowerInvariant()}";
                name = name.DialogCleanOrNull() ?? prop.Name.SpacedPascalCase();

                bool needsRelaunch = prop.GetCustomAttribute <SettingNeedsRelaunchAttribute>() != null;

                TextMenu.Item item     = null;
                Type          propType = prop.PropertyType;
                object        value    = prop.GetValue(settings);

                // Create the matching item based off of the type and attributes.

                if (propType == typeof(bool))
                {
                    item =
                        new TextMenu.OnOff(name, (bool)value)
                        .Change(v => prop.SetValue(settings, v))
                        .NeedsRelaunch(needsRelaunch)
                    ;
                }
                else if (
                    propType == typeof(int) &&
                    (attribRange = prop.GetCustomAttribute <SettingRangeAttribute>()) != null
                    )
                {
                    item =
                        new TextMenu.Slider(name, i => i.ToString(), attribRange.Min, attribRange.Max, (int)value)
                        .Change(v => prop.SetValue(settings, v))
                        .NeedsRelaunch(needsRelaunch)
                    ;
                }

                if (item == null)
                {
                    continue;
                }
                menu.Add(item);
            }
        }