private static SeveralDice ParseBuiltinDice(string nodeRepresentation) { Contract.Requires(!String.IsNullOrEmpty(nodeRepresentation)); SeveralDice result = null; double fixedValue; // Try to match the simplest fixed die first: if (Double.TryParse(nodeRepresentation, out fixedValue)) { result = new SeveralDice(new FixedDie(fixedValue)); } else // Then try to match other known die types: { foreach (var nodePattern in knownNodePatterns) { var dieMatches = Regex.Matches(nodeRepresentation, nodePattern, RegexOptions.Compiled); result = MatchToBuiltinDiceNode(dieMatches); if (result != null) { break; } // Stop trying on successful match. } } return(result); }
/// <summary> Appends a new chain node to the dice chain using a specified operation.</summary> /// <remarks> Append(DiceOperation.Minus, Dice.D10) is equivalent to Minus(Dice.D10). </remarks> /// <param name="linkOperation">The operation used to operate on dice rolls between chain nodes.</param> /// <param name="linkContent">Actual content of the chain node.</param> /// <returns>Dice chain with new node appended.</returns> public DiceChain Append(DiceOperation linkOperation, SeveralDice linkContent) { Contract.Requires(linkOperation != DiceOperation.None); Contract.Requires(linkContent != null); LastLink = new DiceChainLink(linkOperation, linkContent, LastLink); return(this); }
public void BurningHands() { // Let's create a smart sorcerer first: var sorcerer = new Character { Int = 16, BurningHandsSpellLevel = 1 }; // Then let's create a provider for a modifier derived from the sorcerer's Intelligence: var intMod = new IntModProvider { Character = sorcerer }; // Create a provider for a spell damage dice count, which depends on the sorcerer's current spell level. var spellDiceCount = new BurningHandsDiceCountProvider { Character = sorcerer }; // Merge these into a chain: DiceChain damageDice = Dice.Take(spellDiceCount).D(8).EachPlus(intMod); // Let's burn some poor fella: double damage = damageDice.Roll(); // Let's say we want to display the spell damage, so check the dice chain stats: Trace.WriteLine("Damage: " + damageDice.Minimum + " to " + damageDice.Maximum); // Outputs 'Damage: 4 to 11', which is equal to '1d8+3', looks good. // What if sorcerer gains two levels of proficiency in the spell? sorcerer.BurningHandsSpellLevel += 2; Console.WriteLine("Damage: " + damageDice.Minimum + " to " + damageDice.Maximum); // Outputs 'Damage: 8 to 22', which is equal to '2d8(+3)', once again looks good. // Parens mean that 3 is apllied to each die separately instead of affecting dice sum. // Now let's say when you miss an enemy with burning hands, you still can deal some damage. // In this case damage dealt to the target equals to sum of each damage die that rolled its maximum result. // Normally you don't want to deal with individual roll values when using dice chain, // but there is a method just in case: List<DiceChainRollStep> rollParts; // Normal hit damage is returned by dice chain as usual: damage = damageDice.RollStepByStep(out rollParts); // Damage on a miss is calculated as a sum of each damage die that rolled its maximum result. // Looking at dice chain 'Xd8+Y' we can see that damage dice are rolled in the first (leftmost) step. double missedDamage = rollParts[0].Rolls.Where(roll => roll == Dice.D8.Maximum).Sum(); // For the sake of completeness, there is a way of achieving the same thing without a dice chain. // That way we have to do all calculations manually. // Let's bundle D8 with spell damage dice count: var rawSpellDamage = new SeveralDice(Dice.D8, spellDiceCount); // Make a damage roll, rolling each die separately: var rawRolls = rawSpellDamage.RollSeparately().ToArray(); // .ToArray() is important here. Otherwise different roll values would be generated on each enumeration. // Damage on a miss: missedDamage = rawRolls.Where(roll => roll == Dice.D8.Maximum).Sum(); // Damage on a hit: damage = rawRolls.Select(roll => roll + intMod.Roll()).Sum(); }
/// <summary> Converts string representation of a single node in a dice chain to an actual node. </summary> private static SeveralDice ToNode(string nodeRepresentation) { SeveralDice result = null; foreach (var parser in nodeParsers) // Parsers should not throw, but let's enforce the rule. { try { result = parser(nodeRepresentation); } catch { result = null; } if (result != null) { break; } } return(result); }
/// <summary> Initializes a new instance of the <see cref="DiceChain"/> class. </summary> /// <param name="node">The first node for the dice chain.</param> public DiceChain(SeveralDice node) { Contract.Requires(node != null); LastLink = new DiceChainLink(DiceOperation.Plus, node); }
public void EntryLevel() { // Roll a standard cubic die one time: var d6 = new Die(6); var roll = d6.Roll(); // Roll an interval die from 10 to 20 four times: var interval10t20 = new Die(10, 20); roll = interval10t20.Roll(4).Sum(); // There is also a fixed die, which always returns the same value. // It's useful when something expects IDie as value provider and you want to pass a constant value: var fixed50 = new FixedDie(50); // Roll two d10's: var d2d10 =new SeveralDice(Dice.D10, 2); roll = d2d10.Roll(); // Compared to simple Dice.D10.Roll(2), SeveralDice provides statistics and serialization Console.WriteLine("Possible roll value range: " + d2d10.Minimum + " to " + d2d10.Maximum); // Roll a d4d10 / d2 * 3: var d4d10divD2mul3 = Dice.Take(Dice.D4).D(10).Divide(Dice.D2).Multiply(3); // This is a dice chain. // Math operations in dice chains are always executed from left to right, so this chain translates to: // take a 1-4 of d10 dice and divide their roll value by 2 with 50 % chance, then multiply resulting roll value by 3. roll = d4d10divD2mul3.Roll(); // Dice chains also provide statistics and serialization. // This chain was created via fluent interface, but it is also possible to create such chain in a more explicit way: d4d10divD2mul3 = new DiceChain( new SeveralDice(new Die(10), new Die(4))) // d4d10 .Append(DiceOperation.Divide, new SeveralDice(2)) // / 2 .Append(DiceOperation.Multiply, new SeveralDice(3)); // * 3 // Oh, and where is a random number generator? Built-in dice get it from thread-local `Dice.Random`. }
/// <summary> Appends a new chain node to the dice chain using a specified operation.</summary> /// <remarks> Append(DiceOperation.Minus, Dice.D10) is equivalent to Minus(Dice.D10). </remarks> /// <param name="linkOperation">The operation used to operate on dice rolls between chain nodes.</param> /// <param name="linkContent">Actual content of the chain node.</param> /// <returns>Dice chain with new node appended.</returns> public DiceChain Append(DiceOperation linkOperation, SeveralDice linkContent) { Contract.Requires(linkOperation != DiceOperation.None); Contract.Requires(linkContent != null); LastLink = new DiceChainLink(linkOperation, linkContent, LastLink); return this; }