forked from Shardstone/trail-into-darkness
99 lines
3.9 KiB
C#
99 lines
3.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Nox.Game {
|
|
|
|
public interface IModifierResolver {
|
|
int Resolve(int baseValue, IEnumerable<IModifier> modifiers);
|
|
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType);
|
|
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, AttributeType attributeType);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
public sealed class ModifierResolver : IModifierResolver {
|
|
public int Resolve(int baseValue, IEnumerable<IModifier> 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<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType) {
|
|
if(entity == null) {
|
|
return Enumerable.Empty<IModifier>();
|
|
}
|
|
|
|
var direct = entity.Modifiers?.modifiers?
|
|
.Where(m => m != null && m.StatType == statType)
|
|
?? Enumerable.Empty<IModifier>();
|
|
|
|
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<IModifier>();
|
|
|
|
return direct.Concat(fromPerks);
|
|
}
|
|
|
|
public IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, AttributeType attributeType) {
|
|
if(entity == null) {
|
|
return Enumerable.Empty<IModifier>();
|
|
}
|
|
|
|
var direct = entity.Modifiers?.modifiers?
|
|
.Where(m => m != null && m.AttributeType == attributeType)
|
|
?? Enumerable.Empty<IModifier>();
|
|
|
|
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<IModifier>();
|
|
|
|
return direct.Concat(fromPerks);
|
|
}
|
|
}
|
|
}
|