PDA

View Full Version : Scripting



Czarisyn
July 24th, 2010, 06:07
For the past several days, I've been working on learning to script.
So far I am pleased on how much I've gotten accomplished.

I've hit a little snag in my design where I am going to need to do something similar to the linkednumbers from the d20 ruleset.

After looking through the xml and scripts, I've got a question that the answer is probably right in front of me and I'm just blind.

in the script template_linkednumber.lua, the variables:
sources = {};
ops = {};

are consfusing me. In the charsheet_main.xml, the tags are:
<source>
<op>

How does it fill the array?

Also, is source and sourcename a global variable too because the onInit() didn't have it defined yet it called for it.

Again, probably really n00bish, but if I don't ask, I'd know for sure.

Thanks for the help in advance

tdewitt274
July 24th, 2010, 13:02
Actually, those are Lua's Tables (https://www.lua.org/manual/5.1/manual.html#2.5.7).

Usually, from what I've seen, if they're defined in the file, they're used within the file. However, there may be times where it is declared within that file, but used in another place. Since you're in a Template Lua script file, it may be in some other place entirely.

My suggestion would be to download Notepad++ and do a search on files within the folder for the table names. This should give you a better idea of where they're being filled.

Disclaimer: I'm a Lua scripting noob as well.

Zeus
July 24th, 2010, 15:59
Its not uncommon in templates to allow the calling function to define parameters that the underlying templated code exploits to populate and track control parameters.

In this instance it looks like the template XML for the class allows you to define the <source> for a numbercontrol i.e. a database field or perhaps another control in the same class.

In the underlying templated code, it looks like the template defines and maintains an internal table for storing control parameters. I suspect that somewhere the <source> parameter will be added to the source table (source = {}). This way the control can maintain multiple sources or a history of them if so desired.

Look for the assignment call which might something like:


table.insert(sources, class.source[1])

Czarisyn
July 29th, 2010, 17:07
Okay, next block

creating a template,
the new tags I make for example <ability>
in the script, to grab it, I should use "ability" as the variable to call, right?

Zeus
July 29th, 2010, 17:30
It depends upon the script's calling context.

if its from a script supporting the windowclass itself (thats using the template) then you would simply call 'ability' without the quotes from the script.

If its from a script supporting another control from within the windowclass (thats using the template) then you would simply call 'window.ability' without the quotes from the script.

If its a script from within the template itself you reference it as it is defined from within the template.

Czarisyn
July 29th, 2010, 17:56
so in the actual xml, the <ability>thisone</ability> is still "ability" in the script (or window.ability) without quotes.

Its not an array unless there are multiple properties, correct?

Zeus
July 29th, 2010, 17:57
I believe so, yes.

StuartW
July 29th, 2010, 18:07
I believe all tag values become arrays from script, even if there is only one instance, so you'd need to reference ability[1] in this instance. There is some info on this in the FG docs, here (https://www.fantasygrounds.com/modguide/scripting.xcp).

Stuart

StuartW
July 29th, 2010, 18:08
BTW Dr Z, what is a SageViscount? *bemused*

Czarisyn
July 29th, 2010, 21:22
okay, is it possible to use a string as a part of a database node.

for instance "stats."..ability..".die"

is there a way to reference it?

Oberoten
July 29th, 2010, 21:22
I ... have to wonder as well?

- Obe

Zeus
July 29th, 2010, 22:16
BTW Dr Z, what is a SageViscount? *bemused*


I have no idea? Is it something I can change in my profile or something?

Oberoten
July 29th, 2010, 22:59
Some new form of Sage it sounds like.

... question is are you a mutant sage or a mutant green leafy thing?

- Obe

StuartW
July 29th, 2010, 23:12
The approach for accessing controls on a window is different to accessing nodes in the database, and it is explained a bit here (https://www.fantasygrounds.com/forums/showthread.php?p=74723#post74723).

Broadly speaking, window-based access uses control names separated by periods, such as abilities.strength.modifier and database-based access uses datapaths such as getDatabaseNode(".abilities.strength.modifier"), and navigating up one level using window syntax would be something like mycontrol.window, whereas using database syntax would be getDatabaseNode("mycontrol").getParent().

Window-based syntax follows the screen structure and allows access to non-persistent controls, whereas database-based syntax allows you to cross between different windows/tabs, but gives you no access to non-bound controls.

Hope that helps a bit (at least),

Stuart

Czarisyn
July 31st, 2010, 02:18
trying to reference a "dice" type databasenode from a numberfield script block.

I don't think that's possible anymore cause getDice() is coming back nil when the databasenode location is correct

any ideas?

StuartW
July 31st, 2010, 05:31
How are you accessing the dice database node from within the numberfield script? Could you post a few lines of script?

Thanks

Stuart

Czarisyn
July 31st, 2010, 16:31
I made some progress

I made the diecontrol create/set a new node that converts the die into a number

from there, with a few getParent() and then getChild variables, I can pull in the correct value.

now, creating a die table that can be used as drag data is the next step.

Czarisyn
July 31st, 2010, 16:43
and I have a winner, its working
just concatenated a "d" with the new dietype node and applied it to the dice list

Czarisyn
August 1st, 2010, 01:10
okay, making a template
set the merge rule to add

XML



<source>
<name>abilityA</name>
</source>
<source>
<name>abilityB</name>
</souce>






function onHover
print(source[1].name[1]); <--works
print(source[2].name[1]); <-- returns nil
end


I thought, from reading the modding guide and XML/LUA Ref guide, that my code was sound but its not. What am I missing?

Moon Wizard
August 1st, 2010, 03:33
What does your template look like?

It should have a:
<source mergerule="resetandadd" />

Cheers,
JPG

Czarisyn
August 1st, 2010, 04:19
What does your template look like?

It should have a:
<source mergerule="resetandadd" />

Cheers,
JPG


<source mergerule="add" />

Moon Wizard
August 1st, 2010, 08:34
I've never used just the "add" value for mergerule before. Perhaps the resulting structures aren't in the same format as they are when generated by "resetandadd".

You will need to explore the structure of the data in the script using type function do determine the type of each variable and drill in on the tables. There is a function in the 4E chatmanager.lua that will attempt to output variables that you can try to use.

Cheers,
JPG

Czarisyn
August 1st, 2010, 17:37
I'm getting errors because a function is trying to pull info on an empty diefield.

I'm trying to figure out how to make it only call when the return is not nil, so far it keep erroring.


as for the template, I've tried add and resetandadd and still notta, talk about a headache

Moon Wizard
August 1st, 2010, 19:50
You could just try validating the variables to make sure they exist before you use them. Then, you could catch situations where the variable hasn't been instantiated.

For example,
if source then
if source[1] then
if source[1].name then
if source[1].name[1] then
...
end
end
end
end

You essentially would check each step in the chain to make sure it exists. The access to the XML tag structure is a little complex, but it allows template merging options that couldn't be accommodated otherwise.

Regards,
JPG

Czarisyn
August 1st, 2010, 20:22
You could just try validating the variables to make sure they exist before you use them. Then, you could catch situations where the variable hasn't been instantiated.

For example,
if source then
if source[1] then
if source[1].name then
if source[1].name[1] then
...
end
end
end
end

You essentially would check each step in the chain to make sure it exists. The access to the XML tag structure is a little complex, but it allows template merging options that couldn't be accommodated otherwise.

Regards,
JPG

I got the template working, its adding the sources now, just had to change the actual tag. Apparently using source as a tag when it also is used to point to the data source of the main tag is a bad idea.

going to try the validation, I've tried it before with just the



if variable then
stuff
end


but it still went through.

Czarisyn
August 2nd, 2010, 21:37
you know what I hate?
inconsistency when it comes to programming.
same entries, yet 1 is having issues
blarg!

just venting

Czarisyn
August 3rd, 2010, 00:50
how do you detect an update from a field? from what I've read, the onUpdate() only works on databasenodes.

StuartW
August 3rd, 2010, 05:46
onValueChanged is triggered when a control (numbercontrol or stringcontrol) changes, but it is used slightly differently to onUpdate.

onUpdate is a hanlder, which you set by assigning a function to it (myNode.onUpdate = myFunction). onValueChanged is an event, which you manage by having a script with the same name in the control's script tag, so if you wanted to manage the onValueChanged event for myControl, you'd create an onValueChanged function in myControl's script block.

Stuart

Czarisyn
August 3rd, 2010, 14:06
okay, something really odd is happening

When onDrop fires, I have it set to run my function that sets a node ('dietype')
to a number that's equal to the die.

The function works perfectly when onInit and onHover fires, but when onDrop fires, getDice() right after I drop a new die on the control, the return is an empty table when it should be the new dice.

Why would getDice() be empty after I drop a new die?

Moon Wizard
August 3rd, 2010, 20:54
onDrop actually fires before the drop is accepted. This allows rulesets to override the default FG behavior for drop events.

The dice will be in the dragdata object passed into the onDrop handler.

Cheers,
JPG

Czarisyn
August 4th, 2010, 01:14
does the dragdata reset after being used once?
it seems that when I try sending it to another function and it comes back nil

Moon Wizard
August 4th, 2010, 02:12
I'm not sure I'm following you. Why don't you post your onDrop code, and the function it calls?

JPG

Czarisyn
August 4th, 2010, 02:23
function onDrop(button,x,y,dragdata)
setDice(dragdata);
dieType(dragdata);
end




function dieType(dice)
local m = {};
if dice then
m = d;
else
m = getDice();
end
if m[1] then
local x = getDatabaseNode().getParent();
local y = x.createChild("dietype","number");
local n = string.len(m[1]);
local z = string.sub(m[1],2,n);
y.setValue(z);
end
end

Moon Wizard
August 4th, 2010, 03:11
There are definitely some issues with that code:
* setDice() expects a table of dice entries, not a dragdata object. This means that setDice() is not working.
* If you are handling the onDrop event, then you need to "return true" to avoid the default handling. (i.e. I expect you would get double dice)
* You are not checking to make sure that the dragdata object is of type "dice".

Also, probably a better way to handle is to add an onUpdate handler to the databasenode of the dicefield. Then, when the dice are changed, you can update your "dietype" field.

Try looking at the "jpgdiefield" template in the 4E ruleset for an example. In that template, I check to see when the databasenode is updated to determine whether the field is empty or not. Then, I display dice shadows in the field if it is empty.

Cheers,
JPG

Czarisyn
August 4th, 2010, 03:25
having the setDice(dragdata) makes the control only accept 1 die type which is what I want.

i'll check the script you mentioned, once I get some sleep.

thanks

Moon Wizard
August 4th, 2010, 03:52
Definitely take a look after you get some sleep.

The way you are using setDice is not safe, and you need to look at other solutions. If you only want to accept one die type, then you will need to:
* Have an onDrop handler that checks for type "dice".
* If not dice, then just return true to keep the default processing from happening.
* Inspect the dice within dragdata.getDieList to make sure that they are all the right type of dice.
* Decide what to do if some or all of the drag dice are not the right/same type (multiple dice types can be dragged together)
* Decide what to do if the drag dice are not the same type as the field dice
* Get the current dice stored in the database
* Add the new dice from dragdata.getDieList
* Set the dice value of the database to the new dice value.
* Update your dietype variable based on the new dice value
* Return true to let FG that you handled the processing of this node.

Cheers,
JPG

Czarisyn
August 5th, 2010, 23:18
Try looking at the "jpgdiefield" template in the 4E ruleset for an example. In that template, I check to see when the databasenode is updated to determine whether the field is empty or not. Then, I display dice shadows in the field if it is empty.

Cheers,
JPG

I see that you don't use the onDrop function on that all. You are using the database node update to trigger if I am reading it right. Correct?

Moon Wizard
August 7th, 2010, 06:36
Yes, I use the onUpdate handler for the database node tied to the diefield in order to trigger the changes I want to make. This is because the diefield control does not have an onValueChanged event.

Cheers,
JPG

Czarisyn
August 7th, 2010, 16:55
Yes, I use the onUpdate handler for the database node tied to the diefield in order to trigger the changes I want to make. This is because the diefield control does not have an onValueChanged event.

Cheers,
JPG

I'm trying to trace all the functions and what they do. I see that self.OnValueChanged() gets used but there is no function written for it in the template_common.xml or in the charsheet_combat.xml

Is there a default action that it does or is the definition written somewhere else?

Czarisyn
August 8th, 2010, 05:28
Eureka!

I got the script to work exactly the way I want it. Took a little finessing but its pulling the right numbers and updating correctly with only allowing 1 die to fill the diefield.

Thanks for the help :D