namespace Aeshnidae.MaxLevel; /// /// Raises the character level cap beyond retail's 275 by extending the in-memory /// XP table, continuing the retail curve's own growth law rather than inventing /// a new one. See LevelTable for the maths. /// public class Mod : IHarmonyMod { public static readonly string Name = typeof(Mod).Assembly.GetName().Name!; public static readonly string HarmonyId = $"mod.{Name.ToLowerInvariant()}"; public static readonly string ModPath = Path.Combine(ModManager.ModPath, Name); public static ModContainer? Container => ModManager.GetModContainerByPath(ModPath); public static Settings Settings { get; private set; } = new(); private static Harmony? _harmony; private bool _disposed; public void Initialize() { Settings = Settings.Load(ModPath); _harmony = new Harmony(HarmonyId); // Pass the assembly explicitly. The parameterless overload finds its target // by walking the stack, and the JIT inlines a small Initialize(), so the walk // lands on ACE.Server instead of this mod and silently patches nothing. _harmony.PatchAllUncategorized(typeof(Mod).Assembly); // Cold start: the dats are not loaded yet, so the patch above does the work. // Hot reload (/mod restart) on a running server: they already are, and // DatManager.Initialize will not fire again, so apply immediately. ApplyLevelCap("mod enabled"); } /// Safe to call more than once - LevelTable.TryExtend is idempotent. public static void ApplyLevelCap(string reason) { try { var table = DatManager.PortalDat?.XpTable; if (table is null) return; // cold start; the DatManager postfix will call us again if (LevelTable.TryExtend(table, Settings, out var message)) ModManager.Log($"[{Name}] {message} ({reason})"); else ModManager.Log($"[{Name}] not applied: {message}", ModManager.LogLevel.Warn); } catch (Exception ex) { ModManager.Log($"[{Name}] failed to extend the level table: {ex}", ModManager.LogLevel.Error); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { try { // Put the shared dat object back exactly as it was. LevelTable.Revert(DatManager.PortalDat?.XpTable); } catch (Exception ex) { ModManager.Log($"[{Name}] failed to revert the level table: {ex}", ModManager.LogLevel.Error); } _harmony?.UnpatchAll(HarmonyId); _harmony = null; ModManager.Log($"[{Name}] shut down, level cap back to {LevelTable.RetailMaxLevel}"); } _disposed = true; } }