public void DiceChainShouldBeValidlySerialized() { deserializedD2d20mr3t6p40 = (DiceChain)diceChainSerializer.ReadObject(serializedD2d20mr3t6p40); serializedD2d20mr3t6p40.Close(); Assert.AreEqual(d2d20mr3t6p40.Maximum, deserializedD2d20mr3t6p40.Maximum, "Serialized die maximum is wrong"); Assert.AreEqual(d2d20mr3t6p40.Minimum, deserializedD2d20mr3t6p40.Minimum, "Serialized die minimum is wrong"); Assert.AreEqual(d2d20mr3t6p40.ToString(), deserializedD2d20mr3t6p40.ToString(), "Serialized die repr is wrong"); }
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> Initializes a new instance of the <see cref="DiceChainSetup" /> struct. </summary> /// <param name="amountProvider">The amount of dice added to the created <see cref="DiceChain" />.</param> /// <param name="previousOperation">The operation to use with previous <see cref="DiceChain"/>.</param> /// <param name="previousDice">The previous <see cref="DiceChain"/> to carry on to next one.</param> public DiceChainSetup(IDie amountProvider, DiceOperation previousOperation, DiceChain previousDice) { Contract.Requires(previousOperation != DiceOperation.None); Contract.Requires(amountProvider != null); Amount = amountProvider; PreviousOperation = previousOperation; PreviousDice = previousDice; }
private DiceChain AppendDie(IDie die) { Contract.Requires(die != null); var result = PreviousDice; if (result != null) { if (PreviousOperation == DiceOperation.None) { throw new InvalidOperationException("Can't append dice with unknown operation."); } result.Append(PreviousOperation, new SeveralDice(die, Amount)); } else { result = new DiceChain(new SeveralDice(die, Amount)); } return(result); }
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`. }
public void CreatedD10D100Em50P10() { d10d100em50p10 = Dice.Take(Dice.D10).D(100).EachMinus(50).Plus(10); }
public void CreatedD2D20() { d2d20mr3t6p40 = Dice.Take(Dice.D2).D(20).MultiplyTake(Dice.D4).I(3, 6).Plus(40); }
public void CreatedDiceChainD2D20Mr3T6P40() { d2d20mr3t6p40 = Dice.Take(Dice.D2).D(20).MultiplyTake(1).I(3, 6).Plus(40); }
public void D3D8WithPlus3ToEach() { c3d8ep3 = Dice.Take(3).D(8).EachPlus(3); }
public void D4D6MinusInterval2to5() { cd4d6mr2t5 = Dice.Take(Dice.D4).D(6).Minus(new Die(2, 5)); }
public void DieWith6FacesShowingADifferentIntegralNumberFrom1To6() { var c2d6 = new DiceChain(new SeveralDice(new Die(6), 2)); c2d6p3 = c2d6.Append(DiceOperation.Plus, new SeveralDice(3)); }
public void D2D10DivideByD2MultiplyBy2() { d2d10div2mul2 = Dice.Take(Dice.D2).D(10).Divide(Dice.D2).Multiply(2); }
private DiceChain AppendDie(IDie die) { Contract.Requires(die != null); var result = PreviousDice; if (result != null) { if (PreviousOperation == DiceOperation.None) { throw new InvalidOperationException("Can't append dice with unknown operation."); } result.Append(PreviousOperation, new SeveralDice(die, Amount)); } else { result = new DiceChain(new SeveralDice(die, Amount)); } return result; }