using System; using System.Collections.Generic; using System.Linq; namespace Nox.Game { public interface IModifierResolver { int Resolve(int baseValue, IEnumerable modifiers); IEnumerable CollectModifiers(IEntityDefinition entity, StatType statType); IEnumerable CollectModifiers(IEntityDefinition entity, AttributeType attributeType); } /// /// Collects modifiers from an entity's direct modifiers and perk-granted modifiers, /// then resolves them against a base value. /// /// Resolution order: /// 1. Flat — sum of all flat values replaces the base /// 2. Addition — summed and added to the running total /// 3. Percentage — summed into a single multiplier applied to the post-addition total /// 4. Multiplication — each factor applied sequentially to the running total /// public sealed class ModifierResolver : IModifierResolver { public int Resolve(int baseValue, IEnumerable modifiers) { if(modifiers == null) { return baseValue; } var grouped = modifiers .Where(m => m != null && m.Operation != ModifierOperation.None) .GroupBy(m => m.Operation) .ToDictionary(g => g.Key, g => g.ToList()); float result = baseValue; // 1. Flat — if any flat modifiers exist, they replace the base entirely if(grouped.TryGetValue(ModifierOperation.Flat, out var flatMods)) { result = flatMods.Sum(m => m.Value); } // 2. Addition — sum all additive bonuses if(grouped.TryGetValue(ModifierOperation.Addition, out var addMods)) { result += addMods.Sum(m => m.Value); } // 3. Percentage — summed into one multiplier: result * (1 + totalPercent / 100) if(grouped.TryGetValue(ModifierOperation.Percentage, out var pctMods)) { var totalPercent = pctMods.Sum(m => m.Value); result *= 1f + (totalPercent / 100f); } // 4. Multiplication — each factor applied sequentially if(grouped.TryGetValue(ModifierOperation.Multiplication, out var mulMods)) { foreach(var mod in mulMods) { result *= mod.Value; } } return (int)Math.Round(result); } public IEnumerable CollectModifiers(IEntityDefinition entity, StatType statType) { if(entity == null) { return Enumerable.Empty(); } var direct = entity.Modifiers?.modifiers? .Where(m => m != null && m.StatType == statType) ?? Enumerable.Empty(); var fromPerks = entity.Perks?.perks? .Where(p => p?.Modifiers?.modifiers != null) .SelectMany(p => p.Modifiers.modifiers) .Where(m => m != null && m.StatType == statType) ?? Enumerable.Empty(); return direct.Concat(fromPerks); } public IEnumerable CollectModifiers(IEntityDefinition entity, AttributeType attributeType) { if(entity == null) { return Enumerable.Empty(); } var direct = entity.Modifiers?.modifiers? .Where(m => m != null && m.AttributeType == attributeType) ?? Enumerable.Empty(); var fromPerks = entity.Perks?.perks? .Where(p => p?.Modifiers?.modifiers != null) .SelectMany(p => p.Modifiers.modifiers) .Where(m => m != null && m.AttributeType == attributeType) ?? Enumerable.Empty(); return direct.Concat(fromPerks); } } }