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
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)