PDA

View Full Version : Coding dice rolls (actions) in CoreRPG



Trenloe
December 14th, 2016, 17:01
I seem to keep discussing how to roll dice as part of the CoreRPG action manager flow, so it's high time I actually spent the time and put a thread together regarding the subject.

This thread aims to provide details of the CoreRPG actions manager package which is used as the basis for the majority of dice rolling (actions) in CoreRPG and ruleset layered on top of CoreRPG (5E, Pathfinder, 3.5E, 4E, Call of Cthulhu, Castles & Crusades, etc., etc.).

Before you start! Actions can get pretty complex fast. They can use a lot of advanced techniques/procedures (targeting, effects, etc.) and you can soon lose yourself in the complex nested code and give up. Your first custom action doesn't have to be complex (see the basic example in post #4). Start off without targeting, without effects, without success/failure reporting. Then, once you have the base action sorted out, you can slowly add these in. Even if you're an experienced programmer, you can soon get lost in the specifics of FG and it's layered code. Take it in small steps...

Action - this is a term FG uses to describe a specific piece of the RPG system's mechanics that usually requires a dice roll. Common actions are: dice, attack, damage, save, init, skill, etc. and depend on the RPG system in question.

As Fantasy Grounds dice rolling is asynchronous - i.e. FG code doesn't sit waiting for the dice to land before continuing (this would be disastrous to performance), there has to be a number of steps to putting the action together and then processing the result of the action once the dice roll land. These are accomplished in the specific ruleset through an Action Manager.

In it's simplest form, an action could just be a simple dice roll - no recipient (target) of the dice roll, no intended success/fail threshold, etc.. Just roll the dice, add modifiers if necessary, and then display the result. Such an action would have the action type set to "dice".

In a more complex form, say a d20 based attack, there would be one or more targets of the attack, there could be many modifiers (including effects both on the source of the action and the target/s), there would be a threshold the roll needs to meet to succeed (the AC of the target/s), and there could be additional processing needed for certain rolls (critical or fumble). All of this is handled by the ActionAttack package (and the relevant built-in helper functions).

The following posts won't go into the full detail of such a complex action as a d20 RPG attack, but will break down a slightly simpler action to illustrate how the process works. Examples of how to create your own actions will follow.

Note: When you create a new action type, you must let FG know that a new action is present. FG will not process actions with a type it is not aware of. CoreRPG comes with three default action types: dice, table and effect which are stored in the actions table in the GameSystem package (scripts\manager_gamesystem.lua):

-- Ruleset action types
actions = {
["dice"] = { bUseModStack = "true" },
["table"] = { },
["effect"] = { sIcon = "action_effect", sTargeting = "all" },
};

Most rulesets will override this with their own scripts\manager_gamesystem.lua file.

Actions can also be added (handy for extensions) using the GameSystem.actions["XXX_actionname_XXX"] = { XXX_parameters_XXX }; command in the action manager onInit function. See post #4 for an example.

Targetable actions (drag/drop to Combat Tracker entry) In order to make an action drag/droppable it needs to be added to the GameSystem.targetactions LUA table.

Trenloe
December 14th, 2016, 17:02
Action Flow

This example looks at the D&D 3.5E ruleset ActionAbility action handler, which is in the scripts\manager_action_ability.lua file in the 3.5E ruleset. And is initialised in the ruleset base.xml: <script name="ActionAbility" file="scripts/manager_action_ability.lua" /> Thus, any function within this script can be accessed from outside the package with ActionAbility.<function name> See "Script Package" here: https://www.fantasygrounds.com/modguide/scripting.xcp

This is a good example as it contains some complexity, but not too much, which gives a good idea of how an action works and the possibilities. Later in this thread a very simple action will be presented to give an idea of what the action system in it's most base form can be. The "ability" action used in this example is basically a d20 roll modified by the relevant ability bonus - with the ability being strength, dexterity, constitution, intelligence, wisdom or charisma.

All actions should follow the same flow. This is briefly outlined in the header of scripts\manager_actions.lua in the CoreRPG ruleset:

-- ACTION FLOW
--
-- 1. INITIATE ACTION (DRAG OR DOUBLE-CLICK)
-- 2. DETERMINE TARGETS (DROP OR TARGETING SUBSYSTEM)
-- 3. APPLY MODIFIERS
-- 4. PERFORM ROLLS (IF ANY)
-- 5. RESOLVE ACTION

-- ROLL
-- .sType
-- .sDesc
-- .aDice
-- .nMod
-- (Any other fields added as string -> string map, if possible)

Any action will have a LUA record - usually called rRoll with a number of parameters that have specific meaning within the FG code - the base ones are listed in the "ROLL" section in the Action Flow above: sType (action type), sDesc (description to show in the chat window), aDice (dice to roll - includes their result after rolling), nMod (modifier to the roll). These are the base four things needs to define a roll to enable an action to be initiated. There are more parameters, but these are the main four.

Step 1 usually consists of putting the action details together and then initiating the action with ActionsManager.performAction. In this example, it all starts with an ActionAbility.performRoll function call.

Step 2 will be done automatically by the underlying FG code which checks for any targets for the roll - this is targeting in the combat tracker and has nothing to do with any "target" number or ability parameters in the FG control XML or code (more on XML <target> parameters later). In the case of an ability check, there won't be any targets, or if there are (the roller has something targeted in the combat tracker), it won't be used.

Step 3 is where we apply modifiers other than the base ability bonus - this is done with the action modifier Handler. So we go back to manager_action_ability.lua and we see that the onInit() function has a mod handler registered with ActionsManager.registerModHandler("ability", modRoll); So, the modRoll function is our modifier handler. Note: there is also another handler registered here - the ResultHandler of onRoll (more on that later - this is step 5 in our action flow.

Back to the modifier handler - this is where FG sees if there are any additional modifiers to apply to the roll. We've already added the base ability modifier to the roll in step 1 (stored in rRoll.nMod). The modRoll function is more to handle effects that could modify this type of roll. In this case there are a few: ABIL effects, Conditions, STAT effects, negative levels, etc..

Note that the way modRoll gets which ability to use appears somewhat strange at first glance. It parses it out of the roll description using local sAbility = string.match(rRoll.sDesc, "%[ABILITY%] (%w+) check"); It isn't passed as a parameter in rRoll. This is pretty standard practice in FG rulesets - it allows data to be passed asynchronously and also allows the roll to keep it's data well after the roll has finished (so that the result can be drag/dropped from the chat window, for example). The data is usually identified by a keyword in square brackets [] - you've probably seen this a few times in FG roll results in the chat window.

This data passing in the description is setup in the getRoll function, in this case:

rRoll.sDesc = "[ABILITY]";
rRoll.sDesc = rRoll.sDesc .. " " .. StringManager.capitalize(sAbilityStat);
rRoll.sDesc = rRoll.sDesc .. " check";

Confused yet? Still with me? I hope so, because we're nearly there...

Step 4 After the modRoll handler has calculated the final modifications to the roll, the actions manager performs the actual roll. It throws rRoll.aDice (set in the getRoll function) and once the 3D dice land the final final step occurs...

Step 5 The dice land and FG raises a result event and the result handler registered in the manager_action_ability.lua onInit function is executed. This was registered with ActionsManager.registerResultHandler("ability", onRoll); so the onRoll function executes.

This is a fairly straightforward script - it basically gets the result of the roll and works out what the final description displayed in the chat window should be. At this point (the beginning of the onRoll function) no data has been outputted to the chat window, the dice have just landed...


function onRoll(rSource, rTarget, rRoll)
local rMessage = ActionsManager.createActionMessage(rSource, rRoll);

if rRoll.nTarget then
local nTotal = ActionsManager.total(rRoll);
local nTargetDC = tonumber(rRoll.nTarget) or 0;

rMessage.text = rMessage.text .. " (vs. DC " .. nTargetDC .. ")";
if nTotal >= nTargetDC then
rMessage.text = rMessage.text .. " [SUCCESS]";
else
rMessage.text = rMessage.text .. " [FAILURE]";
end
end

Comm.deliverChatMessage(rMessage);
end

A base rMessage record is created - this is what will get sent to the chat window and the message data structure is detailed here: https://www.fantasygrounds.com/refdoc/Comm.xcp

Then the message description has additional data added to it (the description from step 1 comes through and is used as the basis of the description here). So, in this case, we already have a description of "[ABILITY] XXXX check", say "[ABILITY] strength check" for example - see the screenshot in post #3.

Then, if we have a target DC (stored in rRoll.nTarget) we check that against the result of the roll nTotal (returned from ActionsManager.total(rRoll)) and add " (vs. DC XX" (where XX is the target DC) and add [SUCCESS] or [FAILURE] to the description. Then the message is sent to the chat window. This includes the dice roll result, which is stored in rMessage.dice in the message data structure.

That's it! :)

This might seem very complex, but all you have to do is take manager_action_ability.lua as a template, or a similar action manager in your chosen ruleset, or a simple template (available in post 4 of this thread):
- use it to create the manager for your new action
- modify getRoll to setup the roll - set the type, the dice, add the base modifier to rRoll.nMod and include any data to be passed in square brackets - this might be your best way of passing the target number needed.
- use a modifier handler if needed - to handle effects, etc.. I don't know if you'll need this for the kingdom rolls.
- Modify the result handler onRoll to output the appropriate message to the chat window.

In it's most basic form, the action manager might be four functions: onInit (sets up the result handler and possibly the action name registration), getRoll (sets up the initial info needed to start the roll for this action), performRoll (used to start the whole process - usually called from outside the package - see post #3 below) and onRoll (handles the result of the action once the dice have landed). onInit and performRoll are pretty simple, the main coding will be in getRoll and onRoll.

Trenloe
December 14th, 2016, 17:02
Initiating an action

Usually there is a function within the specific action manager that starts the whole action process. For our example (3.5E ability check) this is performRoll in manager_action_ability.lua. We see: function performRoll(draginfo, rActor, sAbilityStat, nTargetDC, bSecretRoll)

The minimum we need here is draginfo (the base action information (https://www.fantasygrounds.com/refdoc/dragdata.xcp) created by FG when a drag action starts - needed for the whole drag/drop process to function correctly), rActor (a LUA record containing details of the PC or NPC that started the action - they are known as the actor and this record can be obtained using the ActorManager.getActor helper function) and sAbilityStat which needs to be the ability to roll the check against - "strength", "dexterity", etc.. There's a couple of arguments (nTargetDC and bSecretRoll) that aren't passed in this case.

So, from this, we could initiate an strength ability check with: ActionAbility.performRoll(draginfo, rActor, "strength"); This would be initiated from a control on a character/NPC sheet or the Party Sheet, so that the Actor record is available, via the control's onDoubleClick (https://www.fantasygrounds.com/refdoc/windowcontrol.xcp#onDoubleClick)or onDragStart (https://www.fantasygrounds.com/refdoc/windowcontrol.xcp#onDragStart)events. If double-click is used draginfo would be blank: ActionAbility.performRoll(, rActor, "strength");

Looking at the ActionAbility.performRoll function:

function performRoll(draginfo, rActor, sAbilityStat, nTargetDC, bSecretRoll)
local rRoll = getRoll(rActor, sAbilityStat, nTargetDC, bSecretRoll);

ActionsManager.performAction(draginfo, rActor, rRoll);
end

The first step in this small function is getRoll which basically puts all of the roll data together in the rRoll record (remember rRoll from post #2?):

function getRoll(rActor, sAbilityStat, nTargetDC, bSecretRoll)
local rRoll = {};
rRoll.sType = "ability";
rRoll.aDice = { "d20" };
rRoll.nMod = ActorManager2.getAbilityBonus(rActor, sAbilityStat);

rRoll.sDesc = "[ABILITY]";
rRoll.sDesc = rRoll.sDesc .. " " .. StringManager.capitalize(sAbilityStat);
rRoll.sDesc = rRoll.sDesc .. " check";

rRoll.bSecret = bSecretRoll;

rRoll.nTarget = nTargetDC;

return rRoll;
end
This is where the type is set = "ability" in this case, what dice to roll, what modifier to use (calls a helper function to get the ability bonus based off sAbilityStat). Then sets up the roll description - this is key (see post #2, Step 3). A couple of other parameters are added and then control passes back to performRoll with the rRoll record being returned and ActionsManager.performAction(draginfo, rActor, rRoll); is called to initiate the full action sequence, which is detailed in post #2.

So, where does the 3.5E ruleset do this? Looking at the Ability control XML in the 3.5E (campaign\template_char.xml) we have a template that is used to create the ability bonus fields: <template name="number_charabilitybonus"> These appear in the GUI as shown below:

https://www.fantasygrounds.com/forums/attachment.php?attachmentid=16914

This screenshot shows the six different ability bonus controls (one for each ability) and an example strength ability check in the chat window.

Each of these controls is created using the <template name="number_charabilitybonus"> template in campaign\record_char_main.xml. For example, the strength bonus field:


<number_charabilitybonus name="strengthbonus" source="abilities.strength.bonus">
<anchored to="strength" />
<target>strength</target>
<modifierfield>abilities.strength.bonusmodifier</modifierfield>
<description textres="char_tooltip_strbonus" />
</number_charabilitybonus>

Of interest here is the <target> parameter. This is used by the template to specify which ability should be used for the roll - allowing the number_charabilitybonus template to be re-used for different abilities. This "target" parameter should not be confused with rTarget in the general action process - rTarget is completely different and is used to store the targeting information of an action - i.e. other PCs/NPCs that are being targeted for an attack, damage, heal, etc. action.

See "Accessing XML parameters from script" here: https://www.fantasygrounds.com/modguide/scripting.xcp for more info on how to store information in a control's XMl and access it from a script.

The ability action uses the XML parameter <target> in the XML <script> section of the number_charabilitybonus control template as follows:


function action(draginfo)
local rActor = ActorManager.getActor("pc", window.getDatabaseNode());
ActionAbility.performRoll(draginfo, rActor, self.target[1]);

return true;
end

function onDragStart(button, x, y, draginfo)
return action(draginfo);
end

function onDoubleClick(x,y)
return action();
end

Either a double-click on the control or a drag start event will call with action function - note that draginfo is only relevant if a drag event is being initiated. The action function gets the rActor record from the ActorManager.getActor helper function (telling the function that it is a "pc" and passing the database record associated with the character sheet). Then ActionAbility.performRoll(draginfo, rActor, self.target[1]); is called to start the whole "ability" action process - self.target[1] retrieves the name of the ability to use for the roll. This kicks off the whole process - see the start of this post for the specifics of the ability action and then post #2 for more general action processing and the final result handler for the ability action.

Trenloe
December 14th, 2016, 17:24
The minimum needed for a custom action!

The following is an example framework for an action manager. In this case, this will handed the "mytestaction" action, rolling a d20 and displaying the result with the description "My Test Roll!"


function onInit()
-- Register the new action we're creating. We'll allow use of the modifier stack for this action type.
GameSystem.actions["mytestaction"] = { bUseModStack = true };

-- Register the result handler - called after the dice have stopped rolling
ActionsManager.registerResultHandler("mytestaction", onRoll);
end

function getRoll(rActor, bSecretRoll)
-- Initialise a blank rRoll record
local rRoll = {};

-- Add the 4 minimum parameters needed:
-- the action type.
rRoll.sType = "mytestaction";
-- the dice to roll.
rRoll.aDice = { "d20" };
-- A modifier to apply to the roll.
rRoll.nMod = 0;
-- The description to show in the chat window
rRoll.sDesc = "My Test Roll!";

-- For GM secret rolls.
rRoll.bSecret = bSecretRoll;

return rRoll;
end

function performRoll(draginfo, rActor, bSecretRoll)
local rRoll = getRoll(rActor, bSecretRoll);

ActionsManager.performAction(draginfo, rActor, rRoll);
end

function onRoll(rSource, rTarget, rRoll)
-- Create the base message based off the source and the final rRoll record (includes dice results).
local rMessage = ActionsManager.createActionMessage(rSource, rRoll);

-- Display the message in chat.
Comm.deliverChatMessage(rMessage);
end

We can save this script as a package - let's call it manager_action_mytest.lua and initialise it with <script name="ActionMyTest" file="scripts/manager_action_mytest.lua" /> - usually in the ruleset base.xml or the extension extension.xml file. To start this custom action all we need to do is call: ActionMyTest.performRoll(draginfo, rActor, bSecretRoll);

Try it yourself!

I've packaged the above code into a simple extension (attached - MyTestAction.ext). download this, put it in your <FG app data>\extensions directory and load up one of the main CoreRPG based rulesets (or CoreRPG itself) and enable the "My Test Action" extension. the, when the campaign loads, you can run this actions by typing /mytestaction in the chat window.

Example shown below. The second roll used a modifier entered in the Modifier Stack.

https://www.fantasygrounds.com/forums/attachment.php?attachmentid=16917

Trenloe
December 14th, 2016, 17:51
OK, that all makes sense - show me more!

Or...

You completely lost me, can we look at a different action?

More Examples

The FG rulesets are littered with many examples. Just look in the ruleset scripts directory, any .lua (script) file beginning with manager_action_ should be an action manager for a specific action. manager_action_attack.lua is the action manager for the "attack" action type, manager_action_damage.lua is the action manager for the "damage" action type, etc., etc.. All of these action types should have been defined in the actions table in scripts\manager_gamesystem.lua

But, these mainstream ruleset actions can still be quite complex and difficult to follow 100% - they usually involve targeting, effects, extra processes (critical damage, for example), etc.. So, I'd recommend looking at some of the dice rolling mechanics basic extensions created by the community:

The count successes extension is similar to the example in post #4 (you can test it from the chat window). It get's a little more advanced in that is passes parameters from the initial roll through to the result handler via square bracket parameters (mentioned in post #2) and then via a custom rRoll variable. Available here: https://www.fantasygrounds.com/forums/showthread.php?31425-Basic-success-counting-extension
Roll and Keep dice mechanics: https://www.fantasygrounds.com/forums/showthread.php?23095-7th-Sea-Roll-amp-Keep&p=199129&viewfull=1#post199129
Custom dice results: https://www.fantasygrounds.com/forums/showthread.php?23013-Demi-Dice&p=197855&viewfull=1#post197855 Not really an action handler, but shows a way to create custom dice results.
The MoreCore ruleset has a number of custom dice rollers built in. Find this excellent addition to the CoreRPG generic functionality here: https://www.fantasygrounds.com/forums/showthread.php?23281-More-Core-extension-for-CoreRPG

ronalmb
December 14th, 2016, 22:44
This has been extremely helpful. Very informative. Thank you! Explained in a way that I grasped it.

The only problem I have is that when I use your extension, my dice results are not coming out the same as yours.


... crickets ...




Ok, really, I'm not that much of a noob. :) Now I'm off to put your lessons to the test!

Trenloe
December 14th, 2016, 22:52
The only problem I have is that when I use your extension, my dice results are not coming out the same as yours.
You have to sign up to Secret University to learn how to do that...

dulux-oz
December 15th, 2016, 00:41
This needs to be on the Wiki as well (if its not already) - Trenloe, if you send me a wiki-marked-up file I'll put it up (assuming you don't have permission too).

Cheers

Tubel
October 20th, 2019, 11:52
Actions can get pretty complex fast. They can use a lot of advanced techniques/procedures (targeting, effects, etc.) and you can soon lose yourself in the complex nested code and give up.


All Hail Trenloe, High Prince of the Understatement!

Having said that...time for some genuine thanks. With the help of this tutorial, after nearly 4 weeks of painstakingly picking apart the 3.5e and coreRPG code, repeatedly working through it in conjunction with this tutorial, i finally managed to get trait tests and skill checks working on my CoreRPG Pendragon extension. I should also throw out a special thanks to Damned and the MoreCore crew for the Pendragon roller code in morecore that I shamelessly stole and repurposed. This has been quite a journey teaching myself how to do this. I have to say I very nearly gave up several times, it must have taken me 40+ hours to get my brain around actions enough to make these simple trait rolls work. I don't entirely understand why the last change worked and the 50 before that didn't, but that has been a consistent feature of my FG customisation learning journey. Frankly I don't understand 2/3 of the nested code that spits out my trait rolls but I'm here to tell you that you can make your custom system work too without understanding it all. Just so long as you can persevere long enough to string other folks work together so that it works for you.

In a broader sense, the process of learning rudimentary XML and even more rudimentary lua to get this far has been both satisfying and humbling. Starting from a rock bottom 'zero coding experience' I can assure you I am definitely not a natural born coder. My first attempt at setting up Pendragon in MoreCore started out with much faster initial progress obviously but then hit a wall as I reached the limit of the standard morecore. The feature rich nature of moreCore meant i wasn't learning the XML/lua at all and it is so rich and layered that frankly my brain couldn't cope with the complexity of it as I tried to understand how it fit together. That's a testament to the brilliance of moreCore and a critical assessment of my own powers of comprehension. So I went back to the start, beginning with vanilla coreRPG, slowly layering tiny change after tiny change. I now have a customised Pendragon character sheet with a good amount of automation set up, an extra history tab and have taught myself a number of FG techniques such as rudimentary windowlists, actions, autocalculating stats, a goodly amount of XML positioning, and so forth. This is pretty good progress for a guy who took perhaps 2 weeks to understand what 'extensible' actually meant...even though it's right there 'on the box' in the name XML...

I relate this story in the hope that it encourages all those folks who read this thread and like me stood staring the screen thinking "WTF does that mean?" Over the course of the last 4 weeks i must have read this through 10-15 times in the hope of having a new epiphany on each read through. Sometimes I did, and eventually after having enough of them, i finally cracked it. So hang in there, take a day off from it if it's getting you down, and just keep going. I'm (almost) taking for granted things that completely stumped me 6 or so weeks ago, like what a template was, or how anchoring works. Half of the challenge is learning to recognise the syntax enough to know where to go searching for stuff. Lua was 100% incomprehensible for a long time, now i understand perhaps 20% what i'm reading and I can stumble through the basic stuff.

There is much still to be done for my Pendragon extension and I keep an ever growing list of features to implement. Each one is a new challenge that helps me to learn more about how this magnificant collaborative undertaking, Fantasy Grounds, works and supports this wonderful hobby that I've loved for 35 years now.

In Pendragon terms, try to crit your Energetic Trait test, and stick with it. Never having coded before does not preclude you from success...although there is no denying it makes it bloody hard...

Tubel

EmptyOwl
June 22nd, 2020, 03:01
The minimum needed for a custom action!

The following is an example framework for an action manager. In this case, this will handed the "mytestaction" action, rolling a d20 and displaying the result with the description "My Test Roll!"


function onInit()
-- Register the new action we're creating. We'll allow use of the modifier stack for this action type.
GameSystem.actions["mytestaction"] = { bUseModStack = true };

-- Register the result handler - called after the dice have stopped rolling
ActionsManager.registerResultHandler("mytestaction", onRoll);
end

function getRoll(rActor, bSecretRoll)
-- Initialise a blank rRoll record
local rRoll = {};

-- Add the 4 minimum parameters needed:
-- the action type.
rRoll.sType = "mytestaction";
-- the dice to roll.
rRoll.aDice = { "d20" };
-- A modifier to apply to the roll.
rRoll.nMod = 0;
-- The description to show in the chat window
rRoll.sDesc = "My Test Roll!";

-- For GM secret rolls.
rRoll.bSecret = bSecretRoll;

return rRoll;
end

function performRoll(draginfo, rActor, bSecretRoll)
local rRoll = getRoll(rActor, bSecretRoll);

ActionsManager.performAction(draginfo, rActor, rRoll);
end

function onRoll(rSource, rTarget, rRoll)
-- Create the base message based off the source and the final rRoll record (includes dice results).
local rMessage = ActionsManager.createActionMessage(rSource, rRoll);

-- Display the message in chat.
Comm.deliverChatMessage(rMessage);
end



Hiya,

I have been trying to wrap my head around coding dice/actions but I am having some oddities occur. If I replace "d20" in the above with "3d6" it does not seem to work. The instructions talk about aDice being the dice you roll, not sure why this does not work. It will work with "d6" however. Any help or pointers on this behavior would be helpful!

Thanks!

damned
June 22nd, 2020, 04:11
it would be { "d6","d6","d6" }

adice is an array of dice.

EmptyOwl
June 22nd, 2020, 22:35
Thank you damned! Helps alot!

-Thanks!

bmos
August 5th, 2020, 22:25
Can multiple registerResultHandlers be set up in different scripts for the same roll?
I want to count consecutive saving throws in Pathfinder and it would be great to take advantage of the saving throw script that already exists.

I tried adding
ActionsManager.registerResultHandler("save", onRoll);
to onInit but the onRoll function I set up to debug with didn't trigger.

damned
August 5th, 2020, 23:26
Can multiple registerResultHandlers be set up in different scripts for the same roll?
I want to count consecutive saving throws in Pathfinder and it would be great to take advantage of the saving throw script that already exists.

I tried adding
ActionsManager.registerResultHandler("save", onRoll);
to onInit but the onRoll function I set up to debug with didn't trigger.

I could be wrong but I think the answer is No.

bmos
August 9th, 2020, 01:41
I'm having a hard time triggering onRoll for some reason.
rRoll is
{ s'aDice' = { #1 = s'd20' }, s'nMod' = #1, s'tags' = s'diseasetracker', s'sType' = s'disease', s'nTarget' = #20, s'sDesc' = s'[DISEASE] Fortitude' }
I never see the debug message on line 214 (https://github.com/bmos/FG-PFRPG-Disease-Tracker/blob/savecounter/scripts/manager_action_diseasesave.lua#L214) but the handler to call it (https://github.com/bmos/FG-PFRPG-Disease-Tracker/blob/savecounter/scripts/manager_action_diseasesave.lua#L8) seems to be set up just like the example.

damned
August 9th, 2020, 04:25
Just something you might try

Ln8: ActionsManager.registerResultHandler('diseasesave' , onDiseaseSave)
Ln213: function onDiseaseSave(rSource, rTarget, rRoll)
Ln214: Debug.chat('onDiseaseSave')

bmos
August 9th, 2020, 11:13
Just something you might try

Ln8: ActionsManager.registerResultHandler('diseasesave' , onDiseaseSave)
Ln213: function onDiseaseSave(rSource, rTarget, rRoll)
Ln214: Debug.chat('onDiseaseSave')Alas, no change. Thanks for the suggestion 'though!

EDIT: I'm not really sure what fixed it but it is working now.

Quorlox
August 9th, 2020, 14:08
Thanks for putting so much effort into this! How would I make the die rolled an exploding die?

ShakyLuigi
October 12th, 2020, 20:43
Thank you for making this guide.
I've used your example action and been able to implement it in a template, so that doubleclicking a frame makes it roll.
But if I would like to have the value in the frame as a modifier, how would I go about that?
I am trying to dig through and dissect the 3.5 lua files, but I am getting completly lost in the codes.
Is there an easy way of getting the value localy, or would I need to create strings and several functions in a lua file to make it function?

Thanks in advance.

Varsuuk
February 1st, 2021, 06:21
I had modified an existing MoreCore attack routine successfully, however - I am not sure how to get a DRAG to target on CT, for example, to work. So I guess I didn't REALLY get it correct ;)

(An example case is you forgot to target someone and rolled, this lets you drop it on the token or CT entry you wish to check a hit against.

When I printout rRoll, I see some clues why it would not work but what am I supposed to do to support this?

Trenloe
February 1st, 2021, 16:46
I had modified an existing MoreCore attack routine successfully, however - I am not sure how to get a DRAG to target on CT, for example, to work. So I guess I didn't REALLY get it correct ;)

(An example case is you forgot to target someone and rolled, this lets you drop it on the token or CT entry you wish to check a hit against.

When I printout rRoll, I see some clues why it would not work but what am I supposed to do to support this?
1) Make sure that you've assigned the action as a targetable action in GameSystem.targetactions.
2) Make sure your action scripts maintain draginfo throughout the action chain.

Fezzik Buttercup
March 19th, 2022, 04:58
Thanks Trenloe (and Damned for pointing me to this tutorial)

I was trying to figure out how to pass back the total from a die roll and banging my head against the computer desk because it kept passing nil values left right and centre (and the rRolls always wanted to be empty too).

I'm able to see a total if I print it from onRoll, but if I try to get a total of the roll anywhere else, it fires an : attempt to index local 'rRoll' (a nil value) in the manager actions lua of the coreRPG.

This is what I'm trying to get around and it almost seemed like I had it and then the computer ripped it away from me :).

damned
March 19th, 2022, 05:30
Once the dice script has finished executing the result doesnt live anywhere that is accessible to your code anymore (except perhaps in the chatwindow ondrag events).

You have to do all your actions inside THIS SET OF SCRIPTS - you will be doing it in that Step 5 - Resolve Action. You cant do it after - you have to do it as part of the roll.

damned
March 19th, 2022, 05:32
have a look at a working example here:

https://github.com/DMFirmy/Drop-Lowest

Fezzik Buttercup
March 19th, 2022, 17:57
Thanks Damned; I can't get that one to work on any ruleset.

Would it be better (wiser) to call the table to be rolled on from onRoll? That's the only function that seems to allow Actionsmanager.total(rRoll) to actually NOT throw an error at me. From there I can use the total to pop out the message from the table (at least that's what make logical sense in my mind, whether that is the case or not remains to be seen :) ).

(thank you for your patience and good advice!)


edit: do I need to look into OOB's? *panic*

damned
March 20th, 2022, 00:22
here is a working build.
its just called Drop Lowest in your extension list

to test it type in chat

/rolld 5d6 2

working thru this example and then modifying it to roll what you want is going to be helpful

Fezzik Buttercup
March 21st, 2022, 03:55
I can't seem to wrap my head around getting the total out of these, so I'm going with the basic math line and maybe let someone else worry about seeing a roll if they want later on :). I'll just work on tables for now and parcing the chat messages to locate critical hits etc.

Thanks damned!

Moon Wizard
March 21st, 2022, 04:22
What script errors are you getting when you use ActionsManager.total(rRoll)?
What is the contents of the rRoll variable that you are passing in? (You can use Debug.chat or Debug.console to output the variable before the call.)

Regards,
JPG

Fezzik Buttercup
March 21st, 2022, 19:18
In all of the functions, it fails except onRoll (I'm taking a wild guess here its because the only thing the ActionsManager.register... is 'linked' to onRoll?). When I cycle through the other functions with debug (and try to get a .total too) the rRoll is empty :O . The error is on 661 (scripts/manager_actions.lua; attempt to perform arithmetic on field 'result' (a nil value). (line 656)

I can't call .onRoll() from my other scripts as that gives a 'attempt to indext local 'rRoll' (a nil value). Calling .processRoll works, but the total is spit out only on onRoll, which isn't called directly (just through the ActionsManager (at least that's what it seems to me; my brain hurts :) )

Thank you for your patience guys.

Trenloe
March 21st, 2022, 19:29
In all of the functions, it fails except onRoll (I'm taking a wild guess here its because the only thing the ActionsManager.register... is 'linked' to onRoll?). When I cycle through the other functions with debug (and try to get a .total too) the rRoll is empty :O . The error is on 661 (scripts/manager_actions.lua; attempt to perform arithmetic on field 'result' (a nil value). (line 656)

I can't call .onRoll() from my other scripts as that gives a 'attempt to indext local 'rRoll' (a nil value). Calling .processRoll works, but the total is spit out only on onRoll, which isn't called directly (just through the ActionsManager (at least that's what it seems to me; my brain hurts :) )

Thank you for your patience guys.
Referring to the steps detailed in post #2 of this thread: performRoll (is this what you're calling processRoll?) is the only function you call externally - and this is to initiate the roll action. Everything else within the action manager LUA file gets called as part of the action process outlined in post #2 - calling those functions externally will give you the errors you're seeing.

I'd recommend going back and going through the process detailed in the first few posts of this thread - and ensuring you have the ActionsManager.registerResultHandler for your onRoll function - otherwise that function won't be called. And also ensure the roll type you're using doesn't have the same name as an existing roll type in the ruleset you're using. If you still can't get it working, then please post the whole action manager LUA file here.

Mephisto
March 24th, 2022, 09:22
What helped me understand the flow of a role was to put a debug message into each function of a script, something like "Debug.chat("FN: performRoll in manager_action_check")". This way you can see how the roll will flow and which script is called at what point in time. From there it is much easier to see how variables/records like rRoll are passed through.
You mentioned somewhere local rRoll in your posts. Keep in mind that rRoll usually doesn't need to be declared in a subsequent function after function "performRoll" as it is defined there for the roll and than passed on.

Fezzik Buttercup
March 27th, 2022, 21:37
Eureaka! I'm only adding this hear so that is some other vacuum headed twit like me is wondering how this works they can get a shortcut version.

Lots of debugs once i figured out the flow.

I can't do hit locations at the moment (currently if I try to call a different processRoll for locations it continuously spams rolls, but this may be a logic issue and not a rolling issue), but I have managed to do the Criticals (success, failure, spell, unarmed) by monitoring the chat.

The process roll is called within the onRecieveMessageTrigger function I have (thank you Celestian for 'showing' me how to do that -- ie. Audio Overseer was a huge help there for the very basics) which is called in the onInit via ChatManager.registerReceiveMessageCallback(onRecei veMessageTrigger).

The same basic code to parse what type of critical it is is used in both onRecieve... and onRoll, but the message is completed in onRoll (which is called with ActionsManager.registerResultHandler("criticals", onRoll) ; on Receive just sniffs out as the above paragraph says, but the real nuts and bolts are done in onRoll -- getting the total, adding to the message etc.; which is what everyone was saying, but I wasn't really understanding.

Somethings I have to tweak/find out yet are:
setting the message from GM to the actual critter/player 'rolling' the dice -- I think I can find that in the combat actions
is it BAD to call the processRoll from a modified version of actions_combat (GURPS has _melee and _ranged, so I would have to call from both of them) in an extension?
why has humanity not made a brain interface with computers that we can just think what we want and they do it?

bloodylemming
September 17th, 2022, 20:22
Does aDice[] have a length function, so you can get the number of dice rolled?
I'd like to handle each individual die result in a roll, but will have no way of determining how many dice will be rolled...

Moon Wizard
September 17th, 2022, 21:26
It’s just a Lua table. If a Lua table has integer indexes starting at 1, you can use #tablename to get the length; otherwise, you have to iterate over table using pairs function to count.

In this case, the dice table returned die shave numerical indexes (plus a couple other string indexes), so # operator should work.

Note that if you edit the dice table, you’ll have to make sure that it is still numerically ordered.

Regards,
JPG

bloodylemming
September 17th, 2022, 21:33
It’s just a Lua table. If a Lua table has integer indexes starting at 1, you can use #tablename to get the length; otherwise, you have to iterate over table using pairs function to count.

In this case, the dice table returned die shave numerical indexes (plus a couple other string indexes), so # operator should work.

Note that if you edit the dice table, you’ll have to make sure that it is still numerically ordered.

Regards,
JPG

What would that syntax look like?
rRoll.aDice.#tablename ?

I tried table.maxn(rRoll), but it returned a value of 0...
table.getn(rRoll) also returns a value of 0...



function KillDiceResult(rSource, rTarget, rRoll)

local rMessage = ActionsManager.createActionMessage(rSource, rRoll);

local nodeChar = rSource.sCreatureNode;
-- Debug.chat("nodeChar: ", nodeChar);

local sIcon;
if rSource.sType == "npc" then
sIcon = "portrait_gm_token";
else
sIcon = DB.getValue(nodeChar .. ".token");
sIcon = string.gsub(sIcon, "token", "chat");
end
rMessage.icon = sIcon;

Debug.chat("# Dice: " .. table.getn(rRoll));
end

Moon Wizard
September 18th, 2022, 00:14
#(rRoll.aDice)

JPG

bloodylemming
September 18th, 2022, 00:53
That works, thankyou.