Character Generator / Virtual Tabletop Assistant

Hello, I am in the process of going through the Udemy course “Learn to Code by Making Games - Complete C# Unity” I am at the tail end of the Block Breaker section.

Full disclosure, one could describe my level of competence as “novice” only if one were being very generous.

I am in the midst of fiddling around using Unity to make a character generator and eventually Virtual Tabletop (VTT) assistant of sorts to aid in online RPG play. One of the key things I will need to do over and over again is roll dice and get the results.

I have the basics of a script for rolling [numberofdice]d[sizeofdie]. In the particular game I will be playing there is are also types of rolls “boons” and “banes” with boons you roll a number of d6 and take the highest one and add that to the 1d20 roll. Banes work the same but you subtract the highest die from the d20 roll.

I figured rather than rewriting essentially the same code for the boons and banes rolls I could point the boon and bane methods to the RollThem Method and use that putting the results of the rolls into an array, sorting them, then pulling the highest number only.

Ultimately, I may scrap this entire approach as I (hopefully) learn more and understand more but I am thus far pretty confused by OOP in general and can only infrequently understand what error messages are trying to convey.

Hans

Hi @Hansmo;

You could of course break this method down a bit further, for example, the commonality between what you have described is the rolling of a D6 and the rolling of D20. For all intents and purposes, that’s two calls to the same “get me a random number” method where you provide the min/max values. You then calculate slightly different based on whether your rolling as “boons” or “banes”. I don’t know whether you may have any other factors to consider such as modifiers to the dice rolls for any other part of the game, but you could break it down a bit like this;

using UnityEngine;

public class DiceRoller
{
    // handles multiple dice and adding 1D20
    public int RollBoon(int numberOfDice)
    {
        int result = 0;

       result = GetHighestValueFromMultipleD6Rolls(numberOfDice) + RollD20();       

        return result;
    }

    // handles multiple dice and deducting 1D20
    public int RollBane(int numberOfDice)
    {
        int result = 0;

        result = GetHighestValueFromMultipleD6Rolls(numberOfDice) - RollD20(); 

        // are negative values are allowed?  If not, handle that here

        return result;        
    }
    
    // returns the highest scoring di from a number of D6s
    private int GetHighestValueFromMultipleD6Rolls(int numberOfDice)
    {
        int highestResult = 0;
        int result = 0;

        for (int i=0; highestResult < 6 && i < numberOfDice; i++)    // multi-condition which will save rolling if you already have a 6
        {
            result = RollD6();

            if(result > highestResult)
            {
                highestResult = result;
            }
        }

        return highestResult;
    }

    // returns 1D6
    public int RollD6()
    {
        return GetRandomValue(1, 6);
    }

	// example of extended D6 roll to support modifiers
	// public int RollD6(int modifier)
	// {
	//     int result = 0;
	//
	//     result = GetRandomValue(1, 6);
	//     result += modifier;
	//
	//     return result;
	// }

        // returns 1D20
	public int RollD20()
	{
		return GetRandomValue(1, 20);
	}

	// example of extended D20 roll to support modifiers
	// public int RollD20(int modifier)
	// {
	//     int result = 0;
	//
	//     result = GetRandomValue(1, 20);
	//     result += modifier;
	//
	//      return result;
	// }

    // returns a random value int between min and max
    private int GetRandomValue(int min, int max)
    {
        return Random.Range(min, max+1)    // note: +1 as using ints maximum is exclusive not inclusive
    }
}

Again, if it were me, I’d probably them look at the above and break out what is actual dice behaviour into a separate class, and have what’s left in a class which handles these types (boon / bane etc)

1 Like

Without being at my PC to mess with what you presented I can see what you
are doing for the most part. The GetHighestValueFromMultipleD6Rolls(int
numberOfDice) is basically making a check on each roll to see if it has hit
the maximum result and then exiting the loop because it would be pointless
to keep rolling. That makes a lot of sense from a "not wasting effort"
perspective. The caveat is that RPG players generally like to see the
results of their dice rolls…they want to know just how well or how poorly
they rolled. So that was the idea behind saving the results of all the
boons/banes but only choosing the highest, hence the attempt on my part to
add it to an array and then sort to get only the highest. It’s certainly
something for me to ponder.

Aha! And there was me thinking I was being performance friendly… hehe…

I didn’t realise all of the values of the dice were required, but I can see exactly what you mean now.

I was about to update the example I gave taking that into account but it strikes me that there may be some other bits I do not understand, so I thought I’d ask :slight_smile:

Does a player get to determine whether they are rolling boons or banes? Or is this based on something else? For example, if I threw 5 dice, can I say “I am going to roll 5 boons”? I’m guessing not because having the additional 1D20 sounds like a positive outcome which I shouldn’t be able to choose.

The reason I ask is that I could update the above example so that you are returned an int array when you call RollBoon() for example. This would give you all of the numbers that were rolled but it may make sense to add the 1D20 to the highest number at that point and return it in the array, however, in doing so, you wouldn’t see what the actual rolled number of that dice was from the array.

The same would be true of the banes.

So, do you want to just have a sorted array returned without adding or deducting the 1D20, then, when you have that, you make a call to RollD20() to get the outcome and add it on separately?

If a boon and a bane were actual “things” you could potentially create a mini class for them, that could then have properties which return the array of all dice rolls and the highest result with a 1D20 added to it.

It’s a little hard for me to suggest which would be the better way to go as I don’t fully understand the rules of the game in question. :slight_smile:

In point of fact the values of the extra dice are not so much required as
desired (at least by me).

Ok this is probably far more geek knowledge flood than you require or even
asked for but the particular game in question is Shadow of the Demon Lord a
dark fantasy tabletop RPG by Robert J. Schwalb.

The basic mechanic of an attack or challenge roll is you roll 1d20. and
that roll needs to meet or exceed a challenge number which might be a fixed
number 10 for an unopposed challenge (Perception check to search a room) or
variable (the characters Defense for an attack roll). To this roll you add
Attribute (Strength, Agility, Intellect, Will) modifiers. The modifier is
the Attribute score - 10 (Strength 12 would be +2, Agility 9 would -1).
Then further modified by boons and banes for situational concerns as
determined by the rules and/or Gamemaster (you are blinded at the moment
roll 3 banes; you have a magic sword roll 1 boon). Banes and Boons cancel
each other out 1 for 1 and before the rolls are made so you never roll both
boons and banes. (In the example of 3 banes for blindness and 1 boon for
magic sword you would roll 2 banes; 1 boon for weapon cancels 1 bane for
blindness). Once the boons or banes are rolled you pick the highest and
add or subract that to the d20 roll. Compare it to the challenge number

if the roll total is <=0 you fail the action attempted and really bad
things might happen (Critical failure)
if the roll total is >0 and < challenge number you fail the action
attempted (Failure)
if the roll total is >=20 and >= (challenge number+5) you succeed in the
attempted action & something extra good might happen (Critical Success)
if the roll total is >=challenge number but less than 20 or less than
(challenge number+5) then you simply succeed in the action attempted.
(Success)

Boons and banes are for the most part determined on the spot at the time
the roll is called for. But the modifier for an attribute can be pulled
from a variable once things are up and running.

Hopefully that clarifies my intended use a little.

Hi,

Thanks for the explanation, it is really useful and also very interesting.

As I mentioned earlier, I think I would probably separate out the main standard dice rolling aspect and have that in a separate class, that would be very clean and provide the basics of getting results for one or more die of differing types.


Dice.cs

using System;

public class Dice
{
    /// <summary>
    /// The minimum value of any DieType
    /// </summary>
    private const int MinimumDieValue = 1;


    /// <summary>
    /// Instance of Random to save on performance
    /// </summary>
    private static Random _random = null;


    /// <summary>
    /// Default constructor
    /// </summary>
    static Dice()
    {
        if (_random == null)
        {
            _random = new Random();
        }
    }


    /// <summary>
    /// Die types, values represent the number of faces and maximum value
    /// </summary>
    public enum DieType { D4 = 4, D6 = 6, D8 = 8, D10 = 10, D12 = 12, D20 = 20 };
    
    /// <summary>
    /// The order in which the dice roll results are returned
    /// </summary>
    public enum SortOrder { Ascending, Descending };


    /// <summary>
    /// Returns the value from a single rolled die of the specified type
    /// </summary>
    /// <param name="dieType">The type of die rolled</param>
    /// <returns>int</returns>
    public static int RollDie(DieType dieType)
    {
        int result = 0;
        int max = (int)dieType;

        result = GetRandomNumber(MinimumDieValue, max);

        return result;
    }

    /// <summary>
    /// Returns the values from the number of specified dice, of the specified type
    /// </summary>
    /// <param name="dieType">The type of die rolled</param>
    /// <param name="numberOfDice">The number of dice to be rolled</param>
    /// <returns>int[]</returns>
    public static int[] Roll(DieType dieType, int numberOfDice)
    {
        int[] results = new int[numberOfDice];

        for (int i = 0; i < numberOfDice; i++)
        {
            results[i] = RollDie(dieType);
        }

        return results;
    }

    /// <summary>
    /// Returns the values from the number of specified dice, of the specified type.  Results are sorted in the specified sort order.
    /// </summary>
    /// <param name="dieType">The type of die rolled</param>
    /// <param name="numberOfDice">The number of dice to be rolled</param>
    /// <param name="sortOrder">The order to sort the results in the returned array</param>
    /// <returns>int[]</returns>
    public static int[] Roll(DieType dieType, int numberOfDice, SortOrder sortOrder)
    {
        int[] results = Roll(dieType, numberOfDice);

        results = Sort(results, sortOrder);

        return results;
    }

    /// <summary>
    /// Returns a newly created "random" number between min and max
    /// </summary>
    /// <param name="min">The minimum value</param>
    /// <param name="max">The maximum value (inclusive)</param>
    /// <returns>int</returns>
    private static int GetRandomNumber(int min, int max)
    {
        return _random.Next(min, max + 1);
    }

    /// <summary>
    /// Sorts the specified int array by the specified sort order
    /// </summary>
    /// <param name="results">The array of dice results to sort</param>
    /// <param name="sortOrder">The order in which to sort the array</param>
    /// <returns>Array</returns>
    /// <remarks>An ArgumentNullException will be thrown if the specified array is null</remarks>
    private static int[] Sort(int[] results, SortOrder sortOrder)
    {
        switch (sortOrder)
        {
            case SortOrder.Ascending:
                Array.Sort<int>(results, new Comparison<int>((i1, i2) => i1.CompareTo(i2)));
                break;
            case SortOrder.Descending:
                Array.Sort<int>(results, new Comparison<int>((i1, i2) => -i1.CompareTo(i2)));
                break;
        }

        return results;
    }
}


Overview;

Dice.RollDie() - returns the result of a single rolled die
Dice.Roll() - returns the results of multiple rolled dice

Both methods require the type of die to be rolled to be specified, e.g. D4, D6, D8, D10, D12, D20, using the DieType enumerator.

Dice.Roll() also requires the number of dice to be rolled to be specified. It also has an overloaded method which will take the desired sort order as a parameter, using the SortOrder enumerator.



Usage Example;

Outputs to the console 5D4, sorted in descending order

    // Use this for initialization
    void Start()
    {
        int[] results = Dice.Roll(Dice.DieType.D4, 5, Dice.SortOrder.Descending);

        for (int i = 0; i < results.Length; i++)
        {
            Debug.Log(results[i].ToString());
        }
    }

Outputs to the console 1D12

    // Use this for initialization
    void Start()
    {
        Debug.Log(Dice.RollDie(Dice.DieType.D12));
    }

Rob, you are a gentleman and a scholar. You have devoted far more time,
effort, and thought to my silly little problem than I could have reasonably
expected. I will try out the solutions you presented and let you know how
they work out.

Thank you,
Hans

1 Like

You are more than welcome Hans.

As it happens I am half way through writing the action/challenge bit of code too, as an example for you… just wiring in the attributes at the moment. If you can give me a couple of hours I’ll pop that up too :slight_smile:


Updated Thu Jul 13 2017 19:16

Attributes done. Just working on the boons/banes now, then I’ll post it up for you :slight_smile:


Updated Thu Jul 13 2017 20:11

Situational concern modifiers done. Testing, then I will post. :slight_smile:

Hi Hans (@Hansmo);

Please find below the scripts based on your detailed explanation of the mechanics. I have include a usage example also.

The only one thing I wasn’t too sure about was this;

“But the modifier for an attribute can be pulled from a variable once things are up and running.”

I have set a const for the value of 10 as per the explanation. This value is applied as a deduction to each attribute.

I wasn’t sure whether you meant the value of 10 could be different, or, whether each attribute could have it’s own deduction value, e.g.

strength -10
will -2
agility -3

I have coded around the first scenario, but do let me know if I have assumed incorrectly.


Code

Attribute.cs

public class Attribute
{
    /// <summary>
    /// The base value to deduct from an attribute
    /// </summary>
    private const int AttributeBaseDeduction = 10;


    /// <summary>
    /// The type of the attribute
    /// </summary>
    private AttributeType _type;

    /// <summary>
    /// The value of the attribute
    /// </summary>
    private int _value;


    /// <summary>
    /// Default constructor
    /// </summary>
    private Attribute()
    {
        // deliberately left empty
    }

    /// <summary>
    /// Overloaded constructor
    /// </summary>
    public Attribute(AttributeType attributeType, int value)
    {
        _type = attributeType;
        _value = value;
    }


    /// <summary>
    /// Possible attribute types
    /// </summary>
    public enum AttributeType { Agility, Intellect, Strength, Will };


    /// <summary>
    /// Returns the type of the attribute
    /// </summary>
    public AttributeType Type
    {
        get { return _type; }
    }

    /// <summary>
    /// Returns the value of the attribute
    /// </summary>
    public int Value
    {
        get { return _value; }
    }

    /// <summary>
    /// Returns the modified value of the attribute
    /// </summary>
    public int ModifiedValue
    {
        get { return (_value - AttributeBaseDeduction); }
    }
}

AttributeCollection.cs

using System.Collections;

public class AttributeCollection : ArrayList
{

}

Action.cs

public class Action
{
    /// <summary>
    /// Threshold for critical success
    /// </summary>
    private const int CriticalSuccessValue = 20;

    /// <summary>
    /// Threshold for critical failure
    /// </summary>
    private const int CriticalFailureValue = 0;


    /// <summary>
    /// Holds the outcome of the action
    /// </summary>
    private OutcomeType _outcome;

    /// <summary>
    /// The target result for a successful outcome
    /// </summary>
    private int _targetResult = 0;

    /// <summary>
    /// The action's result
    /// </summary>
    private int _result = 0;

    /// <summary>
    /// The player attributes to apply as modifiers
    /// </summary>
    private AttributeCollection _playerAttributes = null;

    /// <summary>
    /// The number of boons to apply as a modifier
    /// </summary>
    private int _boons = 0;

    /// <summary>
    /// The number of banes to apply as a modifier
    /// </summary>
    private int _banes = 0;

    /// <summary>
    /// The results for rolled situational concerns
    /// </summary>
    private int[] _situationalConcernResults = null;


    /// <summary>
    /// Default constructor
    /// </summary>
    public Action(AttributeCollection playerAttributes, int boons, int banes)
    {
        _playerAttributes = playerAttributes;

        DetermineSituationalConcerns(boons, banes);
    }


    /// <summary>
    /// Possible outcomes of the action
    /// </summary>
    public enum OutcomeType { CriticalFailure, CriticalSuccess, Failure, Success };


    /// <summary>
    /// Returns the outcome of the action
    /// </summary>
    public OutcomeType Outcome
    {
        get { return _outcome; }
    }

    /// <summary>
    /// Returns the results rolled for situation concerns (boons or banes)
    /// </summary>
    public int[] SituationalConcernResults
    {
        get { return _situationalConcernResults; }
    }


    /// <summary>
    /// Resolves the outcome of a challenge
    /// </summary>
    /// <param name="targetResult">The target result for a successful outcome</param>
    public void Challenge(int targetResult)
    {
        _targetResult = targetResult;

        ResolveAction();
    }

    /// <summary>
    /// Resolves the outcome of an attack
    /// </summary>
    /// <param name="defense">The defence value to beat for a successful outcome</param>
    public void Attack(int defense)
    {
        _targetResult = defense;

        ResolveAction();
     }

    /// <summary>
    /// Determins the number of boons or banes to be used to modify the action's result
    /// </summary>
    /// <param name="boons">The number of boons to apply</param>
    /// <param name="banes">The number of banes to apply</param>
    private void DetermineSituationalConcerns(int boons, int banes)
    {
        if (boons == banes)
        {
            _boons = 0;
            _banes = 0;
        }
        else if(boons > banes)
        {
            _boons = boons - banes;

            CalculateSituationalConcernModifiers(_boons);
        }
        else if (banes > boons)
        {
            _banes = banes - boons;

            CalculateSituationalConcernModifiers(_banes);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    private void ResolveAction()
    {
        RollD20();

        ApplyPlayerAttributeModifiers();
        ApplySituationalConcernModifiers();

        CalculateOutcome();
    }

    /// <summary>
    /// Applies the result of a rolled 1D20 to the action's result
    /// </summary>
    private void RollD20()
    {
        _result = Dice.RollDie(Dice.DieType.D20);
    }

    /// <summary>
    /// Applies any player modifiers to the action's current result
    /// </summary>
    private void ApplyPlayerAttributeModifiers()
    {
        if(_playerAttributes != null)
        {
            foreach (Attribute attribute in _playerAttributes)
            {
                _result += attribute.ModifiedValue;
            }
        }
    }

    /// <summary>
    /// Applies any situational concern modifiers to the action's current result
    /// </summary>
    private void ApplySituationalConcernModifiers()
    {
        if (_boons > 0)
        {
            _result += _situationalConcernResults[0];
        }

        if (_banes > 0)
        {            
            _result -= _situationalConcernResults[0];
        }
    }

    /// <summary>
    /// Calculates the situational concerns for the specified number of dice
    /// </summary>
    /// <param name="numberOfDice">The number of dice to roll</param>
    private void CalculateSituationalConcernModifiers(int numberOfDice)
    {
        _situationalConcernResults = Dice.Roll(Dice.DieType.D6, numberOfDice, Dice.SortOrder.Descending);
    }

    /// <summary>
    /// Calculates the outcome of the action based on the result and target result values
    /// </summary>
    private void CalculateOutcome()
    {
        if (_result <= CriticalFailureValue)
        {
            _outcome = OutcomeType.CriticalFailure;
        }
        else if(_result > CriticalFailureValue && _result < _targetResult)
        {
            _outcome = OutcomeType.Failure;
        }
        else if (_result >= CriticalSuccessValue && _result >= _targetResult)
        {
            _outcome = OutcomeType.CriticalSuccess;
        }
        else if (_result >= _targetResult && _result < CriticalSuccessValue)
        {
            _outcome = OutcomeType.Success;
        }
    }
}

The previously provided Dice.cs is also used.


Usage example;

    // Use this for initialization
    void Start()
    {
        // player setup (nimble, strong, willful, stupid! :D)
        AttributeCollection playerAttributes = new AttributeCollection();

        playerAttributes.Add(new Attribute(Attribute.AttributeType.Agility, 9));    // -1 modifier
        playerAttributes.Add(new Attribute(Attribute.AttributeType.Intellect, 3));  // -7 modifier
        playerAttributes.Add(new Attribute(Attribute.AttributeType.Strength, 12));  // +2 modifier
        playerAttributes.Add(new Attribute(Attribute.AttributeType.Will, 7));       // -3 modifier

        // trigger an action
        Action action = new Action(playerAttributes, 5, 3);

        // fight a beasty
        action.Attack(4);

        // output the action's outcome
        Debug.Log(action.Outcome.ToString());

        // output the dice rolls for the boons/banes (desired feature request)
        for (int i = 0; i < action.SituationalConcernResults.Length; i++)
        {
            Debug.Log(action.SituationalConcernResults[i].ToString());
        }
    }

In the above usage example, a set of player attributes is created, based on your previous explanation. Ordinarily, this would be somewhere else, e.g. within a player object, and the values set through either a player creation process or randomness I assume. This works however for the example.

Next, we trigger an action, again, this would be perhaps based on some event happening during your game. I have included to wrapper methods, Attack() and Challenge() within Action. For all intents and purposes they do the same thing, but could be extended more uniquely should a need be required.

The outcome of the action is output and the rolled dice for the boons/banes are also output.

Try experimenting with some of the values in the usage example, e.g. changing the player attributes, the number of boons/banes and perhaps the defense of the defender, or the target value if you choose to use action.Challenge().

One of your previous feature requests was that seeing the dice rolls for boons/banes, in descending order, where the highest value is used. You can access this via action.SituationalConcernResults which is an int array.

Hoping that the above is of use, any problems let me know :slight_smile:


For your convenience, I’ve zipped the scripts so you don’t have to try and copy/paste them from this post.

Scripts.zip (3.7 KB)

What I was getting at is that each character will have Attribute scores of
Strength, Agility, Intellect, and Will score and it will range (in most
cases) from 8 to 20. This will be subject to change as play goes on but
won’t change that frequently. The modifier for a skill is always the
Attribute - 10.

The fixed number of 10 I was referring to in the case of challenge rolls is
the fixed Challenge Number of 10 for an unopposed roll (that is you are
rolling against something from the environment or the like as in the case
of a Perception check to search a room or a Strength check to continue on
your forced march rather than giving up to exhaustion).

Opposed Challenge rolls are based on some other calculation…for instance
swinging a weapon at a creature would be opposing that creature’s defense
which would be based on the Armor it is wearing (e.g Heavy Full Plate armor
for instance provides a Defense rating of 18, Unarmored uses just the
creature’s Agility score as the challenge roll, and some Lighter armors use
the Agility Score +1 or 2) or might involve another Attribute (e.g.
character attempting to deceive another creature would make an Intellect
based attack roll [1d20 + attacker’s Int modifier + boons/banes] vs
opposing creature’s Intellect score).

I confusingly did not break up the paragraph to make that distinction
clear. If it helps let me present examples.

Creature 1 has Strength 9, Agility 12, Intellect 10, Will 8 and is not
wearing armor so his Defense is 12 (equal to Agility)

So the mod’s would be STR -1, AGI +2, INT 0, WIL -2

Creature 2 has 12 Strength, Agility 9, Intellect 8, Will 11 and is wearing
full plate armor so his Defense is 18 (equal to the armor rating).

So the mod’s for creature 2 would be STR +2, AGI -1, INT -2, WIL +1

Creature 1 tries to sneak up on Creature 2 making an Agility attack versus
Creature 2’s Perception (in this case equal to Creature 2’s Intellect).
Creature 2 is on pretty high alert at the moment so adds one bane to the
the attacker’s roll, also it is bright day light and the ground is covered
in loose gravel making it pretty noisy to traverse so that adds another 2
banes.

Creature 1 rolls a 1d20 and gets a 9, he adds +2 for his agility (11) his
bane rolls are 3, 2, and 5 so he subtracts 5 from the total (11-5= 6) which
is less than the 8 he needed but not so low as to be a critical failure.
Creature 2 hears his approach and decides to respond with a wooden club to
the head of Creature 1.

Creature 2 rolls a 1d20 and gets a 10, he adds +2 for his strength
modifier (12) which meets the challenge number of Creature 1’s Defense (12)
and he scores a hit and rolls damage.

Creature 1, seeing that he is over-matched, having failed to get the drop
on Creature 2, tries to talk his way out of the predicament he has gotten
himself into. He makes a pretty convincing argument that he wasn’t sneaking
up to stab Creature 2 in the back…he simply wanted to scout the situation
out first before making his presence known and offers to share his rations
and provide conversation to pass the time at the boring campsite. A Will
challenge roll versus the opposing creature’s Will.

Creature 1 rolls a 1d20 and gets a 14, to this he subtracts -2 (12) and
he exceeds the challenge number of 11. Creature 2 thinks he may have been
hasty in his decision to attack Creature 1 and puts the club down to parlay
and share a meal.

1 Like

Gotcha…

Ok, so the code I’ve posted up is not quite in line with those requirements, but I don’t think it’s too far out and has room for enhancement.

I would be interested to know what you think/thought of it once you’ve had a chance to test it.

Based on the scenario you have outlined, it would make sense I believe for the Action class above to just receive the target value, as opposed to trying to calculate it based on the quite diverse variations. Keeping it simple etc. Clearly, the -10 skill attribute modifier needs a tweak. Some consideration will also be needed for the comparison of one skill to another.

“Creature 2 thinks he may have been hasty in his decision to attack Creature 1 and puts the club down to parlay and share a meal.”

lol… love it :smiley:

Yes the challenge number will either be a fixed 10 or some number
determined on the spot and will need to be passed to the script by the user.

I will certainly give it a try and get back to you :slight_smile: Thanks again!

1 Like

Sure, that’s not a problem. Knowing the bigger picture helps determine the architecture of it all etc.

I’m assuming that there is some kind of matrix for knowing which/when a skill corresponds to another skill in the case of a challenge. This would help to create the set of rules that will be required to turn that into code.

In the scenario you have described it may be that you reference both creatures in the Action, and then run a series of checks, for example whether a creature is wearing armour or not. But you really want to try and separate the code so that only the relevant objects know what they should / need to know.

You are very welcome. It’s been interesting to put something together with a set of rules already defined :slight_smile:

In practice there are the 4 main Attributes as stated, Strength, Agility,
Intellect, and Will…then there are Characteristics which are, in most
cases, derived from from those Perception, Defense, Health, Healing
Rate
, Power.

Perception in most cases = Intellect but sometimes is modified based on
the character’s ancestry.

Defense as stated previously may equal Agility Score, may equal Armor
rating, or may equal Agility +1 or 2 based on type of armor.

Health at the start of the game equals your Strength but increments
after that when you gain levels. It also increments by a like amount
whenever your Strength score increases. (e.g. If you have a Strength of
10 and Health of 25 and you gain 1 Strength your Health increases by
1 as well to become 26).

Healing Rate = Health .25 rounded down

*Power *is not derived from any attribute but is added or incremented based
on leveling decisions.

Damage,* Insanity*, and* Corruption*.and are special Characteristics that
measure effects you have accrued in the course of play and are incremented
or decremented as dictated by the GM.

Damage represents how healthy or injured you currently are at 0 you are
hale and hearty, When your Damage = your Health you are unconscious and
possibly dying.

Insanity represents the effects of exposure to that which would break the
mind.

Corruption represents the stain on your soul from acts of depravity on
your part.

The idea is that a “character sheet” of sorts can be imported or entered
for any players playing the game and a creature database can be queried
such that if you are playing you can say Bob the player is attacking a
McGuffinlope with his sword. The app would yank the attack value and
modifier from Bob’s sheet, and grab the Defense rating from the
McGuffinlope and then query the GM for boons and banes. Then, with a click
of the mouse, roll the die(dice) and return the result. Next would be the
damage roll but that is a step beyond this.

Please don’t think I expect you to write any or all of this. I’m just
trying to explain the concept behind this. Many dice rollers and and the
like preform this sort of function for many other games. The problem is
none of them 1) work quite the way I would like them to and 2) none of them
are tooled specifically for this system.

It’s funny that by the end of all these exchanges you are going to be an
expert on a game system you have never even played hehe.

Ok, opened the scripts in Unity/MonoDevelop but I am having some trouble
figuring out how to hook them up to buttons in my scene to do
stuff…(perhaps I need a section to take input to populate the
Attributes…I will have to try a bit harder to figure out exactly what is
going on and how to make it all come together.

Your assistance has surpassed my mediocre understanding such that it will
take me a bit to make the connections. Hehe

For now, for testing;

  • Create a new scene
  • Create an empty Game Object in the Hierarchy
  • Add a Script Component
  • Copy the code from Game.cs into it (rename the class to whatever you want in this case, as long as it matches the file name)
  • Add the other script files to the Assets directory
  • Hit Run :slight_smile:

Please don’t think I expect you to write any or all of this. I’m just trying to explain the concept behind this. Many dice rollers and and the like preform this sort of function for many other games. The problem is none of them 1) work quite the way I would like them to and 2) none of them are tooled specifically for this system.

It’s ok, but equally, tell me to butt out at any point :slight_smile:

It’s funny that by the end of all these exchanges you are going to be an expert on a game system you have never even played hehe.

hehe, it’s very interesting and actually quite useful for myself too. It has been a long time since I played things like Dungeons and Dragons and Warhammer / Warhammer 40K. I have recently started writing a small Adventure Game Creator Framework, on the feature list is the ability to battle some beasties, that was going to require dice rolling and obviously some kind of characteristics also. So this is actually very useful to me also to see how some games apply the rules. I wasn’t planning on implementing anything quite as complex but I really like turning real world rules into code :slight_smile:

One thing I will do is break this topic into two, as I believe your original question has been answered regarding sorting arrays. I will move all of the subsequent posts about the specific game into a separate thread. You’ll still be able to find it as you will be tagged in it (they are linked under Activity from your profile).

Topic split :slight_smile:

Interestingly Robert j. Schwalb has worked on both D&D and Warhammer. Shadow of the Demon Lord has been described as a ******* child of the two.

I assume by your reference to the complexity you are referring to the complexity of translating it to code because it actually plays pretty fast and easy. The difference is people can intuit relationships whereas computers need explicit definitions of those same relationships. If interested, perhaps as compensation for your generous tutelage I can purchase and send you a PDF copy of the game :slight_smile:

I am glad my incompetence is good for something :slight_smile:

I did as you suggested and created a test scene and was able to see the results being passed to the console via the Debug Log. Seems to work well enough. Now I just have to figure out exactly what it is doing so I can connected it up. I’ll get there. Just takes me a bit.

p.s. I updated the previous thread to [SOLVED]

1 Like

Hi,

I assume by your reference to the complexity you are referring to the complexity of translating it to code because it actually plays pretty fast and easy.

More the complexity of knowing what to map what to, I suspect in the game rules they are all covered and explained but for example, you knew to use the agility attribute if the character didn’t have armour - that kind of thing.

It will be all of those little rules that will need putting into code, the more there are, the more complex it will be as a solution, but at the same time we must strive for simplicity :slight_smile:

If interested, perhaps as compensation for your generous tutelage I can purchase and send you a PDF copy of the game :slight_smile:

That’s very kind of you Hans but you don’t need to worry about compensating me, I am very happy to help.

I am glad my incompetence is good for something :slight_smile:

hehe, it’s just another learning curve, like walking, talking, riding a bike - it’s all good… stick at it :slight_smile:

Now I just have to figure out exactly what it is doing so I can connected it up. I’ll get there. Just takes me a bit.

The entry point as it were into what there is so far is really the creation of an Action. That would be the place to start. In addition, if you don’t have something already, the ability to create a player would be nice.

A Player class with a few simple fields to start with. You have seen the AttributeCollection and Attributes, you could use that within the Player class which would make sense.

If you run into any difficulties with the examples I’ve given just give me a shout on here, I’m around most days :slight_smile:

p.s. I updated the previous thread to [SOLVED]

That’s great - thanks :slight_smile:

Privacy & Terms