namespace Cthangover.Mods.Stats { using System; using System.Collections.Generic; using System.Linq; using Cthangover.Core.Utils; /// /// The registered s. Filled by /// per mod assembly, so a source leaves with its mod on reload; a throwing source is /// logged and skipped, it never breaks the sheet of everyone else. /// public static class TraitSources { private static readonly List Sources = new(); /// The registered sources, in registration order. public static IReadOnlyList All => Sources; /// /// Adds a source (ignored when already present). Only the cached sheets are dropped: /// sources arrive while mods register, before any game state exists, so this must /// not reach the roster (the next scene entry syncs it). /// public static void Add(ITraitSource source) { if (source != null && !Sources.Contains(source)) Sources.Add(source); StatsService.ClearCache(); } /// Removes a source (cached sheets are dropped, see ). public static void Remove(ITraitSource source) { if (Sources.Remove(source)) StatsService.ClearCache(); } /// Every trait all sources give . public static List Collect(string characterId) { var result = new List(); foreach (var source in Sources.ToList()) { try { var traits = source.TraitsOf(characterId); if (traits != null) result.AddRange(traits.Where(t => t != null)); } catch (Exception ex) { GameLogger.Log("STATS", $"trait source '{source.Id}' failed for '{characterId}': {ex.Message}", LogLevel.Error); } } return result; } } }