PDA

View Full Version : Pulling a string entry and returning a value



Stitched
August 28th, 2010, 10:12
Hey hey,

I am modifying the D20 ruleset and there is a stat that uses the "race" name to return a value.

So I have <labeledstring name="race"> at the top of the sheet (which is the same as D20) and want to read that value. So my question is:

Do I need to define "race" as a source using the "source =" command?

When using the <source> tag to add read sources for calculated stat boxes, since "race" isn't a stat like "abilities.dexterity.bonus", can I create an invisible placeholder or a local variable that holds whatever the "onSourceValue" function that reads "race" and returns the "racespeed" value?

Lost in code.

Stitched
August 28th, 2010, 17:11
Never mind. Found an old thread talking about using the window.databaseNode().getChild() command.
Now that I am able to read valueA, I am having trouble updating valueB, every time valueA changes.

I've tried varying forms of onInit() - which seems to work only if you open/close the sheet.

and

onChangedValue()

Since it's a labeledstring, it's not capturing when the value of the entry changes (or it throws me an error). Do I have to do a check for when the user hits Enter to update?

Zeus
August 28th, 2010, 18:59
Stitched - There are a couple of methods for reading a value from a control. One method uses the window control itself to read the value and the other utilises the underlying databasenode the control is bound to to read the value. In summary you can read the value from the window control itself or from its bound database node if the control is bound.

To read a value from a window control, you can use the getValue() method:
e.g. window.race.getValue();

To read a value from a database node for a bound window control, you can use the database node getValue() method
e.g. window.race.getDatabaseNode().getValue();

To update a value of one field based upon the value set in another field, I would use onValueChanged() for string/number based controls like stringcontrol or stringfield or register an onUpdate() event handler in the onInit() function of the control's database node for non-string/number based controls which don't support onValueChanged().

Given your working with a string based control, I would use the onValueChanged() method as its a little more straightforward to understand.

e.g.


<stringcontrol name="field1">
....
<script>
function onValueChanged()
window.field2.updateDisplay();
end
</script>
</stringcontrol>

<stringcontrol name="field2">
....
<script>
function onInit()
updateDisplay();
end

function updateDisplay()
setValue(window.field1.getValue());
end
</script>
</stringcontrol>


Hope that helps.

Stitched
August 28th, 2010, 19:45
Thanks, Doc.
Been banging my head all day on this stuff. Managed to only get one number box aligned properly and have been trying to automate the calculation for the value. At the very least, I was able to figure out how to grab a value using "window.etc..."

I didn't realize you needed to effectively make two calls for updating. Will give it a shot and see what happens.

I was told by Moon that to set the min/max value of an entry, in this case a modifer, was to use this code:



function onValueChanged()
if not checkrange then
checkrange = true;

local nValue = getValue();
if (nValue < -2) then
setValue(-2);
elseif (nValue > 4) then
setValue(4);
end

checkrange = false;
end
end


Do you know what he means by using a "trapping variable" ? Is this code added to the template that I am using (abilitybonus) or for each score that I want to limit?





Stitched - There are a couple of methods for reading a value from a control. One method uses the window control itself to read the value and the other utilises the underlying databasenode the control is bound to to read the value. In summary you can read the value from the window control itself or from its bound database node if the control is bound.

To read a value from a window control, you can use the getValue() method:
e.g. window.race.getValue();

To read a value from a database node for a bound window control, you can use the database node getValue() method
e.g. window.race.getDatabaseNode().getValue();

To update a value of one field based upon the value set in another field, I would use onValueChanged() for string/number based controls like stringcontrol or stringfield or register an onUpdate() event handler in the onInit() function of the control's database node for non-string/number based controls which don't support onValueChanged().

Given your working with a string based control, I would use the onValueChanged() method as its a little more straightforward to understand.

e.g.


<stringcontrol name="field1">
....
<script>
function onValueChanged()
window.field2.updateDisplay();
end
</script>
</stringcontrol>

<stringcontrol name="field2">
....
<script>
function onInit()
updateDisplay();
end

function updateDisplay()
setValue(window.field1.getValue());
end
</script>
</stringcontrol>


Hope that helps.

Stitched
August 28th, 2010, 19:53
Typed in like this :


function onInit()
updateDisplay();
end
function updateDisplay()
setValue(window.race.getValue());
end


Gives me a "Script Error: [string "charsheet_main:speed"]:1: bad argument #-1 to 'setValue' (number expected, got string)"

Hmmm....

Zeus
August 28th, 2010, 20:14
Thanks, Doc.
Been banging my head all day on this stuff. Managed to only get one number box aligned properly and have been trying to automate the calculation for the value. At the very least, I was able to figure out how to grab a value using "window.etc..."

I didn't realize you needed to effectively make two calls for updating. Will give it a shot and see what happens.


To clarify, I meant there are two different methods depending upon whether what your accessing is a control or a database node. You don't need to make two calls per se.



I was told by Moon that to set the min/max value of an entry, in this case a modifer, was to use this code:



function onValueChanged()
if not checkrange then
checkrange = true;

local nValue = getValue();
if (nValue < -2) then
setValue(-2);
elseif (nValue > 4) then
setValue(4);
end

checkrange = false;
end
end


Do you know what he means by using a "trapping variable" ? Is this code added to the template that I am using (abilitybonus) or for each score that I want to limit?

I believe JPG is referring to the use of a variable (in the example: checkrange) to ensure 'serialization of data access' or in other words to ensure only one function is able to make a change to data (in this case to a fields value) to ensure data integrity.

In the example the function will ONLY update the fields values if the variable 'checkrange' is FALSE. If it is FALSE it sets it to TRUE whilst it makes the change thus stopping any other instances of the function which are attempting to update the field from also applying any changes. Once it completes it resets 'checkrange' back to FALSE.

This is a fundamental software development technique used in client-server systems whereby protection against parallel running instances changing the same data at the same time is essential to ensure data consistency and integrity.





trapping variable in this case

Zeus
August 28th, 2010, 20:18
Typed in like this :


function onInit()
updateDisplay();
end
function updateDisplay()
setValue(window.race.getValue());
end


Gives me a "Script Error: [string "charsheet_main:speed"]:1: bad argument #-1 to 'setValue' (number expected, got string)"

Hmmm....

Your second field is a number based control whilst the first field is a string, you need to do a type conversion, easiest way is to use tonumber(<string>, <base>) something like this.

function updateDisplay()
setValue(tonumber(window.race.getValue(),10));
end

Stitched
August 28th, 2010, 20:20
Ok. So the value that I am "checking" is the text found in "race". I am using that to do a comparision:



if racename == ("Elf") then
setValue (12 + calculateSources());
elseif racename == ("Dwarf") then
setValue (8 + calculateSources());
else
setValue (10 + calculateSources());


Depending on the "race" text, I then set the base value of "speed" then add the ability modifier (dex bonus) and armour penalty (when I get to it - if I get to it).

Maybe there is a more efficient way to tally up all the modifiers for the "Speed" score but I wasn't able to figure out how to add a dummy <source> name, which holds nothing until I write to it from a script in the same window.

I think I am just getting hung up on working with strings inside functions that require a numerical value.

Stitched
August 28th, 2010, 20:25
Using
function onInit()
updateDisplay();
end


gives me the following error:
"Script Error: [string "charsheet_main:speed"]:1: attempt to call global 'updateDisplay' (a nil value)"

If I use the first function that you listed under the "race" control, it throws the same error as soon as I type / change it.

So not sure what I am doing wrong here. I feel that something like this should be so basic. Hell, I could build cells in Excel that would basically update if a cell changed in seconds.

Zeus
August 28th, 2010, 20:48
Using
function onInit()
updateDisplay();
end


gives me the following error:
"Script Error: [string "charsheet_main:speed"]:1: attempt to call global 'updateDisplay' (a nil value)"

If I use the first function that you listed under the "race" control, it throws the same error as soon as I type / change it.

So not sure what I am doing wrong here. I feel that something like this should be so basic. Hell, I could build cells in Excel that would basically update if a cell changed in seconds.

I'm assuming this is the script element from within the 'speed' control. If so, you need to also define the updateDisplay() function within this control. FGII is trying to reference the function 'updateDisplay()' during the control's initialization, which its looking for in the speed control script block.

Stitched
August 28th, 2010, 21:00
Ahhhhhhh! The penny drops.


function updateDisplay()
local racename = window.race.getValue();
setValue(tonumber(window.race.getValue(),10));
if racename == ("Elf") then
setValue (12 + calculateSources());
elseif racename == ("Dwarf") then
setValue (8 + calculateSources());
else
setValue (10 + calculateSources());
end
end
function onInit()
updateDisplay();
end


It would only update once i had hit return. Now with the onInit() added after defining updateDisplay(), it works solidly.

Bloody hell. Thanks for all the help. Hopefully, this will make sense moving forward.

Stitched
August 28th, 2010, 21:36
Ah crap. Spoke to soon.

The "Speed" value now only updates with the value set when Race changes but doesn't allow for updating when the value for "dex.bonus" changes (Speed = Racial bonus + Dex Bonus - Armour Penalty).

It was working before I added the Race / Updating script. Was using what was there before:


function onSourceUpdate()
setValue(10 + calculateSources());
end

but now that the value for speed is getting set by the racial bonus code, I thought I could just read the current value of "speed" and add it to calculateSources(); that didn't work, however.

-----

And of course, adding an update value script under "dexterity", recalculates "Speed".

Zeus
August 29th, 2010, 08:37
Stitched - Consider adding an onValueChanged() function to the script block for the dex.bonus control. As per the race control, you want the onValueChanged() function for dex.bonus to call the updateDisplay() function in the speed control.

This way updating either race or dex.bonus will auto update speed.

Stitched
August 29th, 2010, 08:42
If you read the last line that I snuck in to my last message, I did exactly that, and it worked brilliantly.

So for every stat that affects another stat, use onValueChanged() - Lesson learned. Thanks again for helping me wrestle with this beast of a language.


<script>
function onValueChanged()
window.speed.updateDisplay();
end
</script>


That snippet / method is definitely going into the Ruleset bible. :)



Stitched - Consider adding an onValueChanged() function to the script block for the dex.bonus control. As per the race control, you want the onValueChanged() function for dex.bonus to call the updateDisplay() function in the speed control.

This way updating either race or dex.bonus will auto update speed.

Zeus
August 29th, 2010, 09:25
Oops - my bad. I didn't spot your edit. :)

Glad its now working for you.