Aragas/Bannerlord.MBOptionScreen

View on GitHub
src/MCM.UI/Functionality/DefaultGameMenuScreenHandler.cs

Summary

Maintainability
A
0 mins
Test Coverage
using Bannerlord.BUTR.Shared.Extensions;

using BUTR.DependencyInjection.Logger;

using HarmonyLib;
using HarmonyLib.BUTR.Extensions;

using MCM.UI.Utils;

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.MountAndBlade;
using TaleWorlds.MountAndBlade.ViewModelCollection.InitialMenu;
using TaleWorlds.ScreenSystem;

namespace MCM.UI.Functionality
{
    internal sealed class DefaultGameMenuScreenHandler : BaseGameMenuScreenHandler
    {
        private static readonly AccessTools.FieldRef<InitialMenuOptionVM, InitialStateOption>? InitialStateOption =
            AccessTools2.FieldRefAccess<InitialMenuOptionVM, InitialStateOption>("InitialStateOption");

        private static readonly WeakReference<InitialMenuVM> _instance = new(null!);
        private static Dictionary<string, (int, Func<ScreenBase?>, TextObject)> ScreensCache { get; } = new();

        private readonly IBUTRLogger _logger;

        public DefaultGameMenuScreenHandler(IBUTRLogger<DefaultGameMenuScreenHandler> logger)
        {
            _logger = logger;

            var harmony = new Harmony("bannerlord.mcm.mainmenuscreeninjection_v4");
            harmony.Patch(
                AccessTools2.Method(typeof(InitialMenuVM), "RefreshMenuOptions"),
                postfix: new HarmonyMethod(AccessTools2.Method(typeof(DefaultGameMenuScreenHandler), nameof(RefreshMenuOptionsPostfix)), 300));
        }

        [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "For ReSharper")]
        [SuppressMessage("ReSharper", "InconsistentNaming")]
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void RefreshMenuOptionsPostfix(InitialMenuVM __instance, ref MBBindingList<InitialMenuOptionVM>? ____menuOptions)
        {
            if (____menuOptions is null || InitialStateOption is null) return;

            _instance.SetTarget(__instance);
            foreach (var (key, value) in ScreensCache)
            {
                var (index, screenFactory, text) = value;

                var initialState = InitialStateOptionUtils.Create(key,
                    text,
                    9000,
                    () =>
                    {
                        var screen = screenFactory();
                        if (screen is not null)
                            ScreenManager.PushScreen(screen);
                    },
                    () => (false, null));
                var insertIndex = ____menuOptions.FindIndex(i => InitialStateOption(i).OrderIndex > index);
                ____menuOptions.Insert(insertIndex, new InitialMenuOptionVM(initialState));
            }
        }

        public override void AddScreen(string internalName, int index, Func<ScreenBase?> screenFactory, TextObject? text)
        {
            if (text is null) return;

            if (_instance.TryGetTarget(out var instance) && InitialStateOption is not null)
            {
                var initialState = InitialStateOptionUtils.Create(internalName,
                    text,
                    index,
                    () =>
                    {
                        var screen = screenFactory();
                        if (screen is not null)
                            ScreenManager.PushScreen(screen);
                    },
                    () => (false, null));
                var menuOptions = instance.MenuOptions;
                var insertIndex = menuOptions.FindIndex(i => InitialStateOption(i).OrderIndex > index);
                menuOptions?.Insert(insertIndex, new InitialMenuOptionVM(initialState));
            }

            ScreensCache[internalName] = (index, screenFactory, text);
        }
        public override void RemoveScreen(string internalName)
        {
            if (_instance.TryGetTarget(out var instance) && InitialStateOption is not null)
            {
                var menuOptions = instance.MenuOptions;
                var found = menuOptions?.FirstOrDefault(i => InitialStateOption(i).Id == internalName);
                if (found is not null)
                    menuOptions?.Remove(found);
            }

            if (ScreensCache.ContainsKey(internalName))
                ScreensCache.Remove(internalName);
        }
    }
}