private static ValueConverter ApplyToDamageValueConverter(IEnumerable <IStat> applyStats) { var values = applyStats .Select(s => new FunctionalValue(c => c.GetValue(s) / 100, $"{s}.Value / 100")) .ToList(); var multiplier = new FunctionalValue( c => values.Select(v => v.Calculate(c)).AggregateOnValues(Combine), $"RequireEqualWhereNotNull({string.Join(",", values)})"); return(v => v.Multiply(new ValueBuilderImpl(multiplier))); // There isn't any obvious way to combine different values but it currently can't happen: // - More than one source damage source for ApplyModifiersToSkillDamage can't happen because // With(DamageSource) only takes one argument. // - Different ApplyModifiersToAilmentDamage values for different source damage sources don't make sense. // If e.g. crit multi for spells and attacks would be applied to ailments at different values, it would // be ambiguous how to apply generic crit multi, which is split into the damage sources, to ailments. // - The current solution would not work if different stats of the original StatBuilderResult (built from // the core builder) have different ApplyModifiersTo values. That is possible with IDamageStatBuilder and // its damage types, but damage-type specific ApplyModifiersTo modifiers do not exist. NodeValue Combine(NodeValue left, NodeValue right) { if (left == right) { return(left); } throw new ParseException( $"ApplyModifiersToDamage values must be equal for all concretized stats. {left} and {right} given"); } }
public void CalculateReturnsCorrectResultWithMainPath(Form form) { NodeValue?expected = new NodeValue((int)form); var fireDamage = AilmentDamage(DamageType.Fire); var coldDamage = AilmentDamage(DamageType.Cold); var transformedValue = new FunctionalValue(c => c.GetValues(form, coldDamage).Sum(), ""); var paths = new[]
public void CalculateChecksForSameSta() { var transformedValue = new FunctionalValue(c => c.GetValues(Form.BaseSet, Paths).First(), ""); var sut = CreateSut(new Stat("a"), Form.BaseSet, transformedValue); var context = MockContext(2, 4); var actual = sut.Calculate(context); Assert.AreEqual(2, actual.Single()); }
public void CalculateOnlyManipulatesContextForRegenStat() { var expected = new NodeValue(42); var unrelatedStat = new Stat("a"); var paths = new[] { new PathDefinition(new ModifierSource.Local.Skill()), }; var transformedValue = new FunctionalValue(c => c.GetValues(unrelatedStat, NodeType.PathTotal).Sum(), ""); var context = Mock.Of <IValueCalculationContext>(c => c.GetPaths(unrelatedStat) == paths && c.GetValue(unrelatedStat, NodeType.PathTotal, paths[0]) == expected); var sut = CreateSut(transformedValue); var actual = sut.Calculate(context); Assert.AreEqual(expected, actual); }
public void CalculateOnlyManipulatesContextForPathTotal() { var expected = new NodeValue(42); var transformedValue = new FunctionalValue(c => c.GetValues(Regen(Pool.Life), NodeType.Base).Sum(), ""); var context = Mock.Of <IValueCalculationContext>(c => c.GetPaths(Regen(Pool.Life)) == new[] { Path } && c.GetValue(Regen(Pool.Life), NodeType.Base, Path) == expected && c.GetValue(TargetPool(Pool.Life), NodeType.Total, Path) == TargetValueFor(Pool.Life) && c.GetValue(TargetPool(Pool.Mana), NodeType.Total, Path) == TargetValueFor(Pool.Mana) && c.GetValue(TargetPool(Pool.EnergyShield), NodeType.Total, Path) == TargetValueFor(Pool.EnergyShield)); var sut = CreateSut(transformedValue); var actual = sut.Calculate(context); Assert.AreEqual(expected, actual); }
public void CalculateReturnsOriginalResultIfValueUsesDifferentStats() { var expected = (NodeValue?)43; var ailmentDamage = new Stat("Damage.Spell.Ignite"); var skillDamage = ConcretizeDamage(new SkillDamageSpecification(DamageSource.Spell)); var unrelated = new Stat("unrelated"); var paths = new[] { PathDefinition.MainPath, new PathDefinition(new ModifierSource.Local.Given()) }; var context = Mock.Of <IValueCalculationContext>(c => c.GetPaths(unrelated) == paths.Take(1).ToList() && c.GetPaths(skillDamage) == paths.Skip(1).ToList() && c.GetValue(unrelated, NodeType.PathTotal, paths[0]) == expected && c.GetValue(unrelated, NodeType.PathTotal, paths[1]) == new NodeValue(10)); var transformedValue = new FunctionalValue(c => c.GetValues(unrelated, NodeType.PathTotal).Sum(), ""); var sut = new AilmentDamageUncappedSubtotalValue(ailmentDamage, skillDamage, transformedValue); var actual = sut.Calculate(context); Assert.AreEqual(expected, actual); }
public void CalculateReturnsCorrectResult(params double?[] values) { var expected = (NodeValue?)values.Max(); var transformedStat = new Stat("transformed"); var paths = values .Select((_, i) => new PathDefinition(new ModifierSource.Local.Gem(new Gem("", 1, 1, ItemSlot.Helm, i, 0, true)))) .ToList(); var contextMock = new Mock <IValueCalculationContext>(); contextMock.Setup(c => c.GetPaths(transformedStat)).Returns(paths); for (var i = 0; i < paths.Count; i++) { var path = paths[i]; contextMock.Setup(c => c.GetValue(transformedStat, NodeType.PathTotal, path)) .Returns((NodeValue?)values[i]); } var transformedValue = new FunctionalValue(c => c.GetValues(transformedStat, NodeType.PathTotal).Sum(), ""); var sut = new RequirementUncappedSubtotalValue(transformedStat, transformedValue); var actual = sut.Calculate(contextMock.Object); Assert.AreEqual(expected, actual); }
private static AilmentDamageBaseValue CreateSut(IStat ailmentDamage, IStat skillDamage) { var transformedValue = new FunctionalValue(c => c.GetValue(ailmentDamage, NodeType.BaseSet), ""); return(new AilmentDamageBaseValue(skillDamage, transformedValue)); }
private static AilmentDamageUncappedSubtotalValue CreateSut(IStat ailmentDamage, IStat skillDamage) { var transformedValue = new FunctionalValue(c => c.GetValues(ailmentDamage, NodeType.PathTotal).Sum(), ""); return(new AilmentDamageUncappedSubtotalValue(ailmentDamage, skillDamage, transformedValue)); }