namespace Aeshnidae.QuestBonus;
///
/// Quest Bonus. Solved quests accumulate "quest points" on the character, which
/// convert into a small permanent XP multiplier.
///
/// Ported from Aquafir's ACE.BaseMod sample (Samples/QuestBonus), with its
/// ACE.Shared dependency removed - the only pieces it actually needed were the
/// one-line HasSolves() extension and the FakeFloat.QuestBonus property id, both
/// now in QuestBonusExtensions.
///
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);
// At server startup this is empty. On a /mod restart it resyncs everyone
// who is already logged in, so retuning Settings.json takes effect at once.
ResyncOnlinePlayers();
ModManager.Log($"[{Name}] enabled: {Settings.BonusConversion * 100:0.###}% XP per quest point" +
(Settings.MaxMultiplier > 0 ? $", capped at {Settings.MaxMultiplier:0.##}x" : ", uncapped"));
}
/// Recalculates every online character's total from their quest registry.
public static int ResyncOnlinePlayers()
{
var count = 0;
foreach (var player in PlayerManager.GetAllOnline())
{
try
{
player.ResyncQuestPoints();
count++;
}
catch (Exception ex)
{
ModManager.Log($"[{Name}] resync failed for {player?.Name}: {ex.Message}", ModManager.LogLevel.Warn);
}
}
return count;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_harmony?.UnpatchAll(HarmonyId);
_harmony = null;
ModManager.Log($"[{Name}] shut down");
}
_disposed = true;
}
}