View Full Version : Consolidating code
tdewitt274
December 11th, 2009, 03:32
I'm messing around building my first ruleset and was wondering how I could reduce on some code.
I have 6 ability scores that use the below functions ("variable" names in pipes). Since they all use the same code, I was wondering how to consolidate them.
<script>
function recalc()
local attrib = window.getDatabaseNode().getChild("charsheet.|AttributeName|.value").getValue();
setValue(onValue(attrib));
end
function onInit()
local attrib = window.getDatabaseNode().getChild("charsheet.|AttributeName|.value");
if attrib then
attrib.onUpdate = recalc;
recalc();
end
end
function onValue(result)
return math.floor(result/2);
end
</script>
<script>
function recalc()
local attrib = window.getDatabaseNode().getChild("charsheet.|AttributeName|.value").getValue();
local val = 0;
if attrib > 18 then
val = 5;
elseif attrib > 16 then
val = 4;
elseif attrib > 14 then
val = 3;
elseif attrib > 12 then
val = 2;
elseif attrib > 10 then
val = 1;
elseif attrib > 6 then
val = 0;
elseif attrib > 4 then
val = -1;
else
val = -2;
end
setValue(val);
end
function onInit()
local attrib = window.getDatabaseNode().getChild("charsheet.|AttributeName|.value");
if attrib then
attrib.onUpdate = recalc;
recalc();
end
end
</script>
Here's what I was looking to do. I would like to pass the AttributeName to the function and update the two fields that are modified by changing the Attribute. For example, in D&D 4.0, Updating the DEX figures the Modifier and updates any modifiers for skills relating to it (ie, Balance).
Also, I want to be sure to put the information into the "correct" place. Currently, I have it in my "charsheet.xml" file. I'm still messing around with creating the sheet, so I haven't taken it to the axe yet (not sure how yet, but I'll figure it out ;)). I've looked at other rulesets and it seems that a good place to put it would be "charsheet.lua" but I'm not sure what steps to take to ensure that it does what I want it to do.
Any help is appreciated!
Todd
PS, if you can figure out the system, please don't say it. I want to see if I'm up to the task before making promises ;)
joshuha
December 11th, 2009, 04:28
Read this and then let me know if you need further help on how to turn your control into a template.
https://fantasygrounds.com/modguide/templates.xcp
tdewitt274
December 11th, 2009, 05:28
I have the templates down now. The only thing I wasn't sure on is how to pass the value of the "source" into the script.
window.getDatabaseNode().getChild("charsheet.|AttributeName|.value");
I've tried multiple combinations, but I'm not sure how to return the control in question.
My other question was how do you update another control? Essentially, I'm looking for the LUA version of
var1 = txtSource1.value;
--do some processing --
txtSource2.value = var1;
This has been a GREAT help! Thanks!
joshuha
December 11th, 2009, 13:34
I will post up some examples for you. The best thing for you to do in a template would be to have an XML value in the control hold the source value and use that in the script. Mind posting what you have for the template control with the stubs for the atrributename and I can help you from there?
tdewitt274
December 11th, 2009, 14:27
Here's what I have so far. The Char Sheet information is the same for each of the Attributes.
Again, thanks!
Todd
Template
<template name="attribvalue">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<font>sheettext</font>
<frame>
<name>value</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
</numberfield>
</template>
<template name="attribuntrn">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<font>sheettext</font>
<frame>
<name>untrained</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
<script>
function recalc()
local attrib = window.getDatabaseNode().getChild(self.source.valu e).getValue();
setValue(onValue(attrib));
end
function onInit()
local attrib = window.getDatabaseNode().getChild(self.source.valu e);
if attrib then
attrib.onUpdate = recalc;
recalc();
end
end
function onValue(result)
return math.floor(result/2);
end
</script>
</numberfield>
</template>
<template name="attribresmod">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<font>sheettext</font>
<frame>
<name>resmod</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
<script>
function recalc()
local attrib = window.getDatabaseNode().getChild(self.source.valu e).getValue();
local val = 0;
if attrib > 18 then
val = 5;
elseif attrib > 16 then
val = 4;
elseif attrib > 14 then
val = 3;
elseif attrib > 12 then
val = 2;
elseif attrib > 10 then
val = 1;
elseif attrib > 6 then
val = 0;
elseif attrib > 4 then
val = -1;
else
val = -2;
end
setValue(val);
end
function onInit()
local attrib = window.getDatabaseNode().getChild(self.source.valu e);
if attrib then
attrib.onUpdate = recalc;
recalc();
end
end
</script>
</numberfield>
</template>
Char Sheet
<attriblabel>
<anchored>
<offset>15,18</offset>
</anchored>
<static>Strength</static>
</attriblabel>
<attribvalue name="STR" source="charsheet.STR.value">
<anchored>
<offset>100,15</offset>
</anchored>
</attribvalue>
<attribuntrn name="STRuntr" source="charsheet.STR.untrained">
<anchored>
<offset>135,15</offset>
</anchored>
</attribuntrn>
<attribresmod name="STRresmod" source="charsheet.STR.resmod">
<anchored>
<offset>170,15</offset>
</anchored>
</attribresmod>
tdewitt274
December 11th, 2009, 14:33
As a note, I'm using the Anatomy of a Ruleset (https://wiki.witheredlands.co.uk/anatomy.ashx) to work my way through this.
If there are any other tutorials out there, I'd be interested in seeing them.
tdewitt274
December 12th, 2009, 16:59
I've been looking around, reading more of the online manuals, reading the LUA docs, and other posts. As a result, I've modified my code some more. Makes more sense, but I'm still at a loss for accessing the controls (database or otherwise) through templates.
The CharSheet information in my previous post hasn't changed. Below is an attempt at doing what I stated above. No idea if this will even work, but it's what I've managed to understand so far. If you see something that's not possible, let me know.
As always, any help is appreciated!
Tempate
<template name="attribvalue">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<font>sheettext</font>
<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
<script>
function onInit()
local ctrVal = window.getDatabaseNode(); -- used to get the value
local ctrUnt = window.getDatabaseNode(); -- assign to the Untrained
local ctrRes = window.getDatabaseNode(); -- assign to the ResMod
if ctrVal then
ctrVal.onUpdate = recalc;
ctrUnt.setValue(),ctrRes.setValue() = recalc(ctrVal.getValue());
end
end
function recalc(attribVal)
local resmod = -2;
local untrain = math.floor(attribVal/2);
if attribVal > 18 then
resmod = 5;
elseif attribVal > 16 then
resmod = 4;
elseif attribVal > 14 then
resmod = 3;
elseif attribVal > 12 then
resmod = 2;
elseif attribVal > 10 then
resmod = 1;
elseif attribVal > 6 then
resmod = 0;
elseif attribVal > 4 then
resmod = -1;
end
return untrain,resmod;
end
</script>
</numberfield>
</template>
<template name="attribuntrn">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<font>sheettext</font>
<readonly />
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
</numberfield>
</template>
<template name="attribresmod">
<numberfield>
<anchored>
<to>statsframe</to>
<position>insidetopleft</position>
<size>
<width>28</width>
</size>
</anchored>
<readonly />
<font>sheettext</font>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
</numberfield>
</template>
Brenn
December 12th, 2009, 17:20
Without getting too deep into the code, I think the functionality you might be looking for is in the onUpdate handler (https://www.fantasygrounds.com/refdoc/databasenode.xcp#onUpdate) for a database node.
You then put the code for the derived statistics in the controls for those statistics.
That way anytime the database is modified for the base stat, that info is pushed to whomever needs it and they do with it what they will.
EDIT: Reference Foen's post in this thread (https://www.fantasygrounds.com/forums/showthread.php?t=11360) for a better explanation.
tdewitt274
December 12th, 2009, 23:53
Alright, I think I'm way off kilter based on this (https://www.fantasygrounds.com/forums/showthread.php?t=6844) and this (https://fantasygrounds.com/forums/showthread.php?t=6775). Yes, I've been going through the forum page by page (more or less) :)
I can get what I want to work in the normal character sheet, but by re-using the same code over and over. My intent was to consolidate and was enlightened by Templates. However, this is where I couldn't get the code working (see previous posts).
It seems that I need another step in there somewhere. Question is: if I make a Template for my Attributes section of my character sheet, do I just need an onInit() and update() function that runs an "updateEverythingElse()" function?
So, in the end, the "updateEverythingElse()" updates my Untrained, ResMod, and any other figured characteristics?
If so, can I get the name of the control (ex, DEX, STR, etc) that kicks off the onInit() or update() functions so it doesn't do an ungodly amount of updating? If I can, will I be able to put the onInit() and/or update() in the Template?
Again, thanks for any help!
Brenn
December 13th, 2009, 02:25
I'm still not quite sure how to really answer your question. You if you do say- dexterityNode.onUpdate = calcReflexBonus(); and dexterityNode.onUpdate = calcAcStatBonus(); , any time the value of dexterityNode changes it will call both of the functions. So in your derived statistics you just assign an update function to the ability node that they are dependant on.
If you are using templates then you will need something that is a pointer to the specific database node they need and assign it in the script for the control derived from the template.
I'm tired so that may not have been as clear as it could have been. I'll try again tomorrow if this is still is a problem.
tdewitt274
December 13th, 2009, 04:03
It's most likely that I'm not explaining it very well. While I can do Javascript and VBScript, Lua is new to me and has some odd quirks from what I've read.
However, your last post did snap a piece in place. I didn't understand what the onInit() was doing, especially with assigning a function to another. However, I got the gist of it from your post.
I guess what I'm trying to do is, as described in pidgeon Javascript,
function UpdateProcess () {
CurrCtrl = this.id;
ans = doUberUpdate(CurrCtrl);
}
function doUberUpdate(AbilityName) {
if (AbilityName=="STR") { doSTRUpdatestuff }
elseif (AbilityName=="DEX") { doDEXUpdatestuff }
...
...
}
}
I've refined my code to all fit into the first block of an ability score. I have the below code for each of the six ability score (STR, DEX, CON, INT, etc) where the bold is replaced by the different ability abreviations.
<attribvalue name="STR" source="charsheet.STR.value">
<anchored>
<offset>100,15</offset>
</anchored>
<script>
function onInit()
local attrib = window.getDatabaseNode().getChild("charsheet.STR.value");
if attrib then
attrib.onUpdate = recalc();
recalc();
end
end
function recalc()
local attrib = window.getDatabaseNode().getChild("charsheet.STR.value").getValue();
local val = 0;
if attrib > 18 then
val = 5;
elseif attrib > 10 then
val = math.ceil((attrib - 10) / 2);
elseif attrib > 6 then
val = 0;
elseif attrib > 4 then
val = -1;
else
val = -2;
end
window.getDatabaseNode().getChild("charsheet.STR.resmod").setValue(val);
window.getDatabaseNode().getChild("charsheet.STR.untrained").setValue(math.floor(attrib/2));
end
</script>
</attribvalue>
<attribuntrn name="STRuntr" source="charsheet.STR.untrained">
<anchored>
<offset>135,15</offset>
</anchored>
</attribuntrn>
<attribresmod name="STRresmod" source="charsheet.STR.resmod">
<anchored>
<offset>170,15</offset>
</anchored>
</attribresmod>
I've tried to modify the recalc() function to take a parameter so that the same recalc() function can be used whenever one of the six ability scores are changed. As a result, only the onInit() function would be in the "charsheet.xml" file.
I modified the code to the below and I get an error
"Script Error: [string "charsheet:STR"]:1: attempting to set a nil value as a handler function"
<script>
function onInit()
local attrib = window.getDatabaseNode().getChild("charsheet.STR.value");
if attrib then
attrib.onUpdate = recalc("STR");
recalc("STR");
end
end
function recalc(AttName)
local attrib = window.getDatabaseNode().getChild("charsheet."..AttName..".value").getValue();
local val = 0;
if attrib > 18 then
val = 5;
elseif attrib > 10 then
val = math.ceil((attrib - 10) / 2);
elseif attrib > 6 then
val = 0;
elseif attrib > 4 then
val = -1;
else
val = -2;
end
window.getDatabaseNode().getChild("charsheet."..AttName..".resmod").setValue(val);
window.getDatabaseNode().getChild("charsheet."..AttName..".untrained").setValue(math.floor(attrib/2));
end
</script>
Can anyone tell me what is wrong with the code?
Brenn
December 13th, 2009, 05:23
I'm off to bed now, but I'll get you something in the morning that I think should clarify things quite a bit for you. Unless someone else beats me to it of course...
Brenn
December 13th, 2009, 15:01
Ok this should help you, I think. First the Template definitions for my example:
<template name="baseValue">
<numberfield>
<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
<font>sheetnumber</font>
</numberfield>
</template>
<template name="derivedValue">
<numbercontrol>
<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<keyeditframe>
<name>sheetfocus</name>
<offset>5,5,5,5</offset>
</keyeditframe>
<font>sheetnumber</font>
<script file="scripts/template_derivedValue.lua" />
</numbercontrol>
</template>
Now the instancing of those templates:
<baseValue name="strength" source="abilities.strength">
<anchored>
<to>name</to>
<position>belowleft</position>
<offset>5,10</offset>
<size>
<width>30</width>
<height>30</height>
</size>
</anchored>
</baseValue>
<derivedValue name="strResmod">
<anchored>
<to>strength</to>
<position>right</position>
<offset>5,0</offset>
<size>
<width>30</width>
<height>30</height>
</size>
</anchored>
<!-- Custom Values used in script -->
<masterVal>
<name>abilities.strength</name>
<calcType>resmod</calcType>
</masterVal>
</derivedValue>
<derivedValue name="StrUntrained">
<anchored>
<to>strResmod</to>
<position>right</position>
<offset>5,0</offset>
<size>
<width>30</width>
<height>30</height>
</size>
</anchored>
<!-- Custom Values used in script -->
<masterVal>
<name>abilities.strength</name>
<calcType>untrained</calcType>
</masterVal>
</derivedValue>
and finally the code in "template_derivedValue.lua" that makes it all come together.
function recalc()
local masterVal = myNode.getValue();
if myCalcType == "untrained" then
setValue(math.floor(masterVal/2));
elseif myCalcType == "resmod" then
local val = 0;
if masterVal > 18 then
val = 5;
elseif masterVal > 10 then
val = math.ceil((masterVal - 10) / 2);
elseif masterVal > 6 then
val = 0;
elseif masterVal > 4 then
val = -1;
else
val = -2;
end
setValue(val);
end
end
function onInit()
myNode = nil;
myCalcType = "";
if masterVal then
local mvID = masterVal[1].name[1];
myNode = window.getDatabaseNode().createChild(mvID, "number");
myCalcType = masterVal[1].calcType[1];
end
if myNode then
myNode.onUpdate = recalc;
end
end
Hope this helps, the only kind of strange thing is how you have to index the XML elements from script. Also the createChild function creates the node if it doesn't exist, otherwise it just gets the value from that node, pretty doggone handy. I'm pretty sure that was the problem you were having is that the node didn't already exist in the onInit and therefore when you tried to get it it was returning nil.
Let me know if you have any other questions.
Brenn
December 13th, 2009, 15:06
Oh and also, there is a much more robust implementation of this kind of behavior if in the stock d20 scripts. Look for the linkednumber template in common_templates.xml and it's implementation on charsheet_main.xml.
tdewitt274
December 13th, 2009, 15:45
Thanks! This makes things a lot clearer. I'll try this later today and see how it works. I'll also check out the referenced code in the d20, which is what I'm basing the ruleset on.
I guess the piece that didn't make sense to me is that the "basevalue" code does not have scripting in it. But it seems that the onInit() functions (when loading the character sheet itself) creates code for a basevalue.onUpdate() function for later use. So, after initiation of the window, the code takes on the recalc() function but looks to the other nodes to update themselves? This may make more sense after I read some of the suggested d20 code.
One question that I did have regarding the code is
local mvID = masterVal[1].name[1];
myNode = window.getDatabaseNode().createChild(mvID, "number");
myCalcType = masterVal[1].calcType[1];
Can you explain what this does? In particular the "[1]" references. I know they're referencing tables, but I don't know what they're doing. Everything else makes sense.
Thanks a lot for the help! It is greatly appreciated!
Brenn
December 13th, 2009, 16:01
One question that I did have regarding the code is
local mvID = masterVal[1].name[1];
myNode = window.getDatabaseNode().createChild(mvID, "number");
myCalcType = masterVal[1].calcType[1];
Can you explain what this does? In particular the "[1]" references. I know they're referencing tables, but I don't know what they're doing. Everything else makes sense.
Thanks a lot for the help! It is greatly appreciated!
You have access to the xml that defines the control from script. Due to how the XML is parsed basically everything shows up as a table (or array if you prefer ;) ), so the masterVal[1] is calling the first element of the masterVal tag (in this case the only element).
<derivedValue name="strResmod">
.
.
.
<masterVal>
<name>abilities.strength</name>
<calcType>resmod</calcType>
</masterVal>
</derivedValue>
in script to access the above name tag it would be:
masterVal[1].name[1]
Even though there is only a single item they still are tables because of how the XML is parsed. You can probably simplify this by using attributes in the XML, but I've never really played around with it. You might be able to do something like this:
.
.
.
<masterVal node="abilities.strength" calcType="resmod" />
.
.
.
and access it through script by masterVal[1].node and masterVal[1].calcType. I'm not sure if I'm correct in that as I've never done it ;)
tdewitt274
December 13th, 2009, 16:06
Aaah, I wasn't thinking XML there. XML isn't my strong suit either.
I'll give the other code a try as well and see if it works.
Brenn
December 13th, 2009, 16:15
Aaah, I wasn't thinking XML there. XML isn't my strong suit either.
I'll give the other code a try as well and see if it works.
I think the accessing of attributes from lua is a bit dicey, it would probably be best to stick with the original structure. I just tried it out and had problems.
A more verbose and complete explanation of accessing XML from script can be found here. (https://www.fantasygrounds.com/modguide/scripting.xcp) It's towards the bottom of the page.
tdewitt274
December 17th, 2009, 03:23
Just wanted to make a quick note. I haven't had a chance to really dig into this one. I did try it out, but ran into errors. I'm attributing this to the quick attempt at patching my files with the above code. Not sure when I'll get the chance to really dig into it, but I'll give it a go as soon as I can.
Thanks again!
Powered by vBulletin® Version 4.2.1 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved.