PDA

View Full Version : Run script function in another script?



GrimmSpector
February 27th, 2016, 18:03
Hello, I have a control in my sheet that has a self updating function, addressing a widget within it, and to update that widget based on other input from another control, I'm trying to call that controls method from the script.

I assumed address it hierarchically would function, so I have a control under the windowclass "charsheet_notes" that's in a subwindow "notes" on the character sheet, so I've attempted:

window.charsheet_notes.<name>.<function>(<value>);

Where the <name> is the name property of the control that contains the function, and function is the defined function (which runs just fine from the control itself), and value is a local value in the current controls script being passed.

Soooooo....what am I doing wrong? How can I pull this off? Thanks!

Trenloe
February 27th, 2016, 20:14
You've more than likely not got the windows hierarchy 100% right. Use some Debug.console messages to output the class of a windows you're looking at using getClass (https://www.fantasygrounds.com/refdoc/windowinstance.xcp#getClass) and then use getName (https://www.fantasygrounds.com/refdoc/windowcontrol.xcp#getName) against controls to see if the control you're looking at is the right one.

I have to do this regularly as it's not always obvious what the exact GUI hierarchy is and it can be different depending on where in the hierarchy the script is running.

Also, if there is a subwindow in you're hierarchy you'll need to use the subwindow variable. See "Script block scope" here: https://www.fantasygrounds.com/modguide/scripting.xcp and the description here: https://www.fantasygrounds.com/refdoc/subwindow.xcp

GrimmSpector
February 27th, 2016, 21:51
Ahhhhhhh ok, I'll take a look and let you know if it brings success, nice to know about the debug console method, I've just been using print...

GrimmSpector
February 27th, 2016, 22:12
Ok, it tells me my Class in both cases is "charsheet_notes", which is the subwindow, base from CoreRPG, haven't changed that part:



<subwindow name="notes">
<bounds>0,65,-1,-20</bounds>
<class>charsheet_notes</class>
</subwindow>


and the names of my controls from getName are "affiliation" which is the control being changed, and "affiliation_image" which is the control who's script function I want to call.

I've tried:
window.charsheet_notes.affiliation_image.<function>
subwindow.affiliation_image.<function>
notes.charsheet_notes.affiliation_image.<function>
window.notes.charsheet_notes.affiliation_image.<function>
subwindow.affiliation_image.<function>

a bunch of others, and I always get a nil on charsheet_notes or notes...except the last one which gives it to me on subwindow...

Perhaps I'm misunderstanding the variable, but I thought it functioned like the "window" variable.

So the complete "class" in theory, could be:
window charsheet
subwindow notes
class charsheet_notes
control affiliation_image
function

So I'm a bit confused.

Trenloe
February 27th, 2016, 22:34
So, if window.getClass() is returning "charsheet_notes" then the windows variable points directly to the "notes" subwindow.

I don't know how the rest of your XML is laid out, but perhaps window.affiliation_image will get you to the affilitation_image control? Assuming that control is at the top level within charsheet_notes?

If you can't access the script function within the control, have you tried just accessing the control itself - perhaps window.affiliation_image.getName() just to see if you are getting to the control?

GrimmSpector
February 28th, 2016, 02:06
Good thought on how to troubleshoot, I gave it a try with window.affiliation_image.<function> and it worked! looks like I was over complicating things a tad. Thanks very much!

GrimmSpector
March 4th, 2016, 00:22
I've been having success with this, but am having a similar problem, I need to reference an item in my main record sheet, who's control is in 'charsheet_main', and I'm able to access things within it from itself with window.<anycontrol> and such, I have a sub windowclass element within called 'charsheet_attributes', and it too works fine for internal controls and calues with window.<whatever>; the hitch is I need to call a script function on a control inside the 'charsheet_main' class from 'charsheet_attributes', I've tried:

charsheet_main.control.function - But it tells me that charsheet_main is a nil global and can't be indexed, and if I try to go to the root window, "charsheet" as the whole set of window classes is in a subwindow, it seems that doesn't work either. Any attempt to use subwindow tells me that subwindow is nil.

So I'm at a loss as to get into it, if we were too look at it as a hierarchy, it should look like, and I'll use xml as an example:

<charsheet>
<charsheet_main>
<charsheet_attributes>
<somecontrol>
<script>calls to the other classes controls function</script>
</somecontrol>
</charsheet_attributes>
<someothercontrol>
<script>something I want to call</script>
<someothercontrol>
<charsheet_main>
<charsheet>

It's as per the corerpg ruleset, using the main sheet, with new controls, so everything is within the "main" subwindows sheet. Not sure what it is that I'm doing wrong in the calls. But any help would be spectacular. I've got many things on the sheet working beautifully now, but this is a sticking point for updates and levelling.

Moon Wizard
March 4th, 2016, 01:30
When tracking up or down through windowlist/subwindow controls, there are extra references that need to be made.

The windowinstance within a subwindow control is referred to by "subwindow". (within the control script scope)
The subwindow control that contains a windowinstance is referred to by "parentcontrol". (within the windowclass/windowinstance script scope)

For the windowlist control, you can get all the windowinstances within the list by calling the getWindows() function. (within the control script scope)
The windowlist control that contains a windowinstance is referred to by "windowlist". (within the windowclass/windowinstance script scope)

I don't know what your exact structure looks like, but the call might look something like this (assuming that your calling code is in control embedded in a subwindow, and you are trying to find a control in the parent window of the subwindow).

window.parentcontrol.window["<target control name>"].<function to call>

Sometimes I find it easier to bubble all these types of interactions up to the windowclass scripts from the controls, and let the windowclass scripts handle the burrowing up/down.

Regards,
JPG

GrimmSpector
March 4th, 2016, 03:46
That's not a bad idea, I like it. However, maybe a structure problem here, I tried what you suggested, and get:

Script Error: [string "XP"]:1: attempt to index global 'parentcontrol' (a nil value)

Here's the modified line of code.


window.parentcontrol.window["standard"].updatePips();

GrimmSpector
March 4th, 2016, 04:01
Since I'm having trouble here, I assume you'd recommend setting up a set of functions in the script of the record_char (corerpg base), that call functions within various controls/windows down the hierarchy, and call up to them from inside? Calling down is something we've discussed before, which should be easy enough to figure. Calling up seems to be a bit more interesting, since that parentcontrol field is seeming to be not working as expected, any advice to follow this track would be helpful, thanks again Moon!

Moon Wizard
March 4th, 2016, 05:48
The "parentcontrol" only exist in the windowclass scripts when the window has been instantiated within a subwindow. The "parentcontrol" reference equates to the subwindow control itself. If parentcontrol doesn't exist, then this means that the window is not in a subwindow control.

I was assuming your structure was like this, but without seeing all the files, I'm just guessing.

The information above about referencing up/down for subwindows/windowlists is what is important. You just need to look at it as a hierarchy.

As an example:
For the 3.5E character sheet, you have a "charsheet" windowclass, which contains 7 subwindows ("main", "combat", "skills", "abilities", "inventory", "notes", "actions"). The "combat" subwindow contains a windowinstance of the "charsheet_combat" windowclass. The "notes" subwindow contains a windowinstance of the "charsheet_notes" windowclass.

If I wanted the "age" field in the "charsheet_notes" windowinstance to change the "baseattackbonus" field in the "charsheet_combat" windowinstance; then I would use this reference in the script for the "age" field:

window.parentcontrol.window.combat.subwindow.basea ttackbonus

The first 3 parts of this call are going up the chain to the "charsheet" windowinstance, and the last 3 are navigating down the chain to the "baseattackbonus" control within the "charsheet_combat" windowinstance within the "combat" subwindow.

Regards,
JPG

damned
March 4th, 2016, 06:41
firstly the easiest way is almost always to reference it from the DB because the DB is always in the same location - you just need to know which actor is doing whatever it is you are doing.

but check this thread I think it might help you find the right syntax -

https://www.fantasygrounds.com/forums/showthread.php?21028-Help-With-Accessing-Controls-On-Other-(Peer)-Subwindows

GrimmSpector
March 4th, 2016, 16:15
firstly the easiest way is almost always to reference it from the DB because the DB is always in the same location - you just need to know which actor is doing whatever it is you are doing.

but check this thread I think it might help you find the right syntax -

https://www.fantasygrounds.com/forums/showthread.php?21028-Help-With-Accessing-Controls-On-Other-(Peer)-Subwindows

I'm not referencing a database value, I'm calling a function.

Ok Moon, that's weird since both of these things are in the "main" subwindow in the charsheet, the one set of controls are within a windowlist, so here's the code for the two parts:

Origin of the call, within the windowlist, inside the "main" sheet.


<?xml version="1.0" encoding="iso-8859-1"?>

<root version="1.0">
<windowclass name="charsheet_attribute">
<sizelimits>
<maximum>
<height>23</height>
</maximum>
<minimum>
<height>23</height>
</minimum>
</sizelimits>
<sheetdata>
<numbercontrol name="order">
<bounds>0,0,0,0</bounds>
<invisible />
</numbercontrol>
<stringcontrol name="label">
<anchored position="insidetopleft" offset="0,6" width="75" height="15" />
<font>sheetlabel</font>
<static />
</stringcontrol>
<simplenumber name="score">
<anchored to="label" position="rightlow" offset="10,0" width="30" />
<font>sheetnumber</font>
<frame>
<name>fieldlight</name>
<offset>5,4,5,4</offset>
</frame>
<script>
function onInit()
emptymodifierWidget = addBitmapWidget("tempmod");
emptymodifierWidget.setPosition("topright", 0, 0);
super.onInit();
end
</script>
</simplenumber>
<simplenumber name="link">
<anchored to="score" position="right" offset="15,0" width="30" />
<font>sheetnumber</font>
<frame>
<name>fieldlight</name>
<offset>5,4,5,4</offset>
</frame>
<displaysign />
<script>
function onInit()
emptymodifierWidget = addBitmapWidget("tempmod");
emptymodifierWidget.setPosition("topright", 0, 0);
super.onInit();
end
</script>
</simplenumber>
<simplenumber name="XP">
<anchored to="link" position="righthigh" offset="15,-1" width="30" />
<font>sheetnumber</font>
<frame>
<name>fielddark</name>
<offset>5,4,5,4</offset>
</frame>
<keyeditframe>
<name>fieldfocus</name>
<offset>5,4,5,4</offset>
</keyeditframe>
<target>target</target>
<script>
function onInit()
if getValue() == 0 then
setValue(100);
onValueChanged();
end
end

function onValueChanged()
local nodeWin = window.getDatabaseNode();
local cnName = nodeWin.getNodeName();
local nScore = DB.getValue(cnName .. ".XP") / 100;
local nLink = 0;
local aPath = StringManager.split(cnName, ".");
print(aPath[4]);
if aPath[4] == "BOD" then
DB.setValue(aPath[1] .. "." .. aPath[2] .. ".standard", "number", nScore * 2);
print(tostring(nScore * 2));
if not getDatabaseNode().getChild("standard_current") then
local node = getDatabaseNode().createChild("standard_current");
end
DB.setValue(aPath[1] .. "." .. aPath[2] .. ".standard_current", "number", nScore * 2);
print(window.getClass());
window.parentcontrol.window["standard"].updatePips();
end
if nScore &gt; 11 then
nLink = nScore % 3;
elseif nScore == 10 then
nLink = 2;
elseif nScore &gt; 6 then
nLink = 1;
elseif nScore &gt; 3 then
nLink = 0;
elseif nScore &gt; 1 then
nLink = -1;
elseif nScore == 1 then
nLink = -2;
else
nLink = -4;
end
DB.setValue(cnName .. ".link", "number", nLink);
DB.setValue(cnName .. ".score", "number", nScore);
end
</script>
</simplenumber>
</sheetdata>
</windowclass>
</root>


Main sheet will be in the following post, forums tells me this is too long....

GrimmSpector
March 4th, 2016, 16:16
And here's the rest, since it was too long the main sheet itself:



<?xml version="1.0" encoding="iso-8859-1"?>

<!--
Please see the license.html file included with this distribution for
attribution and copyright information.
-->

<root>
<include source="charsheet_listclasses.xml" />
<windowclass name="charsheet_main">
<margins control="0,0,0,2" />
<sheetdata>
<frame_char name="attributesframe">
<bounds>15,0,250,223</bounds>
</frame_char>
<stringcontrol name="score_label">
<anchored to="attributesframe" position="insidetopleft" offset="98,8" width="35" />
<font>sheetlabelinline</font>
<static>Score</static>
<center />
</stringcontrol>
<stringcontrol name="link_label">
<anchored to="score_label" position="right" offset="10,0" width="35" />
<font>sheetlabelinline</font>
<static>Link</static>
<center />
</stringcontrol>
<stringcontrol name="xp_label">
<anchored to="link_label" position="right" offset="18,0" width="15" />
<font>sheetlabelinline</font>
<static>XP</static>
<center />
</stringcontrol>
<windowlist name="attributes">
<anchored>
<to>attributesframe</to>
<position>over</position>
<offset>-15,-20</offset>
</anchored>
<class>charsheet_attribute</class>
<datasource>.attributes</datasource>
<noscroll />
<script>
STR = nil;
BOD = nil;
RFL = nil;
DEX = nil;
INT = nil;
WIL = nil;
CHA = nil;
EDG = nil;

local order = 1;

function onInit()
STR = addEntry("STR","Strength");
BOD = addEntry("BOD", "Body");
RFL = addEntry("RFL", "Reflex");
DEX = addEntry("DEX", "Dexterity");
INT = addEntry("INT", "Intelligence");
WIL = addEntry("WIL", "Willpower");
CHA = addEntry("CHA", "Charisma");
EDG = addEntry("EDG", "Edge");
applySort();
end

function addEntry(name, label)
local node = getDatabaseNode().getChild(name);
local win = nil;
if not node then
node = getDatabaseNode().createChild(name);
end
for i,w in ipairs(getWindows()) do
if w.getDatabaseNode().getName()==name then
win = w;
end
end
if win then
win.label.setValue(label);
win.order.setValue(order);
order = order + 1;
end
return win;
end

function onSortCompare(w1, w2)
return (w1.order.getValue() &gt; w2.order.getValue());
end
</script>
<skipempty />
</windowlist>
<frame_char name="combatdataframe">
<anchored to="attributesframe" position="belowleft" offset="0,0" width="485" height="265" />
</frame_char>
<stringcontrol name="conditionmonitor_label">
<anchored to="combatdataframe" position="insidetopleft" offset="20,10" />
<font>sheetlabel</font>
<static>Condition Monitor</static>
</stringcontrol>
<stringcontrol name="standarddamage_label">
<anchored to="combatdataframe" position="insidetopleft" offset="10,25" height="12" width="90" />
<font>sheetlabelinline</font>
<static>Standard Damage</static>
</stringcontrol>
<stringcontrol name="standarddamage_row1">
<anchored to="combatdataframe" position="insidetopleft" offset="10,37" height="12" width="5" />
<static></static>
</stringcontrol>
<stringcontrol name="standarddamage_row2">
<anchored to="combatdataframe" position="insidetopleft" offset="10,49" height="12" width="5" />
<static></static>
</stringcontrol>
<simplenumber name="standard">
<anchored to="combatdataframe" position="insidetopright" offset="10,10" />
<readonly />
<frame>
<name>fieldlight</name>
<offset>15,4,5,4</offset>
</frame>
<script>
function onInit()
for i=1,20 do
local newPip = window.createControl("health_pip", "standard" .. tostring(i));
newPip.setAnchor("left", "standarddamage_row1", "right", "relative", -4);
newPip.setAnchor("top", "", "top", "relative", 49);
end
for i=21,40 do
local newPip = window.createControl("health_pip", "standard" .. tostring(i));
newPip.setAnchor("top", "", "top", "relative", 67);
newPip.setAnchor("left", "standarddamage_row2", "right", "relative", -4);
end
updatePips();
end

function updatePips()
local curstd = window.standard_current.getValue();
local maxstd = window.standard.getValue();

print(window.getClass());
if curstd &lt; 1 then
for i=1,maxstd do
window["standard" .. tostring(i)].setHealth("pip_empty");
end
elseif curstd == maxstd or curstd &gt; maxstd then
for i=1,maxstd do
window["standard" .. tostring(i)].setHealth("pip_filled");
end
else
for i=1,curstd do
window["standard" .. tostring(i)].setHealth("pip_filled");
end
for i=curstd + 1,maxstd do
window["standard" .. tostring(i)].setHealth("pip_empty");
end
end

end
</script>
</simplenumber>
<simplenumber name="standard_current">
<anchored to="standard" position="left" offset="5,0" />
<script>
function onValueChanged()
window.standard.updatePips();
end
</script>
</simplenumber>
</sheetdata>
</windowclass>

<windowclass name="char_main_category_header">
<margins control="0,0,0,5" />
<frame>rowshade</frame>
<sheetdata>
<genericcontrol name="leftanchor">
<anchored height="0" width="0">
<top offset="5" />
<left offset="10" />
</anchored>
</genericcontrol>

<genericcontrol name="rightanchor">
<anchored height="0" width="0">
<top offset="5" />
<right offset="-10" />
</anchored>
</genericcontrol>

<stringfield name="label">
<anchored>
<top offset="5" />
<left offset="30" />
<right offset="-30" />
</anchored>
<stateframe>
<keyedit name="fieldfocus" offset="7,5,7,5" />
<hover name="fieldfocus" offset="7,5,7,5" hidereadonly="true" />
<drophilight name="fieldfocus" offset="7,5,7,5" hidereadonly="true" />
</stateframe>
<font>reference-b-large</font>
<multilinespacing>20</multilinespacing>
<center />
</stringfield>
</sheetdata>
</windowclass>
</root>


Hope this can lead us to the right answer!

Moon Wizard
March 4th, 2016, 18:31
I think the problem is that I was assuming that charsheet_attribute was within a subwindow, but it's actually inside a windowlist. That's why I was saying that it depends on your structure.

Within a windowlist, the reference would be:

window.windowlist.window["standard"].updatePips();

JPG

GrimmSpector
March 4th, 2016, 23:22
Fantastic! Thanks so much Moon!

I think I'll start gathering functions into the top level classes like you mentioned to make this sort of thing easier, in the future. But this is a lot of good progress for the GUI!