PDA

View Full Version : How to make <rollable /> work?



gandhi39
April 5th, 2012, 18:47
On making my own ruleset, I want a control that displays a modifier (such as +2) to be "rollable" (automatic roll of 1d20 + the modifier, on a doubleclick).

Yet, I saw that scripts to do these things on 4e or 3.5 rulesets are very complex, accessing multiple script files, "nodes", "childs", "actors", "actions", among other things. Probably to display "character rolls that skill and gets that result" on the chatbox.

Simply putting the <rollable /> definition, shows up a d20 icon, but doesn't seem to make it actually rollable. So a minimum script is probably necessary.

Since I don't need any fancy messages on the chatbox, is it possible to have a simpler scrip, just to roll what is being displayed (such as the +2 modifier)?

Zeus
April 5th, 2012, 22:29
Yes its possible.

Check out the Comm package and method throwDice (https://www.fantasygrounds.com/refdoc/Comm.xcp#throwDice)().

You can create a simple script block in your window control to override the default behaviour for when a mouse click release event is detected over the control. e.g


<script>
function onClickRelease(x,y, button)
Comm.throwDice( "dice", {"d20"}, getValue(), description)
end
</script>

gandhi39
April 5th, 2012, 22:59
I changed your script to:



<script>
function onDoubleClick(x, y)
Comm.throwDice( "dice", {"d20"}, getValue(), description)
end
</script>


And it worked! Thank you very much!

gandhi39
April 6th, 2012, 00:31
The doubleclick works fine, but dragging and dropping doesn't.

I added this code:


function onDragEnd(dragdata)
Comm.throwDice( "dice", {"d20"}, getValue(), "Jogada de Perícia")
end


Works more or less. When I drop (anywhere on screen) it displays the roll on the chatbox.

But when I drag it displays a number, instead of a d20. And if I drop it on the dice tower it rolls unhidden, and I get an extra roll of 1d0 + the modifier.

How can I make drag and drop to work correctly?

Moon Wizard
April 7th, 2012, 06:47
Try this code for a ruleset with v2.8 compatibility:
(Check base.xml for the root version attribute)



function onDragStart(button, x, y, dragdata)
dragdata.setType("dice");
dragdata.setDieList({ "d20" });
dragdata.setNumberData(getValue());
dragdata.setDescription("Jogada de Perícia");

return true;
end


Or this code for a ruleset with v2.0 compatibility:



dragging = false;

function onDrag(button, x, y, dragdata)
if dragging then
return true;
end

dragdata.setType("dice");
dragdata.setDieList({ "d20" });
dragdata.setNumberData(getValue());
dragdata.setDescription("Jogada de Perícia");

return true;
end

function onDragEnd(dragdata)
dragging = false;
end



Cheers,
JPG

gandhi39
April 7th, 2012, 09:40
Thank you very much! It worked perfectly!

gandhi39
April 7th, 2012, 10:06
I know that now I would be entering the "fancy descriptions" territory, but if those rolls are meant for skills, is it possible to make a variable description?

Displaying the name of the respective skill, rolled at that moment, instead of "Jogada de Per&#237;cia".

Like displaying "Intimidation" when I doubleclick on the Intimidation skill. And "Survival" when I doublecklick on the Survival skill.

Those skills are listed on the data_common.lua script file, so their displays on the skills tab are automated.

I would probably need to access the skillsnode, childs, getName and stuff. Sorry, but I just can't understand how this database accessing works. I'm not a programer and its plain greek to me. I manage to work around the xml files, but lua scripting is impossible to understand most of the times.

Thanks again.

Moon Wizard
April 9th, 2012, 19:39
When you are settings up your skill roll, you need to retrieve the name of the skill from another field in the skill record.

Try:
local sSkillRollDesc = "[SKILL] " .. window.label.getValue();

Then, you can set the sSkill variable to be the description string for the drag and the throwDice call.

Regards,
JPG

gandhi39
August 17th, 2012, 00:35
The following script doesn't seem to work on a 4e ruleset with v2.0 compatibility:



<script>
function onDoubleClick(x, y)
Comm.throwDice( "dice", {"d20"}, getValue(), description)
end
</script>


It says:

Script Error: [...] 1: attempt to index global 'Comm' (a nil value)

How can I fix this? Thank you for your help.

Moon Wizard
August 17th, 2012, 04:29
I'm not sure I understand, earlier in the thread, you mentioned that it was working. The Comm package is added automatically in the current version of Fantasy Grounds. (v2.9.2)

Regards,
JPG

Valarian
August 17th, 2012, 14:16
Is this something added for 2.9.2? The current live version is still 2.9.1 isn't it?

Zeus
August 17th, 2012, 23:07
@gandhi39 - if this is for a different ruleset base i.e. 2.0, I don't believe the Comm package will be supported. The Comm package needs 2.9 I believe. For 2.0, try calling ChatManager.throwDice().

@Valarian - I think Comm should be available in 2.9.1; 2.9.2 is available in Test Mode at the moment.

gandhi39
August 18th, 2012, 07:28
ChatManager.throwDice didn't work:


attempt to call global 'throwDice' (a nil value)


There seems to be a DieControlThrow(type, bonus, name, custom, dice) function, but I also don't know how to use it.


function DieControlThrow(type, bonus, name, custom, dice)
if control then
control.throwDice(type, dice, bonus, name, custom);
end
end


I tried this:


function onDoubleClick(x, y)
ChatManager.DieControlThrow( "dice", {"d20"}, getValue(), description)
end


But got "Invalid arguments to throwDice".

gandhi39
August 18th, 2012, 08:12
I have another question:

How can I make a "linkednumber" control to return the greatest value among two sources (example: abilities.strength.bonus and abilities.constitution.bonus)?

I suppose I should use de math.max function perhaps. But how?

Zeus
August 18th, 2012, 11:47
Try,

function onDoubleClick(x, y)
ChatManager.DieControlThrow( "dice", getValue(), description, nil, {"d20"});
end
The order of the parameters is slightly different in the older method approach; it also takes a custom table which in the example above is set to nil (i.e. nothing).

As for your second question, its probably simplest to write a fucntion which compares two source values and returns true or false. You can then use the method to compare any two values to determine the greater value e.g.


function isGreater(x, y)
if x and y and x > y then
return true;
else
return false;
end
end

function onInit()
if isGreater(abilities.strength.bonus.getValue(), abilities.constitution.bonus.getValue()) then
[do some action with the strength stat]
else
[do some action with the constitution stat]
end
end

gandhi39
August 19th, 2012, 01:46
Thanks! ChatManager.DieControlThrow( "dice", getValue(), description, nil, {"d20"}) worked fine!

But I'm having some problems with the second question. The lua script doesn't seem to recognize "abilities" in "abilities.strength.bonus". If I put "abilities.strength.bonus" as a source, with no script, the program returns the strength bonus value normally. But along the script, it is not recognized.

Here is my test:


<linkednumber name="atletstat">
<anchored>
<to>atletaptitude</to>
<position>right</position>
<offset>8,0</offset>
<size>
<width>32</width>
</size>
</anchored>
<frame>
<name>modifier</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<hideonvalue>0</hideonvalue>
<font>sheetnumber</font>
<displaysign />
<disabled />
<source>
<name>abilities.strength.bonus</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.bonus</name>
<op>+</op>
</source>
<script>
function isGreater(x, y)
if x and y and x > y then
return true;
else
return false;
end
end
function onInit()
if isGreater(abilities.strength.bonus.getValue(), abilities.constitution.bonus.getValue()) then
return 1;
else
return 2;
end
end
</script>
</linkednumber>


The console says:

Script Error: [string "charsheet_skills:atletstat"]:1:attempt to index global 'abilities' (a nil value)

gandhi39
August 19th, 2012, 07:15
Is it possible to do something like this?


<linkednumber name="atletstat">
<anchored>
<to>atletaptitude</to>
<position>right</position>
<offset>8,0</offset>
<size>
<width>32</width>
</size>
</anchored>
<frame>
<name>modifier</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<hideonvalue>0</hideonvalue>
<font>sheetnumber</font>
<displaysign />
<disabled />
<source>
<name>abilities.strength.bonus</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.bonus</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(math.max(calculateSources()));
end
</script>
</linkednumber>

I just don't know what to put in place of "calculateSources()". Maybe the "<op>+</op>" should be changed too somehow.


Or perhaps I could do something around this:


<linkednumber name="atletstat">
<anchored>
<to>atletaptitude</to>
<position>right</position>
<offset>8,0</offset>
<size>
<width>32</width>
</size>
</anchored>
<frame>
<name>modifier</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<hideonvalue>0</hideonvalue>
<font>sheetnumber</font>
<displaysign />
<disabled />
<source>
<name>abilities.strength.bonus</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.bonus</name>
<op>+</op>
</source>
<script>
function onSourceValue(source, sourcename)
if sourcename == "abilities.strength.bonus" then
if source.getValue() &gt; "abilities.constitution.bonus".getValue() then
return source.getValue();
elseif source.getValue() == "abilities.constitution.bonus".getValue() then
return source.getValue();
end
return 0;
elseif sourcename == "abilities.constitution.bonus" then
if source.getValue() &gt; "abilities.strength.bonus".getValue() then
return source.getValue();
end
return 0;
end

return super.onSourceValue(source, sourcename);
end
</script>
</linkednumber>

Zeus
August 19th, 2012, 10:58
abilities.strength.bonus (and similar) in this case is a logical reference which refers to the database node that holds the source data for the window control. The logical reference is from the node that the windowclass is bound to.

To reference these source nodes from within another control's script block, you need to first access the database node the current window is bound to:



<linkednumber name="atletstat">
<anchored>
<to>atletaptitude</to>
<position>right</position>
<offset>8,0</offset>
<size>
<width>32</width>
</size>
</anchored>
<frame>
<name>modifier</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<hideonvalue>0</hideonvalue>
<font>sheetnumber</font>
<displaysign />
<disabled />
<script>
function onInit()
local nodeWin = window.getDatabaseNode();
if DB.getValue(nodeWin, "abilities.strength.bonus", 10) >= DB.getValue(nodeWin, "abilities.constitution.bonus", 10) then
setValue(DB.getValue(nodeWin, "abilities.strength.bonus", 10));
else
setValue(DB.getValue(nodeWin, "abilities.constitution.bonus", 10));
end
end
</script>
</linkednumber>


In the example above the DB.getValue() package method is used to bind the the variable nodeWin to the node in the database the window is bound to. Take a look at the templates for number_charabilitybonus and the like to get an understanding of how these templates use the source and target tags.

Note: when you want to access data from another field, you can either reference the source database node or you can access the window control and it value for the other field using the window interface. FGII allows both approaches.

e.g. in your example where you want to source the data from either the strength or constitution bonus fields (depending upon which one is greater) you can do this by referencing:


window.[controlname].getValue();


window = is a FGII keyword reference for the parent window of the current control
<controlname> = the name of the control yo want to reference
getValue() = retrieves the value of the window control


So using the 3.5E ruleset as an example, you can do something like this:


<linkednumber name="atletstat">
<anchored>
<to>atletaptitude</to>
<position>right</position>
<offset>8,0</offset>
<size>
<width>32</width>
</size>
</anchored>
<frame>
<name>modifier</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<hideonvalue>0</hideonvalue>
<font>sheetnumber</font>
<displaysign />
<disabled />
<script>
function onInit()
if window.strengthbonus.getValue() > window.constitutionbonus.getValue() then
setValue(window.strengthbonus.getValue());
else
setValue(window.constitutionbonus.getValue());
end
end
</script>
</linkednumber>

Note: Working with the window interface is often faster than dealing with database access; however depending upon the scenario referring to the database often ensures consistency, particularly when dealing with data that can be accessed by players and the host .

gandhi39
August 19th, 2012, 22:13
Thanks! I tried the second option and it worked nice!

The only problem is if I update the ability bonus, I need to close and reopen the character sheet to update atletstat. Anyway to overcome that? Perhaps using a function other than onInit()?

gandhi39
August 19th, 2012, 23:32
Solved the problem with this:


<source>
<name>abilities.strength.bonus</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.bonus</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
if window.strbonus.getValue() > window.conbonus.getValue() then
setValue(window.strbonus.getValue());
else
setValue(window.conbonus.getValue());
end
end
</script>