public static DamageResult RollFTDamage(IDiceUtility roller, int numberOfDice, int drm = 0, int targetScreenRating = 0, bool dealPenetrating = false, int recursionDepth = 0) { var indent = string.Empty.PadRight(4 * recursionDepth); var penetrationCount = 0; var damageResult = new DamageResult(); var diceRolled = roller.RollStandardDice(numberOfDice); var rolls = diceRolled.ToList().OrderByDescending(v => v); /* * Console.WriteLine( * $@"{indent}[Rolled {numberOfDice}] dice * with DRM {drm}, screen {targetScreenRating}{(dealPenetrating ? ", penetrating" : ", non-penetrating")} * : {string.Join(",", rolls)}"); */ foreach (var roll in rolls) { var dieDamage = CountSuccessesOnRoll(roll, drm, targetScreenRating); damageResult.StandardRolls.Add(roll); if (dieDamage > 0) { // Console.WriteLine($"{indent}Roll of {roll} deals {dieDamage} damage"); damageResult.Standard += dieDamage; if (roll >= Constants.MinRollForDoubleSuccess && dealPenetrating) { penetrationCount++; } } // Exit early if dieDamage == 0, since dice are sorted in descending order? } if (penetrationCount > 0) { // Console.WriteLine($"{indent}Rolling {penetrationCount} penetrating dice: ["); // On a "natural" max roll, deal recursive, shield-ignoring, DRM-ignoring, penetrating followup damage var penetrationFollowup = FullThrustDieRolls.RollFTDamage(roller, penetrationCount, 0, 0, true, recursionDepth + 1); damageResult.Penetrating += penetrationFollowup.Standard; // Penetrating damage could ITSELF penetrate, but it all rolls up to just one count of penetrating damage // TODO: revisit this; what about the case of multiple layers of armor + multiple penetrating recursions? damageResult.Penetrating += penetrationFollowup.Penetrating; damageResult.PenetratingRolls.AddRange(penetrationFollowup.StandardRolls); damageResult.PenetratingRolls.AddRange(penetrationFollowup.PenetratingRolls); } return(damageResult); }
public void TestPenetratingDamageRollup_Screen1_DRM_Zero() { IDiceUtility roller = this.services.GetService <IDiceUtility>(); FullThrustDieRolls.RollFTSuccesses(roller, 1, out IEnumerable <int> rolls); var units = new List <GameUnit>(); var f = GameEngineTestUtilities.GenerateTestFormation(1, "Test Formation", ref units); var fu = f.Units.First(); var u = units[0]; var result = f.RollManeuverSpeedAndEvasion(this.services, f.Orders.First(), f.FormationId, 1, speedDRM: 0, evasionDRM: 0); // Console.WriteLine($"{u.Name} rolls {result.SpeedSuccesses} for Speed and {result.EvasionSuccesses} for Evasion."); // Console.WriteLine("Testing penetrating damage versus Screen Rating 2..."); var damageResult = FullThrustDieRolls .RollFTDamage( new DiceNotationUtility(), numberOfDice: 20, drm: 0, targetScreenRating: 1, dealPenetrating: true); // Console.WriteLine($"Dealt a total of {damageResult.Standard} standard damage and {damageResult.Penetrating} penetrating damage."); Assert.IsNotNull(damageResult); int natural6sOnInitialRoll = damageResult.StandardRolls.Count(r => r == 6); int natural5sOnInitialRoll = damageResult.StandardRolls.Count(r => r == 5); int natural4sOnInitialRoll = damageResult.StandardRolls.Count(r => r == 4); int natural6sOnPenetratingRolls = damageResult.PenetratingRolls.Count(r => r == 6); int natural5sOnPenetratingRolls = damageResult.PenetratingRolls.Count(r => r == 5); int natural4sOnPenetratingRolls = damageResult.PenetratingRolls.Count(r => r == 4); // Since ScreenRating is 1 and DRM is 0, 6s hit for 2 and penetrate; 5s hit for 1; and 4s miss. int expectedStandardDamage = (2 * natural6sOnInitialRoll) + natural5sOnInitialRoll + (0 * natural4sOnInitialRoll); // Penetrating damage ignores screens and DRM int expectedPenetratingDamage = (2 * natural6sOnPenetratingRolls) + natural5sOnPenetratingRolls + natural4sOnPenetratingRolls; Assert.AreEqual(expectedStandardDamage, damageResult.Standard); // Penetration dice all come from 6s in the original roll, OR ELSE from rerolls in the penetration pool Assert.AreEqual(damageResult.PenetratingRolls.Count - natural6sOnPenetratingRolls, natural6sOnInitialRoll); // Penetration dice are unaffected by DRM or screens Assert.AreEqual(expectedPenetratingDamage, damageResult.Penetrating); }
public void TestPenetratingDamageRollup_Screen2_DRM_Minus_One() { IDiceUtility roller = this.services.GetService <IDiceUtility>(); FullThrustDieRolls.RollFTSuccesses(roller, 1, out IEnumerable <int> rolls); var units = new List <GameUnit>(); var f = GameEngineTestUtilities.GenerateTestFormation(1, "Test Formation", ref units); var fu = f.Units.First(); var u = units[0]; var result = f.RollManeuverSpeedAndEvasion(services: this.services, formationOrders: f.Orders.First(), currentVolley: 1, formationId: f.FormationId, speedDRM: 0, evasionDRM: 0); // Console.WriteLine($"{u.Name} rolls {result.SpeedSuccesses} for Speed and {result.EvasionSuccesses} for Evasion."); // Console.WriteLine("Testing penetrating damage versus Screen Rating 2..."); var damageResult = FullThrustDieRolls .RollFTDamage( new DiceNotationUtility(), numberOfDice: 20, drm: -1, targetScreenRating: 2, dealPenetrating: true); // Console.WriteLine($"Dealt a total of {damageResult.Standard} standard damage and {damageResult.Penetrating} penetrating damage."); Assert.IsNotNull(damageResult); // Since ScreenRating is 2 and DRM is -1, only 6's hit (and they all penetrate). int natural6sOnInitialRoll = damageResult.StandardRolls.Count(r => r == 6); int natural6sOnPenetratingRolls = damageResult.PenetratingRolls.Count(r => r == 6); int natural4sAnd5sOnPenetratingRolls = damageResult.PenetratingRolls.Count(r => r == 4 || r == 5); Assert.AreEqual(natural6sOnInitialRoll, damageResult.Standard); Assert.IsTrue(damageResult.PenetratingRolls.Count >= natural6sOnInitialRoll); // Penetration dice all come from 6s in the original roll, OR ELSE from rerolls in the penetration pool Assert.AreEqual(damageResult.PenetratingRolls.Count - natural6sOnPenetratingRolls, natural6sOnInitialRoll); // Penetration dice are unaffected by DRM or screens var penetrationDamageCalc = (2 * natural6sOnPenetratingRolls) + natural4sAnd5sOnPenetratingRolls; Assert.AreEqual(penetrationDamageCalc, damageResult.Penetrating); }
public static int RollFTSuccesses(IDiceUtility roller, int numberOfDice, out IEnumerable <int> rolls, int drm = 0, int difficultyLevel = 0) { if (numberOfDice == 0) { rolls = new List <int>(); return(0); // No successes possible if no dice rolled! } int successes = 0; string expression = $"{numberOfDice}D6"; rolls = roller.RollStandardDice(numberOfDice).OrderByDescending(i => i); foreach (int roll in rolls) { successes += CountSuccessesOnRoll(roll, drm, difficultyLevel); } return(successes); }