|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using BepInEx.Unity.IL2CPP; |
| 5 | +using Reactor.Patches; |
| 6 | +using Reactor.Utilities.Extensions; |
| 7 | + |
| 8 | +namespace Reactor.Utilities; |
| 9 | + |
| 10 | +/// <summary> |
| 11 | +/// Provides a way for mods to show their version information in-game. |
| 12 | +/// </summary> |
| 13 | +public static class ReactorCredits |
| 14 | +{ |
| 15 | + private readonly struct ModIdentifier(string name, string version, Func<Location, bool>? shouldShow, bool isPreRelease) |
| 16 | + { |
| 17 | + private const string NormalColor = "#fff"; |
| 18 | + private const string PreReleaseColor = "#f00"; |
| 19 | + |
| 20 | + public string Name => name; |
| 21 | + |
| 22 | + public string Text { get; } = $"{name} {version}".EscapeRichText().Color(isPreRelease ? PreReleaseColor : NormalColor); |
| 23 | + |
| 24 | + public bool ShouldShow(Location location) |
| 25 | + { |
| 26 | + return shouldShow == AlwaysShow || shouldShow(location); |
| 27 | + } |
| 28 | + } |
| 29 | + |
| 30 | + private static readonly List<ModIdentifier> _modIdentifiers = []; |
| 31 | + |
| 32 | + /// <summary> |
| 33 | + /// Represents the location of where the credit is shown. |
| 34 | + /// </summary> |
| 35 | + public enum Location |
| 36 | + { |
| 37 | + /// <summary> |
| 38 | + /// In the main menu under Reactor/BepInEx versions. |
| 39 | + /// </summary> |
| 40 | + MainMenu, |
| 41 | + |
| 42 | + /// <summary> |
| 43 | + /// During game under the ping tracker. |
| 44 | + /// </summary> |
| 45 | + PingTracker, |
| 46 | + } |
| 47 | + |
| 48 | + /// <summary> |
| 49 | + /// A special value indicating a mod should always show. |
| 50 | + /// </summary> |
| 51 | + public const Func<Location, bool>? AlwaysShow = null; |
| 52 | + |
| 53 | + /// <summary> |
| 54 | + /// Registers a mod with the <see cref="ReactorCredits"/>, adding it to the list of mods that will be displayed. |
| 55 | + /// </summary> |
| 56 | + /// <param name="name">The user-friendly name of the mod. Can contain spaces or special characters.</param> |
| 57 | + /// <param name="version">The version of the mod.</param> |
| 58 | + /// <param name="isPreRelease">If this version is a development or beta version. If true, it will display the mod in red.</param> |
| 59 | + /// <param name="shouldShow"> |
| 60 | + /// This function will be called every frame to determine if the mod should be displayed or not. |
| 61 | + /// This function should return false if your mod is currently disabled or has no effect on gameplay at the time. |
| 62 | + /// If you want the mod to be displayed at all times, you can set this parameter to <see cref="ReactorCredits.AlwaysShow"/>. |
| 63 | + /// </param> |
| 64 | + public static void Register(string name, string version, bool isPreRelease, Func<Location, bool>? shouldShow) |
| 65 | + { |
| 66 | + const int MaxLength = 60; |
| 67 | + |
| 68 | + if (name.Length + version.Length > MaxLength) |
| 69 | + { |
| 70 | + Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorCredits)} because the combined length of the mod name and version is greater than {MaxLength} characters."); |
| 71 | + return; |
| 72 | + } |
| 73 | + |
| 74 | + if (_modIdentifiers.Any(m => m.Name == name)) |
| 75 | + { |
| 76 | + Error($"Mod \"{name}\" is already registered in {nameof(ReactorCredits)}."); |
| 77 | + return; |
| 78 | + } |
| 79 | + |
| 80 | + _modIdentifiers.Add(new ModIdentifier(name, version, shouldShow, isPreRelease)); |
| 81 | + |
| 82 | + _modIdentifiers.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); |
| 83 | + |
| 84 | + if (!isPreRelease) |
| 85 | + { |
| 86 | + Info($"Mod \"{name}\" registered in {nameof(ReactorCredits)} with version {version}."); |
| 87 | + } |
| 88 | + else |
| 89 | + { |
| 90 | + Warning($"Mod \"{name}\" registered in {nameof(ReactorCredits)} with DEVELOPMENT/BETA version {version}."); |
| 91 | + } |
| 92 | + |
| 93 | + ReactorVersionShower.UpdateText(); |
| 94 | + } |
| 95 | + |
| 96 | + /// <summary> |
| 97 | + /// Registers a mod with the <see cref="ReactorCredits"/>, adding it to the list of mods that will be displayed. |
| 98 | + /// </summary> |
| 99 | + /// <typeparam name="T">The BepInEx plugin type to get the name and version from.</typeparam> |
| 100 | + /// <param name="shouldShow"><inheritdoc cref="Register(string,string,bool,System.Func{Location,bool})" path="/param[@name='shouldShow']"/></param> |
| 101 | + public static void Register<T>(Func<Location, bool>? shouldShow) where T : BasePlugin |
| 102 | + { |
| 103 | + var pluginInfo = IL2CPPChainloader.Instance.Plugins.Values.SingleOrDefault(p => p.TypeName == typeof(T).FullName) |
| 104 | + ?? throw new ArgumentException("Couldn't find the metadata for the provided plugin type", nameof(T)); |
| 105 | + |
| 106 | + var metadata = pluginInfo.Metadata; |
| 107 | + |
| 108 | + Register(metadata.Name, metadata.Version.WithoutBuild().Clean(), metadata.Version.IsPreRelease, shouldShow); |
| 109 | + } |
| 110 | + |
| 111 | + internal static string? GetText(Location location) |
| 112 | + { |
| 113 | + var modTexts = _modIdentifiers.Where(m => m.ShouldShow(location)).Select(m => m.Text).ToArray(); |
| 114 | + if (modTexts.Length == 0) return null; |
| 115 | + |
| 116 | + return location switch |
| 117 | + { |
| 118 | + Location.MainMenu => string.Join('\n', modTexts), |
| 119 | + Location.PingTracker => ("<space=3em>" + string.Join(", ", modTexts)).Size("50%").Align("center"), |
| 120 | + _ => throw new ArgumentOutOfRangeException(nameof(location), location, null), |
| 121 | + }; |
| 122 | + } |
| 123 | +} |
0 commit comments