namespace Aeshnidae.ResonanceAuras; /// /// One aura's tunables. The key is fixed in code (it is what the database row and /// the formula patch key on); everything a player sees or pays is here. /// public class AuraSetting { /// Which aura this row configures - see . Not a name; do not edit. public string Key { get; set; } = ""; /// The name players see and type. Any unambiguous prefix matches. public string Name { get; set; } = ""; /// Highest rank a character can buy. 0 disables the aura without losing anyone's ranks. public int MaxRanks { get; set; } /// /// Resonance for the first rank of this aura, or 0 to use . /// Rank n costs n times this. /// public long RankPriceBase { get; set; } } public class Settings { public const string FileName = "Settings.json"; public bool Enabled { get; set; } = true; /// /// Resonance for rank 1 of any aura; rank n costs n times this, so 100, 200, 300... /// Linear on purpose: the point of an aura is small and steady, not a mastery-style /// wall. With Resonance at 10 a quest, 100 is ten quests - a week-old character can /// buy a first rank, and the whole table (see the Readme) is a long-term goal. /// public long RankPriceBase { get; set; } = 100; /// /// Thrifty Caster: chance, per rank, that a spell component which would have burned /// survives. 0.10 x 5 ranks = half of them. Applied per component, after ACE has /// already decided which ones burn. /// public double ComponentSaveChancePerRank { get; set; } = 0.10; /// /// Steady Quiver: chance, per rank, that an arrow, bolt or dart is not spent when /// fired. 0.10 x 5 ranks = half of them. The last one in the stack always goes, /// so "out of ammunition" stays true when it is said. /// public double AmmoSaveChancePerRank { get; set; } = 0.10; /// Play the augmentation burst and chime on a purchase. public bool Effects { get; set; } = true; /// /// The auras, in the order /aura lists them. Names and maximums are yours to /// change; keys are not. A key missing from this list is treated as MaxRanks 0. /// Each rank adds one to a number ACE already uses in a formula - what that number /// does is fixed in code and described in the Readme. /// public List Auras { get; set; } = new() { new() { Key = "spellduration", Name = "Lasting Enchantments", MaxRanks = 3 }, new() { Key = "itemmana", Name = "Frugal Mana", MaxRanks = 5 }, new() { Key = "imbue", Name = "Charmed Hands", MaxRanks = 3 }, new() { Key = "manastone", Name = "Generous Stones", MaxRanks = 5 }, new() { Key = "salvage", Name = "Keen Salvager", MaxRanks = 4 }, new() { Key = "carry", Name = "Strong Back", MaxRanks = 5 }, new() { Key = "regen", Name = "Deep Rest", MaxRanks = 2 }, new() { Key = "components", Name = "Thrifty Caster", MaxRanks = 5 }, new() { Key = "ammo", Name = "Steady Quiver", MaxRanks = 5 }, }; private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true, }; public static Settings Load(string modPath) { var path = Path.Combine(modPath, FileName); try { if (File.Exists(path)) return JsonSerializer.Deserialize(File.ReadAllText(path), JsonOptions) ?? new Settings(); var defaults = new Settings(); defaults.Save(modPath); ModManager.Log($"[{Mod.Name}] wrote default settings to {path}"); return defaults; } catch (Exception ex) { ModManager.Log($"[{Mod.Name}] could not read {path}, using defaults: {ex.Message}", ModManager.LogLevel.Warn); return new Settings(); } } public void Save(string modPath) { try { File.WriteAllText(Path.Combine(modPath, FileName), JsonSerializer.Serialize(this, JsonOptions)); } catch (Exception ex) { ModManager.Log($"[{Mod.Name}] could not save settings: {ex.Message}", ModManager.LogLevel.Error); } } }