JavaScript Objects

I keep finding posts I need to write before the next post on Loops. JavaScript contains a special object, literally called an Object, which is kind of like an array but ordered by name.

Javascript Objects and setAttrs?

Each object has keys, which are the names of things inside the object, and values which are the things inside the object. Here’s an example of an object:

const stats = {
   str: 13,
   dex: 7,
   wis: 18
}   Code language: JavaScript (javascript)

This is the stats object, which contains 3 keys: str, dex, and wis, and each of those keys has values (13, 7, and 18).

Notice how similar this is to setAttrs:

setAttrs({
   str: 13,
   dex: 7,
   wis: 18
});   Code language: JavaScript (javascript)

In this example, you would update the sheet, assigning values to three stats 13, 7, and 18.

It’s not a coincidence that these are similar. The setAttrs command expects objects, so if you had already created the stats object, you could do this:

setAttrs(stats);   Code language: JavaScript (javascript)

This is very useful, and we’ll come back to that. You can create an empty object like this:

const stats = {};Code language: JavaScript (javascript)

then assign keys and values like this.

stats.str = 13;
stats.dex = 7;
stats.wis = 18;Code language: JavaScript (javascript)

And getAttrs is an Object too

At this point, you would have the stats object shown at the stat of this post, and use setAttrs to save it. But let’s see something else: when you want to get a value out of the object, you do this:

const a_stat = stats.str;
stats.dex = 7;
stats.wis = 18;Code language: JavaScript (javascript)

Here we have extracted the str value from an existing object, and saved in a variable named a_stat. Do you how this is similar to getAttrs:

getAttrs(['str', 'dex', 'wis'], function(stats) {
   const str = +stats.str || 0;
   const dex = +stats.dex || 0;
   const wis = +stats.wis || 0;Code language: JavaScript (javascript)

Yes, the getAttrs function creates an object. You give it a list of attribute names in an array ([‘str’, ‘dex’, ‘wis’]) then the following function creates an object variable, and saves the supplied attribute names as keys, with their values. Then if you so desire, the last three lines show you extract those attributes and store them in distinct variables.

Putting It All Together – Object Databases

So getAttrs and setAttrs both work with JavaScript Objects, but Objects are incredibly useful on their own. Imagine this situation. You want to create a weapon drop down, and when the player chooses that weapon, various modifiers are applied. So you’d start with a select in html, like this:

<select name="attr_weapon">
    <option selected>None</option>
    <option>Dagger</option>
    <option>Sword</option>
    <option>Greatsword</option>
    <option>Long Bow</option>
</select>Code language: HTML, XML (xml)

In reality, you’d probably have a longer list of weapons. That last one includes a space – that will need to be dealt with later.

Then you have the weapon’s properties, and a button for attacks that takes those into account:

<input type="text" name="weapon_damage" value="" readonly>
<input type="text" name="weapon_stat" value="" readonly>
<button type="roll" name="roll_attack" value="&{template:default} {{name=Attack with @{weapon} }} {{Attack=[[1d20+@{bab}+@{weapon_stat}]] }} {{Damage=[[@{weapon_damage}+@{str_mod}]]}}">Attack!</button>Code language: HTML, XML (xml)

Now, you would probably have more properties – this is just an example. Each stat is readonly, because the player can’t change them manually and for that you need a sheet worker. We start by creating the shell that is very similar in all sheet workers:

on('change:weapon', function() {
    getAttrs(['weapon'], function(values) {
       
    });
});Code language: JavaScript (javascript)

Now we start to create the ‘body’ – the part of the code that does the actual work. We want to grab the name of the weapon from the dropdown, then change all the weapon properties to match that weapon. We do this by creating a weapons object. Like this:

        const weapons = {
            None: {weapon_damage: 0, weapon_stat: 0},
            Dagger: {weapon_damage: '1d4', weapon_stat: '@{str_mod}'},
            Sword: {weapon_damage: '1d8', weapon_stat: '@{str_mod}'},
            Greatsword: {weapon_damage: '2d6', weapon_stat: '@{str_mod}'},
            "Long Bow": {weapon_damage: '1d8', weapon_stat: '@{dex_mod}'},
        };  
Code language: JavaScript (javascript)

Now we have a database of weapons. Notice how the value of each entry is itself another object (with a weapon_damage and weapon_stat key). Also notice that these keys match exactly the name of the attributes on the sheet you want to update with those values.

Because the last name, Long Bow, contains a space we need to wrap it in quotes. If a key does not fit the format for a variable name, it must be in quotes.

Now we need to code that updates the attributes on sheets to match the chosen weapon:

        const weapon = values.weapon;
        const weapon_stats = weapons[weapon];
        setAttrs(weapon_stats);    
Code language: JavaScript (javascript)

That’s all it takes.

You can extract the value of an object key when you don’t know what that key is before hand. So we could use weapon.Sword, but since we don’t know ahead of time which will be chosen, we use a variable ‘weapon’, and then we have to use the [ ] syntax to grab its data.

You have to do this with ‘Long bow’ too (weapon[“Long Bow”] not weapon.long bow) since that doesn’t fit variable syntax and would break.

Since the object keys and the javascript object keys are identical, we don’t have to worry about what happens when the key is not found in the object. We know the key always exists.

Be careful with capital letters – the value in the select must match exactly the key – that’s why the key is ‘Sword’ and not ‘sword’.

So weapon_stats contains the entire value, which is itself an object. setAttrs expects an object, so you can give it one. The entire sheet worker then look like this:

on('change:weapon', function() {
    getAttrs(['weapon'], function(values) {
       const weapons = {
            None: {weapon_damage: 0, weapon_stat: 0},
            Dagger: {weapon_damage: '1d4', weapon_stat: '@{str}'},
            Sword: {weapon_damage: '1d8', weapon_stat: '@{str}'},
            Greatsword: {weapon_damage: '2d6', weapon_stat: '@{str}'},
            "Long Bow": {weapon_damage: '1d8', weapon_stat: '@{dex}'},
        };
        const weapon = values.weapon;
        const weapon_stats = weapons[weapon];
        setAttrs(weapon_stats);    
    });
});Code language: JavaScript (javascript)

Summary

In summary, JavaScript objects are kind of like arrays, but the individual items are grabbed by their keys. Here are some things to remember.

const empty_object = {}; // create am empty object with {}
const stats = {
     str: 13,
     dex: 7,
     wis: 18,
     'a long name': 17;
}; // you can create an object with values already
// likewise if the key isnt a valid variable name (e.g. contains spaces) put quotes around the key.

const str = stats.str; // you can grab an item's value using its key
const str = +stats.str || 0; // if you aren't sure if its a number but want a number, you can include the usual error checking.
const score = stats[this_stat]; // if the key is dynamic, or if its not a avalid variable name, use [ ] brackets.
Code language: JavaScript (javascript)

The getAttrs and setAttrs functions both work with objects, and these objects are interchangeable with ones you create. You can use this to your advantage as seen above.

There’s a lot more that can be said about Objects that we’ll likely cover in the future, but that basically covers all you need in roll20 character sheets.

Here are some other things to look up if you are interested:

  • Object.keys, Object.values, and Object.entries
  • hasOwnProperty
  • delete
  • reduce (especially as a way to sum some Object.values)
Series Navigation

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.