Dice roll Lua script syntax errors
Posted: Wed Jan 29, 2025 8:23 pm
Hello!
I'm using this script to add dice rolls to dialogue system. I've added it to the Dialogue Manager.
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:
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:
I'm unfortunately not sure where to check from here. I'm quite new to this so any direction would be appreciated!
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);
}
}
Code: Select all
Variable["lastDiceRollResult"] = RollDice(6|6,12,knowledge);
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)