PDA

View Full Version : Best method to pass variable between controls ?



Tabulazero
July 16th, 2019, 09:15
Hi there,

I want to ask a question for the collective wisdom of the forum. I have a window which has multiple controls. The window and the various controls each have various scripts running inside them. Right now, when I need to pass one variable from one script to another, I basically have my first script save the value in an invisible numbercontrol and then have the second script read the value from the invisible numbercontrol.

It works but I would like to know if there is a more elegant way to define a global variable which gets used by different functions across multiple <script></script> blocks ?

celestian
July 16th, 2019, 13:54
If the windows are within relatively close distance just reference them within them?

window.nameof-subwindow.buttoncontrol-name.updateSomething()

parentcontrol.window.updateOtherthing()

You can also make a globalscript, something like the CombatManager tho that is more for having access to re-usable functions that you pass variables to and they are generally not nailed to controls.

damned
July 16th, 2019, 13:57
store it in the DB and reference it there?

Trenloe
July 16th, 2019, 15:26
The general recommendation is to try to access data from the database, assuming your controls are storing info in the database - which probably 99% of controls do. If you’re not used to the FG database format and access it can seem a little daunting at first, but it’s well worth the time and effort to learn as this is very often the best way to code within FG.

You can directly reference the controls as celestian mentions, and that can work fine. My experience doing it that way is that the control hierarchy can sometimes be confusing and if you make minor changes to your GUI you very often have to make big changes to how your LUA accesses data. Going the database route is the lowest common denominator (i.e. you can get at data from within the window via controls, from within other windows and even when windows aren’t open), and I find it easier to troubleshoot as I can look directly at the FG database (an XML file) and see the exact structure and what is there.

Nose66
July 16th, 2019, 17:45
If you’re not used to the FG database format and access it can seem a little daunting at first, but it’s well worth the time and effort to learn as this is very often the best way to code within FG.

Concerning this, can you point us (noob FG developers, mainly me) to some documentation (with examples) on how to access the DB? I have been able to "hack" some things (mainly using DB.getValue()), but others (like iterating over a list), I can't figure out.

Trenloe
July 16th, 2019, 17:54
Concerning this, can you point us (noob FG developers, mainly me) to some documentation (with examples) on how to access the DB? I have been able to "hack" some things (mainly using DB.getValue()), but others (like iterating over a list), I can't figure out.
Introduction: https://www.fantasygrounds.com/wiki/index.php/Developer_Guide_-_Rulesets_-_Database

Two main APIs: https://www.fantasygrounds.com/refdoc/DB.xcp and https://www.fantasygrounds.com/refdoc/databasenode.xcp

There are plenty of examples in FG code - extract the .pak files to a temporary directory (not your <FG app data> directory) and use Notepad++ (or a similar text editor) that allows find in files. Find in files for getDatabaseNode - as this is usually one of the first API commands that starts FG database access.

To iterate over a list, start with the database node that contains the "list" records - these records are known as children in FG database terms. Then use getChildren (this is available in both the DB package and the databasenode object) to return a LUA table of children databasenode objects, and iterate through the table using standard LUA for key,value in pairs...

An example of this is in the CoreRPG PC inventory code - that runs some code when the "carried" icon is changed. It steps through each child in the inventorylist node of the nodeChar (character) databasenode - checks some values (getValue) in each child and sets a carried value (setValue) if appropriate.

This code is in the CoreRPG campaign\scripts\char_invlist.lua file, reproduced here for convenience:


function onCarriedChanged(nodeCarried)
local nodeChar = DB.getChild(nodeCarried, "....");
if nodeChar then
local nodeCarriedItem = DB.getChild(nodeCarried, "..");

local nCarried = nodeCarried.getValue();
local sCarriedItem = StringManager.trim(ItemManager.getDisplayName(node CarriedItem)):lower();
if sCarriedItem ~= "" then
for _,vItem in pairs(DB.getChildren(nodeChar, "inventorylist")) do
if vItem ~= nodeCarriedItem then
local sLoc = StringManager.trim(DB.getValue(vItem, "location", "")):lower();
if sLoc == sCarriedItem then
DB.setValue(vItem, "carried", "number", nCarried);
end
end
end
end
end

onEncumbranceChanged();
end

Tabulazero
July 18th, 2019, 08:11
Dumb question: is it possible to declare a global variable that gets used across multiple functions ?

When I define a variable without the "local" tag ahead, I get an error message: trying to index VariableName at nil value. I thought that in LUA if you omitted local your variable would de facto be treated as global variables.

damned
July 18th, 2019, 08:48
Use LOTS of
Debug.console("description: ", variablename);
entries throughout your code while testing.
these will output to the Console (/console) when the Console is open
this will help you see when a variable is or isnt holding the expected data

Trenloe
July 18th, 2019, 15:08
Dumb question: is it possible to declare a global variable that gets used across multiple functions ?

When I define a variable without the "local" tag ahead, I get an error message: trying to index VariableName at nil value. I thought that in LUA if you omitted local your variable would de facto be treated as global variables.
Not using local means that the variable is "global" to the LUA file you're in.

There are two ways of using data that is global across FG code:
1) Store it in the FG database. This has many advantages - one of which is that the value is persistent across FG restarts, which may help your use-case or may not be needed.
2) Store data in a global script package: https://www.fantasygrounds.com/wiki/index.php/Developer_Guide_-_Rulesets_-_Scripting#Global_Script_Packages

Tabulazero
July 18th, 2019, 15:58
Thanks a million.

The issue I am running into is that I am scripting a bespoke pool system. The game system works as follow. You start by rolling a d20 vs a difficulty. Not a problem so far. Once the result is know, the player can build a pool of D6 from his character pool and from the party pool to roll and keep the highest number. The GM also get to build a pool of D6 from the GM pool whose best result will add to the difficulty.

In FG terms this means that I have 3 roll commands (one for the player pool, one from the party pool and one from the GM pool) to contend with and we know that FG is asynchronous.

So I thought I would store the number of pool that need to be rolled (because the GM or the player may well chose not to roll additional d6) somewhere and have each roll result function decrease the said variable until it hit 0 which would then trigger a specific output script at the same time.

The problem I encounter is that when I test with only one pool in any configuration it works like a charm but as soon as I try to do all three pools it breaks down. What I guess is happening is because of the time it takes to read/write in the DB I get a conflict with multiple function trying to read the same DB field and decrement it.

Hence I am going to try the global script package because I guess it must be faster and I may avoid this conflict. If it does not work, I try rolling all the D6 together so to only have one result function instead of three.

Trenloe
July 18th, 2019, 16:50
The problem I encounter is that when I test with only one pool in any configuration it works like a charm but as soon as I try to do all three pools it breaks down. What I guess is happening is because of the time it takes to read/write in the DB I get a conflict with multiple function trying to read the same DB field and decrement it.
The issue you may have with database fields is that only the GM and one other player (if assigned as the owner) can change a database field. This carries into FG scripts running on a player instance - they have the same rights access rights. So, it's possible if you're running code on the GM and player side, that it's not updating the DB field because of ownership. If you have the FG console open on each instance, you'll see warnings about not being able to update fields because they aren't owned - it's not reported as an error, so the console won't pop up.

The usual way to address this is to use OOB messaging - to send the request to the GM instance to update the database.

The issue you'll have with global packages is that they are just "global" to each instance of FG - i.e. any variables stored in them are only within that instance. You could use OOB messaging to run the code on the GM side and access a global package there (so the GM global package stores the data), but if you're doing that you may as well use OOB messaging to read/write to the database and keep the database as the master data store.