PDA

View Full Version : A Tutorial for newbs by a noob.



Meliora
September 25th, 2007, 22:01
I will use this thread as a small blog on what I learned and how to do it, and hopefully ease the path for some other newbies, when they enter the world of .xml and .lua. The language in these tutorial are hopefully set in an easy to understand tone, and is not going into the deeper explanation of why things works as they do (since I don’t know…yet), just how to make it work.

I bought FG2 for three days ago, and knew nothing about .xml and .lua editing, so some of it could be wrong or be done better, but if, I expect some of the vets to correct me, and I will update the tutorial accordingly. Hopefully this can provide a little bit of insight on what you can do with FG2 and how to do some basic things, especially when it comes to editing the character sheet.

I will update it every time I have been successful with a new project. I’m trying to make a character sheet for one of my favorite RPGs (Mutants and Masterminds edition 1), but will try to avoid giving complete specifications, since I do not want the good folks from Green Ronin to come after me.

I will be using the D20 rule set as base. I will recommend copy that rule set and rename it.

BTW, I know my spelling sucks :).

Tutorial 1: Room Improvement
How to change the sizes and positions of character sheets and boxes. (https://www.fantasygrounds.com/forums/showthread.php?p=49461#post49461)

Tutorial 2: I’m a Punk, not Neutral Evil
Changing options in the Overview Field (https://www.fantasygrounds.com/forums/showthread.php?p=49462#post49462)

Tutorial 3: Different times take different talents
How to change the skill list (https://www.fantasygrounds.com/forums/showthread.php?p=49463#post49463)

Tutorial 4: The Price of Being Great, Part 1 of 3
Letting Abilities and Skills cost Points plus adding a new stat box. (https://www.fantasygrounds.com/forums/showthread.php?p=49494#post49494)

Tutorial 4: The Price of Being Great, Part 2 of 3
Letting Abilities and Skills cost Points plus adding a new stat box. (https://www.fantasygrounds.com/forums/showthread.php?p=49495#post49495)

Tutorial 4: The Price of Being Great, Part 3 of 3
Letting Abilities and Skills cost Points plus adding a new stat box. (https://www.fantasygrounds.com/forums/showthread.php?p=49496#post49496)

Tutorial 5: We have no Class, whatsoever
Removing the old Level and Class Box. (https://www.fantasygrounds.com/forums/showthread.php?p=49506#post49506)

Tutorial 6: Not for the Untrained, Part 1 of 2
Remove any skill rank in Untrained Skills plus adding a cost. (https://www.fantasygrounds.com/forums/showthread.php?p=49749#post49749)

Tutorial 6: Not for the Untrained, Part 2 of 2
Remove any skill rank in Untrained Skills plus adding a cost. (https://www.fantasygrounds.com/forums/showthread.php?p=49750#post49750)

Coming soon

Tutorial 7: One of a Kind
Changing the Main sheet by adding a few new abilities and remove others.

Tutorial 8: Bloody Wounds
Making an overhaul and change of the hit-point system to a Damage Condition system

Meliora
September 25th, 2007, 22:02
Tutorial 1: Room Improvement
How to change the sizes and positions of character sheets and boxes.

This is for the ones that wants a little bit more room in the character sheet, which can be useful if you want to use a game, that have a couple of more or different stats and values that are important to keep track on.

To make the whole sheet bigger is quite easy. The size of your character sheet is stored in the file charsheet_toplevel.xml

Find these lines:


<windowclass name="charsheet">
<frame>charsheet</frame>
<placement>
<size>
<width>525</width>
<height>611</height>
</size>
<position>
<x>410</x>
<y>90</y>
</position>
</placement>
The name between the frame, is the name of the picture you wish to use as background of your sheet. Between the size, you have width and height and the number is in pixels. If you just use the basic D20 background sheet, these numbers are the one you want to change. If you want to use a different frame and don’t want to get into too much coding, make certain that your background picture is about 525 * 611 pixels in size and name it charsheet. Save it under frames (which will overwrite the default d20 sheet) as a PNG webfile. You can make it a different size and name it another name, but it will take a bit of different coding, which I will not cover here.
The different stat boxes on the charsheet background can also be changed in size and position.

The D20 sheet has 7 pages, which are:

Charsheet_abilities
Charsheet_combat
Charsheet_inventory
Charsheet_main
Charsheet_notes
Charsheet_skills
Charsheet_spells

To make one of the boxes bigger or move in position, you have to find the name of the relevant box. I wanted to make the overview box bigger (the one that cover name, age, race, etc), so I looked for overview (other names are hp, ac, classes and so on).

Guess the name of the box and look for the following code: <genericcontrol name="the_name">. As I wanted to make the overview bigger, I found it under this:


<genericcontrol name="overviewframe">

The size and position of this frame is to be found just under that line, and will look like this:


<genericcontrol name="overviewframe">
<bounds>15,20,415,70</bounds>
<frame>
<name>sheetgroup</name>
</frame>
</genericcontrol>

The numbers you want to play around with are in between bounds.

There are four numbers (in this example it is 15,20,415,70)

The 15 are the x-axis position on the sheet.
The 20 are the y-axis position on the sheet.
The 415 are the width of the box on the sheet.
The 70 are the height of the box on the sheet.

You can now change these numbers until the box is the exact size and on the exact position you want it to be.

And that is all it takes, folks, to change the size of the character sheet and the boxes on it.

A very important note: Sizes and position of boxes are saved under campaigns under the name of your last campaign. To check each change either make a new and unique campaign name or delete the CampaignRegistry.lua in your relevant campaign folder.

Thanks too Dachannien and Foen.

Meliora
September 25th, 2007, 22:07
Tutorial 2: I’m a Punk, not Neutral Evil
Changing options in the Overview Field

I’m planning on running a modern age campaign, and do not need races and really never liked the alignment system, so I want to get rid of that. I also thought it could be neat to have a Hometown and an Alias field.

It is very easy to add in the overview field, but can take a little bit of time to adjust position and size of each.

Coding for the overview field can be found in charsheet_main.xml.

First I planned it out on a piece of notepaper on what I wanted in my over view field, and how much space I could assume it would take. The information I came up with, would take up three lines in height instead of the D20s two lines, so first I had to adjust the height of the overview box. I did a little bit of calculation and figured out that each line of height would take up about 22 pixels, plus a 13 pixels margin on each side. So the first thing I did, was to change the height of my overview from 70 to 92. Of course I would have to change the height of my background sheets too and changed that from 611 to 633.

If you now went into check your result you will see a bigger sheet, but also notice that your overview box lapse into the ability box. To avoid that you will have to move every other box down. Do that by finding every genericcontrol box on your mainsheet, and adjust the y-axis (the second number in between bounds) with the relevant height (in this cases with 22 pixels). See more in the first tutorial about changing sizes and position of these boxes.

Now we have enough room to add what we want in the overview field.

Each option within the overview field is made by a little simple code. Each start with a labeledstring and ends with a /labeledstring, E.G age is this code:


<labeledstring name="age">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>80,32</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>age</label>
<tabtarget>
<next>alignment</next>
<prev>gender</prev>
</tabtarget>
</labeledstring>
One of the first things you will see in this code is “anchored”. It is where this piece is locked into, as in the above example age is locked until the overviewframe.
The position is where the offset is calculated from. The first number in offset is how far to the right of the position and the second number is how far down.
The width under size is how long a line you want the players to be able to write on, and the height is how tall it is.
The label is the actually title of the option, that will be showed on your sheet. Just leave it off caps. The font used here, will make it Caps within the game.
Tabtarget is for when you are tabbing. Next is where you end next time you tab, prev is where you end if you use shift+tab. The name between those (next and prev) is the one right after “labeledstring name=”. If you want to use tabbing, remember to change those when you add or delete an option on a sheet.

Now you can start deleting and adding new options.
Let’s say you want to have Name, hometown, alias, nationality, sex, age, birthday, height, weight and size in your overview field, your code would look like this (remember to adjust the size of the background sheet and move each other box down a little bit on the sheet, which in this case is 22)


<!-- OVERVIEW -->
<genericcontrol name="overviewframe">
<bounds>15,20,415,92</bounds>
<frame>
<name>sheetgroup</name>
</frame>
</genericcontrol>

<labeledstring name="name">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>15,10</offset>
<size>
<width>250</width>
<height>20</height>
</size>
</anchored>
<anchorto>overviewframe</anchorto>
<height>20</height>
<label>name</label>
<tabtarget>
<next>hometown</next>
<prev>appearance</prev>
</tabtarget>
</labeledstring>

<labeledstring name="hometown">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>275,10</offset>
<size>
<width>125</width>
<height>20</height>
</size>
</anchored>
<label>hometown</label>
<tabtarget>
<next>alias</next>
<prev>name</prev>
</tabtarget>
</labeledstring>

<labeledstring name="alias">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>15,32</offset>
<size>
<width>250</width>
<height>20</height>
</size>
</anchored>
<anchorto>overviewframe</anchorto>
<height>20</height>
<label>alias</label>
<tabtarget>
<next>nationality</next>
<prev>hometown</prev>
</tabtarget>
</labeledstring>

<labeledstring name="nationality">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>275,32</offset>
<size>
<width>125</width>
<height>20</height>
</size>
</anchored>
<label>nationality</label>
<tabtarget>
<next>sex</next>
<prev>alias</prev>
</tabtarget>
</labeledstring>

<labeledstring name="sex">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>15,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>sex</label>
<tabtarget>
<next>age</next>
<prev>nationality</prev>
</tabtarget>
</labeledstring>

<labeledstring name="age">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>80,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>age</label>
<tabtarget>
<next>birthday</next>
<prev>sex</prev>
</tabtarget>
</labeledstring>

<labeledstring name="birthday">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>145,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>birthday</label>
<tabtarget>
<next>height</next>
<prev>age</prev>
</tabtarget>
</labeledstring>

<labeledstring name="height">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>210,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>height</label>
<tabtarget>
<next>weight</next>
<prev>birthday</prev>
</tabtarget>
</labeledstring>

<labeledstring name="weight">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>275,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>weight</label>
<tabtarget>
<next>size</next>
<prev>height</prev>
</tabtarget>
</labeledstring>

<labeledstring name="size">
<anchored>
<to>overviewframe</to>
<position>insidetopleft</position>
<offset>340,54</offset>
<size>
<width>60</width>
<height>20</height>
</size>
</anchored>
<label>size</label>
<tabtarget>
<next>strength</next>
<prev>weight</prev>
</tabtarget>
</labeledstring>


One important note is about the “Name”. I wanted to change it to “Character’s name”, which is possible. Just only change the label. If you change the name that are after the labeledstring it will give problems elsewhere since the name is linked to things such as portrait, mini sheets and combat tracker.

Thanks too Joshuha, Toadwart and Foen.

Meliora
September 25th, 2007, 22:09
Tutorial 3: Different times take different talents
How to change the skill list

This tutorial will cover a bit about the skill page. My goal is to change a few of the skills to fit better for my campaign and I want to remove cross-class mark and let all skills be class skills to give my players bigger variation in pick (and it is also how it work in my preferred game system :) ).


The different skills are touched in a LUA files: charsheet_skilllist.lua.

In charsheet_skilllist.lua the list starts just under the line skilldata. When you see it, it will make sense. You will see all the skills there and should now, have no trouble picking the ones you don’t want and delete them. To add a new skill, I find it easier to copy and existing one and paste it under another skill, and just change the name and the stat to what I find fitting for my new skill. If you do not want a skill to be related to an ability, just delete that stat line. A few other lines you can add or delete if you wish:

Armorcheckmultiplier = 1, the skill have a penalty from armor
sublabeling = true, a skill have under classes, like knowledge which can be Knowledge (Nature), Knowledge (Religion) and so on.

Let say I want to have a skill called computer, which is linked to Intelligence, I will just add the following in the list:

["Computers"] = {
stat = "intelligence",
},
You do not have to put them alphabetical, since when you open the sheet it will take care of that, but again, it is nice with a little bit of order in all the chaos.

Let’s be a little bit more advanced. Say we have a skill that the players could choose between two different abilities, E.G. Intimidate could use Strength or Charisma, depending on character and how the player is intimidating. If you have noticed, you can already now, make new skills on the skill sheet by right-clicking and choose new item. Those custom skills you are able to change the ability linked to the skill. We want the same result, but only be able to choose between Strength or Charisma.
Open the charsheet_skills.xml and find <stringcontrol name="statlabel">. Within this string control we have to add a little script. There is already a script within and we might as well just build upon that one. The function onClickDown is the one that gets called when a player clicks on that area. As now the script calls for being able to change the ability if the skill is a custom one and looks like this:

function onClickDown(button, x, y)
if window.iscustom then
window.stat.cycleStat();
end
end
We will add a few lines to that script and it will look like this instead:

function onClickDown(button, x, y)
if window.iscustom then
window.stat.cycleStat();
elseif window.label.getValue() == "Intimidate" then
window.stat.changeIntimidatestat();
end
end
Our script starts after elseif, and it checks to see if the label of the skills is Intimidate. If, it ask us to do a window.stat.changeIntimidatestat(). That’s a name I came up with. The program already know the window.stat, but have no idea what a changeIntimidatestat is. So we better tell it. If you see in the script we started with there is a window.stat.cycleStat. Lets search for that in the file. It looks like this:


function cycleStat()
if statnamenode then
local cyclemap = { [""] = "strength",
["strength"] = "dexterity", ["dexterity"] = "constitution", ["constitution"] = "intelligence",
["intelligence"] = "wisdom", ["wisdom"] = "charisma", ["charisma"] = "" };
local statnow = statnamenode.getValue() or "";

setStat(cyclemap[statnow]);
end
end

Check it out a little bit and see if you can figure it out. When we can do that, we are getting way into understanding what .xml is about (or at least that is what I’m telling myself).

We want a function like that for our new changeIntimidatestat, but we do not want it to cycle through all the abilities, only strength and charisma. It will look like this:


function changeIntimidatestat()
if statnamenode then
local cyclemap = {["charisma"] = "strength",
["strength"] = "charisma"};
local statnow = statnamenode.getValue();

setStat(cyclemap[statnow]);
end
end

Now put this script under the other one, and we should be ready to check it out. Remember to save the file. You will notice on your character sheet that when you change the skill the rank will also adjust to whatever bonus the character have in those two abilities. Neat, right?


To remove the cross-class/class indicator to the left of the skills, you will have to go into the charsheet_skills.xml, and find a line that make that icon pop up. The icon is called “indicator_checkoff”, search for it and delete that line (and only that line). If you were to start the program up, you will see that the indicator is still there, which is strange, since you just remove the line that tells it to pop in on the sheet.
It is because of the script, which is just under the line you deleted. Go into your script library and open that file (charsheet_skillstate.lua).
Just in the start of that file, you will see a function called update and within that one, you will see and if and else statement which will either make a indicator_checkon or a ndicator_checkoff. If we do not wish to change a lot of scripts and have some sad bugs, we will have to leave the function update(), but will actually delete what it does.
So instead of having:


function update()
if activesetnode then
if window.isInSet(activesetnode.getValue()) then
setIcon("indicator_checkon");
else
setIcon("indicator_checkoff");
end
end
end

We will only have:


function update()

end

If you now start up the program, you will see that the indicator is gone, but now, you will have to pay for all skills as if they were cross-class with no chance of changing that.
Changing one line can prevent that. Go back to the charsheet_skillstate.lua and find the function isClassSkill(). It is the small code that tells the sheet whatever a skill is a class skill or not. The return True say it is. The return False say it is not. Since we do not want it to say it is not, we will change the False to a True, so no matter what happens, the result will always be true.

The way I remove cross class mark should not be considered very pro and will probably make some vets turn around in the grave. I cheat quite a bit, and leave a lot of code within the ruleset that is no longer necessary. But it works. When I get a better grip about the whole coding system, perhaps I will go clean up the mess. For now, I just want to move on and get a rule system up and working.

Oberoten
September 25th, 2007, 22:25
Gloriously useful. :) This is the kind of tutorials that is most needed I think. Step by step explanations and dissection of the files that be.

newcoda
September 25th, 2007, 23:45
Awesome, helped me out a bit

Im looking forward to more, hopefully you can post some stuff on the Abilities tab. I cannot figure out how to let you drag feats onto the list, and after that getting them to link like they do for spells.

Meliora
September 26th, 2007, 00:20
Awesome, helped me out a bit

Im looking forward to more, hopefully you can post some stuff on the Abilities tab. I cannot figure out how to let you drag feats onto the list, and after that getting them to link like they do for spells.

I want to do that as well, just not there yet. Want to make a little customized level and point system first. The next thing after that will be a little playing around with the feats.

phantomwhale
September 26th, 2007, 00:26
Good stuff. Keep them coming. Trying to get into this LUA stuff, and examples are very very handy right now

Meliora
September 26th, 2007, 01:09
Edited Tutorial 3 to incl. the following (sorry for the setup of the code. Don't know how to make it look good on this forum):

Let’s be a little bit more advanced. Say we have a skill that the players could choose between two different abilities, E.G. Intimidate could use Strength or Charisma, depending on character and how the player is intimidating. If you have noticed, you can already now, make new skills on the skill sheet by right-clicking and choose new item. Those custom skills you are able to change the ability linked to the skill. We want the same result, but only be able to choose between Strength or Charisma.
Open the charsheet_skills.xml and find <stringcontrol name="statlabel">. Within this string control we have to add a little script. There is already a script within and we might as well just build upon that one. The function onClickDown is the one that gets called when a player clicks on that area. As now the script calls for being able to change the ability if the skill is a custom one and looks like this:

function onClickDown(button, x, y)
if window.iscustom then
window.stat.cycleStat();
end
end
We will add a few lines to that script and it will look like this instead:

function onClickDown(button, x, y)
if window.iscustom then
window.stat.cycleStat();
elseif window.label.getValue() == "Intimidate" then
window.stat.changeIntimidatestat();
end
end
Our script starts after elseif, and it checks to see if the label of the skills is Intimidate. If, it ask us to do a window.stat.changeIntimidatestat(). That’s a name I came up with. The program already know the window.stat, but have no idea what a changeIntimidatestat is. So we better tell it. If you see in the script we started with there is a window.stat.cycleStat. Lets search for that in the file. It looks like this:


function cycleStat()
if statnamenode then
local cyclemap = { [""] = "strength",
["strength"] = "dexterity", ["dexterity"] = "constitution", ["constitution"] = "intelligence",
["intelligence"] = "wisdom", ["wisdom"] = "charisma", ["charisma"] = "" };
local statnow = statnamenode.getValue() or "";

setStat(cyclemap[statnow]);
end
end

Check it out a little bit and see if you can figure it out. When we can do that, we are getting way into understanding what .xml is about (or at least that is what I’m telling myself).

We want a function like that for our new changeIntimidatestat, but we do not want it to cycle through all the abilities, only strength and charisma. It will look like this:


function changeIntimidatestat()
if statnamenode then
local cyclemap = {["charisma"] = "strength",
["strength"] = "charisma"};
local statnow = statnamenode.getValue();

setStat(cyclemap[statnow]);
end
end

Now put this script under the other one, and we should be ready to check it out. Remember to save the file. You will notice on your character sheet that when you change the skill the rank will also adjust to whatever bonus the character have in those two abilities. Neat, right?

Sorontar
September 26th, 2007, 12:42
Most informative matey, when you think it's ready I'd vote for this to get a big fat sticky so we don't have to go routing for it further down the line.

Meliora
September 26th, 2007, 17:21
Tutorial 4: The Price of Being Great, Part 1 of 3
Letting Abilities and Skills cost Points plus adding a new stat box.

In my system I use a different way to level up. I reward the players with points, which the can buy abilities and skills for. The amount of points they have gotten through a campaign will also calculate their level. I want that in a new box on the Main page. First I plan out how much info I want in the box.
I want a “Level”, which a calculated based on how many Character Point a player have gotten through the course of gaming. Let’s say for every 10 points a character have gotten he is going up one level (trying to avoid the exact numbers I use for my game, since I do not have a license for Mutant and Masterminds). Should not be editable by players.
I want a “Character Points”, which is the number a player have gotten through the course. This should be editable for players, so they can add the number of Character Points they have gotten.
And last, I want a “Character Points Left” (which I will call C.P. left, so it don’t take up too much space), which is based on Character Points minus points the players have used on buying Abilities and Skill Points. I want every ability point over 10 to cost 2 points, and every skill rank will cost 1 point. So I need room for 3 information blocks.
To make it easy for myself, I check out the character sheet now, and see if there already is a block that have room for that, and I find that the HitPoint block on the main page, seem useful, if I just copy it and change the titles.
First I need more room on my sheet. I could delete another block, but for now, I want to keep everything, since I’m not certain when I need to check out (“steal”), some code. Instead I will increase the width of my character sheet, just enough to leave my new block right beside the HitPoints blocks.
We covered that in Tutorial 1, but let’s just repeat it here. To increase the size of the character sheet, open charsheet_toplevel.xml. The code we are looking for is just under <frame>charsheet</frame>, and since it is width we want, we will check out the number in between <width></width>. Now we could just start increasing it and check back and forth, but since we know what size of box we want, we will just check the size of that and add that number to width. So let’s go and find the info for the Hitpoint box. It is in the file charsheet_main.xml, and try guess what “genericcontrol” we are looking for?
Yes, <genericcontrol name="hpframe">. Under it we will have <bounds>400,112,95,200</bounds>, which tell us, that the (x,y) position is (400,112) and the box is 95 wide and 200 tall. So 95 wide plus let’s add another 5 to leave some room between the two boxes. So our sheet need to be 100 pixels wider. Go back in the charsheet_toplevel.xml and add that to the width of the sheet. Now, that we have enough room on the sheet, let’s copy the Hitpoint box in the charsheet_main.xml and adjust the position a little bit. We already found the code for it, so let’s copy all the information in that box which is hitpoint, lethal and nonlethal. See if you can find how much that is. It is all of this code:


<!-- HP -->
<genericcontrol name="hpframe">
<bounds>400,112,95,200</bounds>
<frame>
<name>sheetgroup</name>
</frame>
</genericcontrol>

<hitpointfield name="hp" source="hp.total">
<anchored>
<to>hpframe</to>
<position>insidetop</position>
<top>
<offset>28</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<noreset />
<tabtarget>
<next>wounds</next>
<prev>expneeded</prev>
</tabtarget>
</hitpointfield>
<stringcontrol>
<anchored>
<to>hp</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>Hit points</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<hitpointfield name="wounds" source="hp.wounds">
<anchored>
<to>hpframe</to>
<position>insidetop</position>
<top>
<offset>81</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<tabtarget>
<next>subdual</next>
<prev>hp</prev>
</tabtarget>
</hitpointfield>
<stringcontrol>
<anchored>
<to>wounds</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>Wounds</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<hitpointfield name="nonlethal" source="hp.nonlethal">
<anchored>
<to>hpframe</to>
<position>insidetop</position>
<top>
<offset>134</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<tabtarget>
<next>appearance</next>
<prev>wounds</prev>
</tabtarget>
</hitpointfield>
<stringcontrol>
<anchored>
<to>nonlethal</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>Nonlethal</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<windowreferencecontrol>
<anchored>
<to>hpframe</to>
<position>insidetopleft</position>
<offset>28,173</offset>
<size>
<width>40</width>
<height>15</height>
</size>
</anchored>
<icon>
<normal>button_minisheet</normal>
<pressed>button_minisheet_down</pressed>
</icon>
<class>charsheetmini_main</class>
<tooltip>
<text>Minisheet</text>
</tooltip>
<nodrag />
<closetoggle />
</windowreferencecontrol>
The first line of code is <!-- HP -->. Anything in between <!-- --> is not read by the program, but is good for us, for future references. As soon as we have copy this box, just under it, I will change that line to look like this <!--Character Points -->, so we from now on, at least know the differences between these two codes.
The next thing I will do is to move it the 100 pixels on the x-axis. The x-axis is the first number in between bounds. Let’s check out how it looks. An important note, which I mentioned in the Tutorial 1, which is worth repeating here (since I struggled with it for a very long time), is that every time you change boxes and positions in the program, you should either use a unique campaign name or delete the CampaignRegistry.lua in your relevant campaign folder, since the program save the last size and position under there, and any adjustments will not show up within the program if you do not change it.

When you check it, you will see you have the box, but not all the text in it. Its because the code for the HitPoint box and for this new Character box is the same, and the text is anchored to any hitpoint box, and have decided to stay in the old one. We will change that real quickly. First let’s give our new box a unique genericcontrol name. We will call it cpframe. Now change everywhere it say hpframe in the code, to cpframe. The hitpoint box also have a small icon, you can use to make a mini window pop up. I do not need it for this new box, so I will delete all the code that covers that. See if you can find that code.
Yes, it is in between the windowreferencecontrol that mention something about a minisheet. Just delete it.
We also need to change the titles. Change hp to level, wounds to cp and nonlethal to cpl. The codes between <static> </static> is the label or text that will be shown on the sheet. Enter the title you want to show up on the sheet for each box, in my case it will be, Level, Character Points and C.P. Left.

Again let’s go check our new result (remember to save the script). I like checking a lot. This way, I can see if anything went wrong and still remember what I did, so I can undo it. You will notice that Character Points is too long and goes outside the box. You can either change the text or increase the size of the box and sheet. I decide to do the later. To increase the width of the box, change the 3rd number up between bounds under the genericcontrol and back to the charsheet_toplevel to increase the width of the sheet. 10 pixels should be enough. And now we have a new box with a new set of stats.

Meliora
September 26th, 2007, 17:24
Tutorial 4: The Price of Being Great, Part 2 of 3
Letting Abilities and Skills cost Points plus adding a new stat box.

Let’s move on to the more fun part of the programming. Telling it to do our work. First, we only want the players to be able to edit one of these three fields and the two fields have some auto calculation in it, which is linked and based from other numbers. Let’s concentrate about those two boxes first (Level and C.P. Left). This line:


<hitpointfield name="level" source="level.total">

Of course first it say hitpointfield, which it is not any longer. It would be logical to call it something like characterpoint field, but since this is a linked and locked box, we will have to use a “linkednumber” command, so the code will end up looking like this:


<linkednumber name="level" source="level.total">

A little bit further down, you will see a </hitpointfield>. As we have change that, it need to be </linkednumber> instead.

The same have to be done to C.P. Left


<linkednumber name="cpl" source="level.cpl">
And remember to add </linkednumber>.

If you were to go in and check now, you will see that the frame around the number is gone and the number is now in a different font. That’s because the command linkednumber do not know about the box and calls for a different font. Of course we will change that. Just above the </linkednumber> we will add this in both the Level section and the C.P. Left section:

<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<font>sheetnumber</font>


The “bonus” is the name of the frame (if you go into your “Frame” library, you will see it). The offset is the position of the frame. The <readonly /> is, yeah, a box that is only readable. The font is self-explaining.

Remember we had to change “<hitpointfield name,”. Let’s do that for Character points too, but let’s give it a unique name, which should be characterpoint.


<characterpointfield name="cp" source="level.cp">
Remember to go down and change </hitpointfield> to </characterpointfield>. Now the program doesn’t know what a characterpointfield is, so we will have to tell it. Those templates are under charsheet_templates.xml. Check that file out a little bit and see if you start understanding it all a little bit. As this templete reminds very much about the hitpointfield, let’s just find that and copy it just right under, changing hitpointfield to characterpointfield.


<template name="characterpointfield">
<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>
<stateframe>
<drophilight>
<name>sheetfocusplus</name>
<offset>5,5,5,5</offset>
</drophilight>
</stateframe>
<droptypes>
<type>number</type>
</droptypes>
<script>
function onDrop(x, y, draginfo)
if draginfo.isType("number") then
setValue(getValue() + draginfo.getNumberData());
return true;
end
end
</script>
</numberfield>
</template>

Return to charsheet_main.xml and we will start playing around with a bit of coding. First, I wanted level to be based on the amount of Character Points. For every 10 Character Points the character is 1 level.

Above </linkednumber> insert


<source>
<name>level.cp</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(math.floor(calculateSources()/10));
end
</script>
In between the Source is where this script gets its information from, in this case from level.cp. If you check the area where you have coded the Character Points you will see it starts with <characterpointfield name="cp" source="level.cp">. So now we know what the source in that line is useful for. The + between <op></op>, tells the script to add 1 for every 1 in the source. You can add more sources, but we only need one for this script. Now we will have a value, gained from our sources. We want to end with a number one tenth of that, so we will make a small script. The function which is onSourceUpdate(), tells the program to call for this script every time there is an update in the source area, which in this case is the Character Point field. The next line is to set the Value in the Level area based on a bit of calculation. First the math.floor means round down to the nearest whole number. You could also use math.ceil, which would round up. I found all these commands on this site: https://www.lua.org/manual/5.1/

After math.floor, it will calculated the value from all sources and divides it with 10.
And that is it. We know have a Level that adjusts automatically after the amount of character points.

Now to C.P. Left. Let’s first concentrate about getting the amount from Character Points and adjust character point left, based on how high the player’s abilities are. As with level, we need to enter our source and in the case it is sources. We use Character Points and the six Abilities. Since we want it to cost two points for each ability point over 10, we will have to make another linkednumber.

Let’s make that linkednumber under C.P. Left. It will look like this:


<linkednumber name="abilitycost" source="cost.ability">
<anchored>
<to>cpframe</to>
<position>insidetop</position>
<top>
<offset>0</offset>
</top>
<left>
<offset>0</offset>
</left>
<right>
<offset>0</offset>
</right>
<size>
<height>0</height>
</size>
</anchored>
<source>
<name>abilities.strength.score</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.score</name>
<op>+</op>
</source>
<source>
<name>abilities.dexterity.score</name>
<op>+</op>
</source>
<source>
<name>abilities.intelligence.score</name>
<op>+</op>
</source>
<source>
<name>abilities.wisdom.score</name>
<op>+</op>
</source>
<source>
<name>abilities.charisma.score</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(calculateSources()*2-120);
end
</script>
<invisible />
</linkednumber>
All of this you should have seen before, except for this command <invisible />. With this command this number does not show up on the sheet.

Meliora
September 26th, 2007, 17:26
Tutorial 4: The Price of Being Great, Part 3 of 3
Letting Abilities and Skills cost Points plus adding a new stat box.


Now we have the price for the abilities. Noticed we subtracted 120, since the 10 points for each ability is free and the player will actually gain points if he goes under. Now we need to make the calculation in the C.P. Left box. Again, just above the </linkednumber> add this script:


<source>
<name>level.cp</name>
<op>+</op>
</source>
<source>
<name>cost.ability</name>
<op>-</op>
</source>
<script>
function onSourceUpdate()
setValue(calculateSources());
end
</script>
The only new thing in that script is that now we use a minus in between <op></op>, when it comes to cost.ability. Of course that is because we want that to be subtracted.

We also wanted to get a cost from the skills. We will return to that in tutorial 6. For now, you new stat box code should look like this:


<!-- Character Points -->
<genericcontrol name="cpframe">
<bounds>500,112,105,200</bounds>
<frame>
<name>sheetgroup</name>
</frame>
</genericcontrol>

<linkednumber name="level" source="level.total">
<anchored>
<to>cpframe</to>
<position>insidetop</position>
<top>
<offset>28</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<noreset />
<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<font>sheetnumber</font>
<source>
<name>level.cp</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(math.floor(calculateSources()/10));
end
</script>
</linkednumber>
<stringcontrol>
<anchored>
<to>level</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>Level</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<characterpointfield name="cp" source="level.cp">
<anchored>
<to>cpframe</to>
<position>insidetop</position>
<top>
<offset>81</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<tabtarget>
<next>apperance</next>
<prev>nonlethal</prev>
</tabtarget>
</characterpointfield>
<stringcontrol>
<anchored>
<to>cp</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>Character Points</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<linkednumber name="cpl" source="level.cpl">
<anchored>
<to>cpframe</to>
<position>insidetop</position>
<top>
<offset>134</offset>
</top>
<left>
<offset>18</offset>
</left>
<right>
<offset>-19</offset>
</right>
<size>
<height>30</height>
</size>
</anchored>
<frame>
<name>bonus</name>
<offset>5,5,5,5</offset>
</frame>
<readonly />
<font>sheetnumber</font>
<source>
<name>level.cp</name>
<op>+</op>
</source>
<source>
<name>cost.ability</name>
<op>-</op>
</source>
<script>
function onSourceUpdate()
setValue(calculateSources());
end
</script>
</linkednumber>
<stringcontrol>
<anchored>
<to>cpl</to>
<position>aboveleft</position>
<offset>0,3</offset>
</anchored>
<static>C.P. Left</static>
<font>sheetlabelsmall</font>
</stringcontrol>

<linkednumber name="abilitycost" source="cost.ability">
<anchored>
<to>cpframe</to>
<position>insidetop</position>
<top>
<offset>0</offset>
</top>
<left>
<offset>0</offset>
</left>
<right>
<offset>0</offset>
</right>
<size>
<height>0</height>
</size>
</anchored>
<source>
<name>abilities.strength.score</name>
<op>+</op>
</source>
<source>
<name>abilities.constitution.score</name>
<op>+</op>
</source>
<source>
<name>abilities.dexterity.score</name>
<op>+</op>
</source>
<source>
<name>abilities.intelligence.score</name>
<op>+</op>
</source>
<source>
<name>abilities.wisdom.score</name>
<op>+</op>
</source>
<source>
<name>abilities.charisma.score</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(calculateSources()*2-120);
end
</script>
<invisible />
</linkednumber>

Last, if you use tabbing in your sheet, you will have to update is as covered in Tutorial 2. The uneditable areas do not need any tabcoding and can be bypassed.

Meliora
September 26th, 2007, 20:37
Tutorial 5: We have no Class, whatsoever
Removing the Old Level and Class Box.

I use a class less system and I have already made my own Character Level Box, so there is no use for the default D20 level and class box anymore. Now before we rush in and just delete it, we should check a little bit around and see what functions it has. One way is to check the different source names. Let start with the source name for level, which is “characterlevel”. Do a search for it and see what comes up.

I find it in charsheet_templates.xml and in charsheet_skillranks.lua. Check those files out and see what characterlevel function are.

In the template file it is a command that tells the total level to be updated every time there is a change in one of the class levels. It is under a template called “levelnumber”. Lets make a search for that one too, before deleting anything. What we find is, that levelnumber is the one used beside class. We don’t need that anymore, so lets go ahead and delete that template.

The other file we found characterlevel in was in charsheet_skillranks.lua, so let’s see what it does in there.


levelnode = window.windowlist.window.getDatabaseNode().createC hild("characterlevel", "number");

This line is found under skillranks, so with a little bit of thoughts the only thing I can come up with, where skills is touched by level, is the maximum rank (in D20 you can max. have lvl. +3 in rank). I’m planning on using that in my system, so what I will do, is to change the source in that line. My level source is called “level.total”, so I will replace characterlevel with that.


levelnode = window.windowlist.window.getDatabaseNode().createC hild("level.total", "number");

Now, if you do not wish to use a max rank, you could try to delete that line and find all it is related too. Instead of that, I would rather suggest to go a bit up in the .lua file and find this line:


local maxranks = levelnode.getValue() + 3;

and just change the +3 to whatever max, you want (or even change that script completely to something unique).

The next thing we have to check for is the classes. The generic controls and sources are classanchor, classes.slot1.name (1-3), classes.slot1.level (1-3). I found a few, but take a look at them and it should be easy to decipher that it is not something we need in a class less system and it does not change anything else in our program, except for one thing. The classes.slot1.name comes up with some skillsetlabel under charsheet_skills.xml. If you can’t remember what classes are doing on the skill sheet, go take a look in the d20 ruleset before deleting anything. Down at the bottom of the skill sheet each name of the classes you have entered in your classbox is printed with a checkmark. It is used to remember the class skills for each. Since I don’t use cross classes in my rule set I gladly went ahead and deleted those files as well.

About the experience points I simply can not see where else it is used on the sheet, so I ignore it.

Now everything that are referring to the old class and level box, have either been changed to use my new Level box or deleted. I can now go ahead and delete the D20 class/level box in charsheet_main.xml, cross my fingers and start the tools up, hoping for no errors.

And it runs smooth. One thing to keep in mind is, that even the biggest software development companies out there, releases products with bugs, so it would be way too optimistic to assume that one haven’t sneaked in here. That is why, we won’t ever override the default D20 ruleset, so we have something to come back and refer too, if we do not remember what we deleted, plus another good tip is to make a new folder for your ruleset after every update. I call mine something like Ruleset0924, Ruleset0925 and Ruleset0926, where the numbers is the date I saved it. That way, I will only lose a days work, if I make a rule-set that screws up my whole program.

When you check the character sheet now, there is of course a huge empty space from where that class box was. You could of course start adjusting your boxes as in Tutorial 1, but I leave it for now, since I have plans for the ability box.

Again, remember to update the tab code as covered in Tutorial 2, since we removed some boxes.

Meliora
September 26th, 2007, 21:48
Forgot about tabbing in the last two tutorials. The tutorials have been update with a single line with that information. Just remember that everytime you add or remove a box, you will probably have to change some of the tabbing codes as covered in tutorial 2. This is only important if you use that function. It will not break or burn your cpu if you do not.

newcoda
September 26th, 2007, 22:21
Awesome, kind of makes me want to switch out of an experience level system. =)

Meliora
October 2nd, 2007, 15:40
Tutorial 6: Not for the Untrained, Part 1 of 2
Remove any skill rank in Untrained Skills plus adding a cost.

I want to return to skills and add few more features. I want to add the cost of skills to my character point system. I want my skills that trained only; to only be usable if the players have at least 1 in rank. I also want to remove the features on the skill sheet that are no longer needed for my rule-set.

The first thing I want to do, is to remove the available ranks, since in my rule set, numbers of ranks is not based on a character level, but on how much points a player want to use on skills, which could be every single one. The box on the charsheet_skills is called “unspentskillpoints”. Of course we want to remove that box, but if we don’t remove the code, that say, that a player cannot use more than what is entered in that one, we won’t really get far. Let’s go into the lua files and search for “unspent”.
The important scripts we find are charsheet_skillranks.lua and charsheet_skilllist.lua.

In charsheet_skilllist.lua, we can remove the following lines:


local unspentnode = window.getDatabaseNode().getChild("unspentskillpoints");
if unspentnode then
if unspentnode.getValue() - total >= 0 then
unspentnode.setValue(unspentnode.getValue() - total);
else
unspentnode.setValue(0);
end
end
This way, there will be no adjustment in the unspent box.
In charsheet_skillrank.lua, a available node is made from unspent.


availablenode = window.windowlist.window.getDatabaseNode().createC hild("skillpoints.unspent", "number");

So let’s check what the availablenode does in that script.


if availablenode.getValue() - totalplanned < plannedranks then
maxranks = availablenode.getValue() - totalplanned + ranks + halfranks;
end

It subtract the planned skill points from availablenode and if it is less than planned, it will adjust the maxranks possible, to be the what is available minus what is already planned plus what ranks that have already been done. Let’s delete that, so there is no max. based on what is available.

Now we can also delete the following line:


availablenode = window.windowlist.window.getDatabaseNode().createC hild("skillpoints.unspent", "number");

Unspent get used in one more script, the charsheet_updater.lua. We neither need that anymore so delete that line, which is:


["unspentskillpoints"] = "skillpoints.unspent",

One more thing: remember the submit button on the skill sheet, first come up when planned ranks are equal what is available. Let’s make it visible at all time. Go to charsheet_skills.xml and find the submit button code (under <buttoncontrol name="submitplan">). Within there we have a line <invisible />. Delete that. Now go up under <numbercontrol name="plannedskillpoints" source="skillpoints.planned">. Within this numbercontrol we have the following code:


if points ~= 0 and points == window.unspentskillpoints.getValue() then
window.submitplan.setVisible(true);
else
window.submitplan.setVisible(false);
end

It also set the visibility of the submit button, whenever unspentpoints is equal planned points. Let’s delete those lines.

Now everything should be gone, that relates to the unspent function. Let’s delete the box within charsheet_skills.xml (everything within the <numberfield name="unspentskillpoints" source="skillpoints.unspent">)

Now the player can keep adding ranks to his skills.

Let’s do the cost. One problem with skills that does not exist with abilities, is that it’s a list where players can add and remove new and old skills, meaning the amount and titles of skills can change over time. Luckily for us, ranks are exported to the Database. I still don’t grasp the full function of how the database work, but there is a way to see what is saved and posted to the database and by looking at it, what numbers we have access too. To see what is in the database, make a campaign and fill out a character sheet. Incl. the information you need to know more about, whatever it is skills, spells or equipment. When you are done, close down the campaign, and go into your campaign folder (E.G. C:\Documents and Settings\Application Data\Fantasy Grounds II\campaigns\A Tale of Dinor). There you will find a DB.xml. Open it, and you will see all the information that is stored within the campaign. I needed to know a little bit about skills, so I filled out a character sheet with a couple of skills. I found the following was stored under skilllists in the db for the intimidate skill:


<armorcheckmultiplier type="number">0</armorcheckmultiplier>
<halfranks type="number">0</halfranks>
<label type="string">Intimidate</label>
<misc type="number">0</misc>
<plannedhalfranks type="number">0</plannedhalfranks>
<plannedranks type="number">0</plannedranks>
<ranks type="number">0</ranks>
<statname type="string">charisma</statname>
<total type="number">0</total>
I need to use the information “ranks”, and I need it from all the skills, incl. custom made one.

The following script will run over the database and pick all the numbers from ranks.


function calculatePoints()
local points = 0;

for k, v in pairs(window.skilllist.getDatabaseNode().getChildr en()) do
if v.getChild("ranks") then
points = points + v.getChild("ranks").getValue();
end
end
setValue(points);
end
First I gave this function a name “calculatePoints”. Will be used in a moment. I set the points to 0 for every time I start running this script, so it will not just keep adding up. The for k, v in pairs(window.skilllist.getDatabaseNode().getChildr en()), tells the script to look in the database under skilllist. The “if v.getChild("ranks")” tells the script that is a database entry is called “ranks” then do the following. The following is to take the value from that entry and add to points. It will take every entry of “ranks” and add all the points together. When finished it will set the value to the sum of the points in the number box, where I call this script from.

Meliora
October 2nd, 2007, 15:41
Tutorial 6: Not for the Untrained, Part 2 of 2
Remove any skill rank in Untrained Skills plus adding a cost.

To add that box on the skill sheet, let’s use the space we cleared (available skillpoints). We will add a label called Total Ranks. The box has to be a linkednumber since we are going to use this number for our Character Point left. The code of the box should look something like this:


<linkednumber name="totalskillpoints" source="cost.skillpoints">
<anchored>
<to>skillpointframe</to>
<position>insidetopleft</position>
<offset>300,15</offset>
<size>
<height>20</height>
<width>35</width>
</size>
</anchored>
<font>sheetnumbersmall</font>
<hideonvalue>0</hideonvalue>
<readonly />
<nodrag />
<frame>
<name>modifier</name>
<offset>3,3,3,3</offset>
</frame>
<script>
function calculatePoints()
local points = 0;

for k, v in pairs(window.skilllist.getDatabaseNode().getChildr en()) do
if v.getChild("ranks") then
points = points + v.getChild("ranks").getValue();
end
end
setValue(points);
end
</script>
</linkednumber>
<stringcontrol>
<anchored>
<to>skillpointframe</to>
<position>insidetopleft</position>
<offset>253,15</offset>
<size>
<height>21</height>
<width>45</width>
</size>
</anchored>
<static>Total Ranks</static>
<font>sheetlabelsmall</font>
<multilinespacing>10</multilinespacing>
</stringcontrol>

The script is still not running. It doesn’t know when to do so. It should run every time we have a skill point change. That script can be found in “charsheet_skilllist.lua” and looks like this:


function skillPointsChanged()
if not disablerecounts and window.plannedskillpoints then
-- window.skillpoints.calculatePoints();
window.plannedskillpoints.calculatePoints();
end
end
Let’s add a line too it (window.totalskillpoints.calculatePoints();) :


function skillPointsChanged()
if not disablerecounts and window.plannedskillpoints then
-- window.skillpoints.calculatePoints();
window.plannedskillpoints.calculatePoints();
window.totalskillpoints.calculatePoints();
end
end

Now the source of our box is called cost.skillpoints and in the above script we ask for totalskillpoints. To let the program know that those are alike, open “charsheet_updater.lua”. In the script there is a lot of fieldconversations. We will add this:


["totalskillpoints"] = "cost.skillpoints",

and now our script know when to run and from where to get the script. Now you can try it out in game.
The only thing we know need is to calculate the cost away from Character Point left. Just go to your charsheet.main.xml, scroll down to your cpl entry (character point left) and add the following to sources to calculate from:


<source>
<name>cost.skillpoints</name>
<op>-</op>
</source>

I only need this, since I just want each skillpoint to cost one character point. Now, if I wished to take two points for every skillpoint, I would have to make a separate calculation box as I did with abilities (see last tutorial).

Now, we need to prevent the players to take more points in skills than he have character points available. First we have to add an available box on charsheet_skills which are equal character point left.

Insert this on your charsheet_skills.xml


<linkednumber name="availableskills" source="available.skills">
<anchored>
<to>skillpointframe</to>
<position>insidetop</position>
<top>
<offset>0</offset>
</top>
<left>
<offset>0</offset>
</left>
<right>
<offset>0</offset>
</right>
<size>
<height>0</height>
</size>
</anchored>
<source>
<name>level.cpl</name>
<op>+</op>
</source>
<script>
function onSourceUpdate()
setValue(calculateSources());
end
</script>
<invisible />
</linkednumber>

I inserted it just above the submit button. You can see that it reminds a lot about the ability cost we used in the 4th tutorial. Now we need to go back to the plannedskillpoints. Just under the setValue; insert this script:


if window.availableskills.getValue() >= points then
window.submitplan.setVisible(true);
else
window.submitplan.setVisible(false);
end

And now the submit button will only be available if the amount of planned skills is no more than character points left.

In my rule-set and I assume most d20 games, there are skills a player cannot use if they do not have at least 1 rank in it.

I have thought about different ways to approach that. One was too let a line go across the skill that was untrained; another was to change the color. But those do not prevent a player to use the skill, just illustrate that it is untrained. In the end I came up with making the stat, mics and total boxes invisible if a player have 0 rank in the skill and it requires training.
Back in tutorial 3 we covered how a few features are incl. in skills, such as armorcheck, and this is where I want to add the new feature “trainedonly”. Under charsheet_skilllist.lua, I add the following to each skill that I want to be trained only:


["Disable device"] = {
stat = "intelligence",
trainedonly = 1
},
Now we just have to tell the program how to use that information. The way I did it was to follow the armorcheck function.

In the same script I found


if t.armorcheckmultiplier then
match.getDatabaseNode().createChild("armorcheckmultiplier", "number").setValue(t.armorcheckmultiplier);
end

And made a copy of that one, just under it, and change the armorcheckmultiplier to trainedonly


if t.trainedonly then
match.getDatabaseNode().createChild("trainedonly", "number").setValue(t.trainedonly);
end

Now, I set the visibility of the boxes in charsheet_skills.xml. I thought that checking for whatever or not a trained only skills had ranks would be good to set whenever total of skills was updated. So under the <numberfield name="total"> and function update(source), just below the check for armor I added this script;


if trainedonlynode.getValue() ~= 0 and ranknode.getValue() == 0 then
window.total.setVisible(false);
window.misc.setVisible(false);
window.stat.setVisible(false);
else
window.total.setVisible(true);
window.misc.setVisible(true);
window.stat.setVisible(true);
end

It checks for whatever a skill has the value trainedonly and will be called if the rank is 0. If, it will make total, misc and the stat box invisible, if not, they will be visible.

I use trainedonlynode.getValue() and as of yet, the script don’t know what the trainedonlynode is. Under function onInit(), I add the following line:


trainedonlynode = window.getDatabaseNode().createChild("trainedonly", "number");

And we are all set.
Minor Bug: The boxes Stat, Mics and Total are visible for the skills that are trained only the first time you open the sheet, and will stay so, until you either update a skill or close it. After that there are no problems and works as wanted. If you know how to fix this, please tell me, and I will update the above (and of course my ruleset :) ).

Edited: Man, I forgot. Thanks to
GoOrange
Hamish
Joshuha
Dachannien

Meliora
October 2nd, 2007, 16:02
BTW, I'm certain a lot of the above don't make sense, since I am pretty new to this and only write what I know and find out. Please, if you have any questions or need more clarification on something, just ask. I will either try to find out or I'm certain one of the vets on this forum will step in and explain it. In any case, I will then update the tutorials to be more useful for anyone that are as new as me.

Meliora
October 11th, 2007, 22:03
Don't know how useful this thread is anyhow, but if anyone is sitting out there waiting for the next step, I better add that it will take some time.

Really busy with work and the short time I have for this hobby ATM, I mainly use on working on the rules for my own private RPG.

Will return to this when time seems more plentiful.

Doombringer
January 21st, 2008, 15:47
Just want to say thank you for all the work put into this. I will be using this in my attempt at making my games better with FG.