Dice roll Lua script syntax errors

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
mrife
Posts: 1
Joined: Wed Jan 29, 2025 7:10 pm

Dice roll Lua script syntax errors

Post by mrife »

Hello!

I'm using this script to add dice rolls to dialogue system. I've added it to the Dialogue Manager.

Code: Select all

// Requires Dialogue System for Unity: https://www.pixelcrushers.com/dialogue-system/

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using PixelCrushers.DialogueSystem;
using UnityEngine;

/// <summary>
/// IMPORTANT: C# methods registered with Lua must use double for numbers, not float or int.
/// You can cast to (int), (float), etc inside your function if necessary.
/// </summary>
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")]
public class DiceLuaCommands : MonoBehaviour
{
    // you could easily make this a bool parameter of the RollDice method instead and toggle it on a per roll basis!
    public bool AllowLuckySave = true;

    private static readonly RNGCryptoServiceProvider generator = new();

    private void OnEnable() 
    {
        Lua.RegisterFunction("RollDice", this, SymbolExtensions.GetMethodInfo(() => RollDice(String.Empty, 0, string.Empty)));
    }

    /// <summary>
    /// Rolls a given number of die, applies modifiers if given, and returns the result.
    /// IMPORTANT: The result is not the total rolled but instead a double which represents the result of the roll, See Returns for return values.
    /// </summary>
    /// <param name="diceStr">Each integer stands for a new dice with x number of sides. 1|2|3</param>
    /// <param name="success">Threshold for a successful roll</param>
    /// <param name="modifiersStr">Number Lua variables to apply to the total of the roll. Modifier1|Modifier2|Modifier3</param>
    /// <example>
    /// "Variable["lastDiceRollResult"] = RollDice(6|6,8,strength|charisma)"
    /// Roll 2 6-sided dice, apply 2 Number variables (strength and charisma) as modifiers, and return the result of if it passed the success threshold of 8 to a variable named "lastDiceRollResult".
    /// </example>
    /// <returns>
    /// -1 = Error
    /// 0 = Critical Failure
    /// 1 = Failure
    /// 2 = Success
    /// 3 = Critical Success
    /// 4 = Lucky Save (Optional: make sure the AllowLuckySave variable is true to offer as a return value)
    /// </returns>
    private double RollDice(string diceStr, double success, string modifiersStr)
    {
        if (string.IsNullOrWhiteSpace(diceStr))
            return -1;
        
#if UNITY_EDITOR
        if (DialogueDebug.logInfo)
        {
            Debug.Log(!string.IsNullOrWhiteSpace(modifiersStr)
                ? $"[ROLL DICE] Rolling dice ({diceStr}) with modifiers ({modifiersStr}). Aiming for a total of {success} or greater."
                : $"[ROLL DICE] Rolling dice ({diceStr}). Aiming for a total of {success} or greater.");
        }
            
        var rollDebug = "";
#endif

        int[] dice = DeconstructIntArrayString(diceStr);

        int rolledTotal = 0;
        for (int i = 0; i < dice.Length; i++)
        {
            var roll = MoreRandomInt(1, dice[i]);
            rolledTotal += roll;
            
#if UNITY_EDITOR
            rollDebug += roll;
            if (i != dice.Length - 1)
                rollDebug += ",";
#endif
        }

        var modifierDebug = ""; // i gave up putting this behind UNITY_EDITOR, so it's here!
        int modifiersTotal = CalculateModifiers(modifiersStr, out modifierDebug);
        rolledTotal += modifiersTotal;

#if UNITY_EDITOR
        if (DialogueDebug.logInfo)
        {
            Debug.Log(modifiersTotal > 0
                ? $"[ROLL DICE] Rolled a total of {rolledTotal} ({rollDebug}) with a modifer of {modifiersTotal} ({modifierDebug})"
                : $"[ROLL DICE] Rolled a total of {rolledTotal} ({rollDebug})");
        }
#endif
        
        // calculate total possible value of all the dice together
        var totalPossibleValue = 0;
        foreach (var die in dice)
            totalPossibleValue += die;
        
        if (totalPossibleValue >= (int) success) // check if we've reached the min for success
            return totalPossibleValue <= rolledTotal ? 3 : 2; // calculate if we achieved a crit success or a regular success

        // check if we've made a lucky save
        if (AllowLuckySave && dice.Length > 1)
        {
            // a "lucky save" is when we roll 2 or more dice less than the success threshold
            // but we roll all matching values that aren't 1. idk it's a fun idea, but feel free to disable it.

            int toMatch = dice[0];
            bool allMatch = true;
            for (var i = 1; i < dice.Length; i++)
            {
                var die = dice[i];
                if (die != toMatch)
                {
                    allMatch = false;
                    break;
                }
            }

            if (allMatch)
                return 4;
        }
        
        return totalPossibleValue == dice.Length ? 0 : 1; // calculate if we achieved a crit failure or regular failure

        // deconstruct a string formatted as integers with the deliminator "|" (ex. "1|2|3")
        int[] DeconstructIntArrayString(string str)
        {
            List<int> output = new List<int>();
            var splitStr = str.Split('|');
        
            foreach (var s in splitStr)
            { 
                if (int.TryParse(s, out var result))
                    output.Add(result);
                else if (DialogueDebug.logWarnings)
                    Debug.LogWarning($"[ROLL DICE] Unable to parse the given value from the int array as an int ({s}). Continuing.");
            }

            return output.ToArray();
        }
        
        // split the modifiers string, get the individual variable names, and attempt to parse each as an int
        int CalculateModifiers(string str, out string debugResult)
        {
            debugResult = "";

            if (string.IsNullOrWhiteSpace(str))
                return 0;
            
            int modifiersTotal = 0;
            var splitStr = str.Split('|');

            for (var i = 0; i < splitStr.Length; i++)
            {
                var s = splitStr[i];
                var result = DialogueLua.GetVariable(s);
                if (result.hasReturnValue && result.isNumber)
                {
                    var value = result.asInt;

                    modifiersTotal += value;

#if UNITY_EDITOR
                    modifierDebug += value;
                    if (i != splitStr.Length - 1)
                        modifierDebug += ",";
#endif
                }
                else if (DialogueDebug.logWarnings)
                {
                    Debug.LogWarning(!result.hasReturnValue
                        ? $"[ROLL DICE] Unable to find a variable named ({s}). Continuing."
                        : $"[ROLL DICE] The given variable ({s}) isn't a Number variable. Continuing.");
                }
            }

            return modifiersTotal;
        }
    }
    
    /// <summary>
    /// Returns a more random integer than System.Random or UnityEngine.Random between two values, both params are inclusive.
    /// </summary>
    private static int MoreRandomInt(int min, int max)
    {
        byte[] randomNumber = new byte[1];

        generator.GetBytes(randomNumber);

        double asciiValueOfRandomCharacter = Convert.ToDouble(randomNumber[0]);

        // We are using Math.Max, and substracting 0.00000000001, 
        // to ensure "multiplier" will always be between 0.0 and .99999999999
        // Otherwise, it's possible for it to be "1", which causes problems in our rounding.
        double multiplier = Math.Max(0, asciiValueOfRandomCharacter / 255d - 0.00000000001d);

        // We need to add one to the range, to allow for the rounding done with Math.Floor
        int range = max - min + 1;

        double randomValueInRange = Math.Floor(multiplier * range);

        return (int)(min + randomValueInRange);
    }
}
I've added number variables with skill names (e.g. "knowledge") for use as a modifier within the Lua code. I also created a variable "lastDiceRollResult". I then put the following Lua in the Script field of a dialogue entry:

Code: Select all

Variable["lastDiceRollResult"] = RollDice(6|6,12,knowledge);
As I understand it, this uses the RollDice command to roll two 6-sided die + knowledge (a num. variable set to 5) with a success threshold of 12. The command then returns a response -1, 0, 1, 2, 3, 4 which I assume can then be used to determine the dialogue path. When I play through it, I'm receiving the following error:

Code: Select all

Dialogue System: Lua code 'Variable["lastDiceRollResult"] = RollDice(6|6, 12, knowledge);' threw exception 'Code has syntax errors:
Line 1, Col 44 '|': Failed to parse ')' of GroupExpr.
Line 1, Col 42 '(': Failed to parse Letter of Name.
Line 1, Col 42 '(': Failed to parse Name of VarName.
Line 1, Col 42 '(': Failed to parse 'nil' of NilLiteral.
Line 1, Col 42 '(': Failed to parse Text of BoolLiteral.
Line 1, Col 42 '(': Failed to parse '0'...'9' of Digit.
Line 1, Col 42 '(': Failed to parse (Digit)+ of FloatNumber.
Line 1, Col 42 '(': Failed to parse Name of VariableArg.
Line 1, Col 42 '(': Failed to parse firstTerm of OperatorExpr.
Line 1, Col 42 '(': Failed to parse Expr of ExprStmt.
Line 1, Col 42 '(': Failed to parse remaining input.
'
UnityEngine.Debug:LogError (object)
PixelCrushers.DialogueSystem.Lua:RunRaw (string,bool,bool) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/Lua/Lua Wrapper/Lua Interpreter/Lua.cs:252)
PixelCrushers.DialogueSystem.Lua:Run (string,bool,bool) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/Lua/Lua Wrapper/Lua Interpreter/Lua.cs:131)
PixelCrushers.DialogueSystem.ConversationModel:ExecuteEntry (PixelCrushers.DialogueSystem.DialogueEntry,PixelCrushers.DialogueSystem.CharacterInfo) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/Model/Logic/Model/ConversationModel.cs:296)
PixelCrushers.DialogueSystem.ConversationModel:GetState (PixelCrushers.DialogueSystem.DialogueEntry,bool,bool,bool) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/Model/Logic/Model/ConversationModel.cs:266)
PixelCrushers.DialogueSystem.ConversationModel:GetState (PixelCrushers.DialogueSystem.DialogueEntry) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/Model/Logic/Model/ConversationModel.cs:354)
PixelCrushers.DialogueSystem.ConversationController:OnSelectedResponse (object,PixelCrushers.DialogueSystem.SelectedResponseEventArgs) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/Controller/ConversationController.cs:344)
PixelCrushers.DialogueSystem.ConversationView:SelectResponse (PixelCrushers.DialogueSystem.SelectedResponseEventArgs) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/View/View/ConversationView.cs:517)
PixelCrushers.DialogueSystem.ConversationView:OnSelectedResponse (object,PixelCrushers.DialogueSystem.SelectedResponseEventArgs) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/View/View/ConversationView.cs:510)
PixelCrushers.DialogueSystem.AbstractDialogueUI:OnClick (object) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/UI/Abstract/Dialogue/AbstractDialogueUI.cs:355)
PixelCrushers.DialogueSystem.StandardDialogueUI:OnClick (object) (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/UI/Standard/Dialogue/StandardDialogueUI.cs:383)
UnityEngine.Component:SendMessage (string,object,UnityEngine.SendMessageOptions)
PixelCrushers.DialogueSystem.StandardUIResponseButton:OnClick () (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/UI/Standard/Dialogue/StandardUIResponseButton.cs:132)
UnityEngine.EventSystems.EventSystem:Update () (at ./Library/PackageCache/com.unity.ugui/Runtime/UGUI/EventSystem/EventSystem.cs:530)
I'm unfortunately not sure where to check from here. I'm quite new to this so any direction would be appreciated!
User avatar
Tony Li
Posts: 23256
Joined: Thu Jul 18, 2013 1:27 pm

Re: Dice roll Lua script syntax errors

Post by Tony Li »

Hi,

The first and third parameters are strings. Try this:

Code: Select all

Variable["lastDiceRollResult"] = RollDice("6|6", 12, "knowledge");'
(quotes around them make them strings.)

Note: For quick testing, you can test this using a Lua Console component. Enter:

Code: Select all

return RollDice("6|6", 12, "knowledge")
Post Reply