Jampi0n_Skyrim-PassiveEnchantingXP/PassiveEnchantingXP/Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Mutagen.Bethesda;
using Mutagen.Bethesda.Synthesis;
using Mutagen.Bethesda.Skyrim;
using System.Threading.Tasks;
using Mutagen.Bethesda.FormKeys.SkyrimSE;
using Mutagen.Bethesda.Plugins;
using Mutagen.Bethesda.Synthesis.Settings;

namespace PassiveEnchantingXP {

    public class Settings {

        [SynthesisTooltip("Maximum possible amount of experience the player can get per minute. To reach the maximum value, the player needs to wear many strong custom enchanted items. In vanilla, the player gets around 1 experience for every enchanted item made.")]
        public float maxXpPerMinute = 0.2f;
        [SynthesisTooltip("How frequently xp is awarded. A smaller update time detects the time spent in combat more accurately, but also requires more script executions.")]
        public float updateTimer = 10.0f;
        [SynthesisTooltip("If a single battle exceeds this duration, no further experience will be awarded until the player exits and re-enters combat.")]
        public float maxBattleDuration = 60.0f;
    }
    public class Program {
        const float MAGNITUDE = 1000.0f;

        public static Lazy<Settings> _settings = null!;
        public static Settings settings => _settings.Value;
        public static async Task<int> Main(string[] args) {
            return await SynthesisPipeline.Instance
                .AddPatch<ISkyrimMod, ISkyrimModGetter>(RunPatch)
                .SetAutogeneratedSettings(
                    nickname: "Settings",
                    path: "settings.json",
                    out _settings)
                .SetTypicalOpen(GameRelease.SkyrimSE, "PassiveEnchantingXP.esp")
                .Run(args);
        }

        public static Quest createPlayerAlias(ISkyrimMod mod, string name) {
            var q = mod.Quests.AddNew();
            q.EditorID = name;
            q.Name = name;

            q.Aliases.Add(new QuestAlias {
                ID = 0,
                Name = name + "PlayerAlias"
            });
            q.Aliases.First().ForcedReference.SetTo(Constants.Player.FormKey);
            return q;
        }

        public static void RunPatch(IPatcherState<ISkyrimMod, ISkyrimModGetter> state) {
            var enchEffectGetter = state.LinkCache.Resolve<IMagicEffectGetter>(FormKey.Factory("000802:PassiveEnchantingXP.esp"));
            var maxXpPerMinuteGetter = state.LinkCache.Resolve<IGlobalGetter>(FormKey.Factory("000805:PassiveEnchantingXP.esp"));
            var maxPerBattleGetter = state.LinkCache.Resolve<IGlobalGetter>(FormKey.Factory("000806:PassiveEnchantingXP.esp"));
            var updateTimerGetter = state.LinkCache.Resolve<IGlobalGetter>(FormKey.Factory("000807:PassiveEnchantingXP.esp"));

            var maxXpPerMinute = state.PatchMod.Globals.GetOrAddAsOverride(maxXpPerMinuteGetter);
            ((GlobalFloat)maxXpPerMinute).Data = settings.maxXpPerMinute;
            var maxPerBattle = state.PatchMod.Globals.GetOrAddAsOverride(maxPerBattleGetter);
            ((GlobalInt)maxPerBattle).Data = (int)(settings.maxBattleDuration / settings.updateTimer);
            var updateTimer = state.PatchMod.Globals.GetOrAddAsOverride(updateTimerGetter);
            ((GlobalFloat)updateTimer).Data = settings.updateTimer;

            var baseEnchantments = new HashSet<IObjectEffectGetter>();
            foreach(var armorGetter in state.LoadOrder.PriorityOrder.Armor().WinningOverrides()) {
                if(armorGetter.ObjectEffect.TryResolve(state.LinkCache, out var enchGetter)) {
                    if(enchGetter != null) {
                        var ench = (enchGetter as IObjectEffectGetter);
                        if(ench != null && (armorGetter.Keywords == null || !armorGetter.Keywords.Contains(Skyrim.Keyword.MagicDisallowEnchanting))) {
                            if(ench.BaseEnchantment.TryResolve(state.LinkCache, out var baseEnch)) {
                                if(baseEnch != null) {
                                    if(baseEnch.CastType == CastType.ConstantEffect && baseEnch.TargetType == TargetType.Self) {
                                        if(baseEnchantments.Add(baseEnch)) {
                                            Console.WriteLine("Patching base enchantment: " + baseEnch.EditorID);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            foreach(var baseEnchGetter in baseEnchantments) {
                var baseEnch = state.PatchMod.ObjectEffects.GetOrAddAsOverride(baseEnchGetter);
                var alreadyHasEffect = false;
                foreach(var e in baseEnch.Effects) {
                    if(e.BaseEffect.FormKey == enchEffectGetter.FormKey) {
                        alreadyHasEffect = true;
                        break;
                    }
                }
                if(!alreadyHasEffect) {
                    var effect = new Effect {
                        Data = new EffectData {
                            Magnitude = MAGNITUDE,
                            Duration = 0,
                            Area = 0
                        }
                    };
                    effect.BaseEffect.SetTo(enchEffectGetter);
                    baseEnch.Effects.Add(effect);
                }
            }
        }
    }
}
