using NUnit.Framework; using UnityEngine; using YachtDice.Modifiers.Core; using YachtDice.Modifiers.Definition; using YachtDice.Modifiers.Effects; using YachtDice.Modifiers.Runtime; using YachtDice.Scoring; namespace YachtDice.Tests { public class ModifierEffectTests { private ModifierInstance CreateInstance(string id = "test") { var def = ModifierDefinitionSO.CreateForTest(id, null); return new ModifierInstance(def); } private ModifierContext CreateContext(int baseScore, int[] dice, YachtCategory category) { return new ModifierContext { BaseScore = baseScore, DiceValues = dice, Category = category, }; } // ── AddPerDieEffect ───────────────────────────────────────── [Test] public void AddPerDieEffect_CountsMatchingDice() { var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 1); var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 }, YachtCategory.Ones); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(30, ctx.FlatBonus); // 10 * 3 matching dice } [Test] public void AddPerDieEffect_ZeroTarget_CountsAllDice() { var effect = AddPerDieEffect.CreateForTest(2, targetDieValue: 0); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(10, ctx.FlatBonus); // 2 * 5 dice } [Test] public void AddPerDieEffect_NoMatches_ZeroBonus() { var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 6); var ctx = CreateContext(5, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(0, ctx.FlatBonus); } [Test] public void AddPerDieEffect_ScalesWithStacks() { var effect = AddPerDieEffect.CreateForTest(10, targetDieValue: 1); var ctx = CreateContext(5, new[] { 1, 1, 3, 4, 1 }, YachtCategory.Ones); var inst = CreateInstance(); inst.Stacks = 2; effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(60, ctx.FlatBonus); // 10 * 3 * 2 stacks } // ── AddFlatScoreEffect ────────────────────────────────────── [Test] public void AddFlatScoreEffect_AddsFlat() { var effect = AddFlatScoreEffect.CreateForTest(15); var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 }, YachtCategory.FullHouse); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(15, ctx.FlatBonus); } [Test] public void AddFlatScoreEffect_ScalesWithStacks() { var effect = AddFlatScoreEffect.CreateForTest(15); var ctx = CreateContext(25, new[] { 3, 3, 2, 2, 2 }, YachtCategory.FullHouse); var inst = CreateInstance(); inst.Stacks = 3; effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(45, ctx.FlatBonus); // 15 * 3 stacks } // ── MultiplyPerDieEffect ──────────────────────────────────── [Test] public void MultiplyPerDieEffect_MultipliesPerMatch() { var effect = MultiplyPerDieEffect.CreateForTest(2f, targetDieValue: 6); var ctx = CreateContext(18, new[] { 6, 6, 6, 1, 2 }, YachtCategory.Sixes); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(8f, ctx.Multiplier, 0.001f); // 1 * 2 * 2 * 2 = 8 } [Test] public void MultiplyPerDieEffect_NoMatches_MultiplierUnchanged() { var effect = MultiplyPerDieEffect.CreateForTest(3f, targetDieValue: 6); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(1f, ctx.Multiplier); } // ── MultiplyScoreEffect ───────────────────────────────────── [Test] public void MultiplyScoreEffect_MultipliesOnce() { var effect = MultiplyScoreEffect.CreateForTest(1.5f); var ctx = CreateContext(50, new[] { 6, 6, 6, 6, 6 }, YachtCategory.Yacht); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(1.5f, ctx.Multiplier, 0.001f); } [Test] public void MultiplyScoreEffect_ScalesWithStacks() { var effect = MultiplyScoreEffect.CreateForTest(2f); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); inst.Stacks = 3; effect.Apply(ctx, inst).GetAwaiter().GetResult(); // Pow(2, 3) = 8 Assert.AreEqual(8f, ctx.Multiplier, 0.001f); } // ── PostMultiplyEffect ────────────────────────────────────── [Test] public void PostMultiplyEffect_MultipliesPostMultiplier() { var effect = PostMultiplyEffect.CreateForTest(2f); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(2f, ctx.PostMultiplier, 0.001f); } // ── AddCurrencyEffect ─────────────────────────────────────── [Test] public void AddCurrencyEffect_AddsToCurrencyDelta() { var effect = AddCurrencyEffect.CreateForTest(25); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(25, ctx.CurrencyDelta); } [Test] public void AddCurrencyEffect_ScalesWithStacks() { var effect = AddCurrencyEffect.CreateForTest(25); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); inst.Stacks = 2; effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(50, ctx.CurrencyDelta); // 25 * 2 stacks } // ── ConsumeChargeEffect ───────────────────────────────────── [Test] public void ConsumeChargeEffect_DecrementsRemainingUses() { var effect = ConsumeChargeEffect.CreateForTest(1); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var def = ModifierDefinitionSO.CreateForTest("limited", null, hasLimitedUses: true, maxUses: 3); var inst = new ModifierInstance(def); effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(2, inst.RemainingUses); } [Test] public void ConsumeChargeEffect_IgnoresPermanent() { var effect = ConsumeChargeEffect.CreateForTest(1); var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); var inst = CreateInstance(); // permanent (no limited uses) effect.Apply(ctx, inst).GetAwaiter().GetResult(); Assert.AreEqual(-1, inst.RemainingUses); // unchanged } // ── FinalScore Integration ────────────────────────────────── [Test] public void FinalScore_CombinesBaseAndFlatAndMultiplier() { var ctx = CreateContext(10, new[] { 1, 2, 3, 4, 5 }, YachtCategory.Chance); ctx.FlatBonus = 5; ctx.Multiplier = 2f; ctx.PostMultiplier = 1.5f; // FinalScore = floor((10 + 5) * 2 * 1.5) = floor(45) = 45 Assert.AreEqual(45, ctx.FinalScore); } } }