- Anatomy of a Sheet Worker
- Events, and watching Attributes
- Variables – How to Name Things
- Arithmetic in Sheet Workers
- What If? in Sheet Workers
- JavaScript Objects
- Getting Loopy With JavaScript
- Logging in the Browser Console
- Strings, Arrays, and Loops
- Asynchronicity and Things to Avoid With Loops
- Changes and the eventInfo Object
- Action Buttons
- setAttrs and Saving Attributes
- Castle Falkenstein Design – Sheet Workers
- The Perils of Sheet Worker Functions
- The Script Block and Identifying Characters
- Arrays and Dropdowns
- Undefined and Other Error Values
- The Ternary Operator – The One-Line If
- Template Literals
- Functions and the Fat Arrow
- Strings in Sheet Workers
- A Sheet Worker Reprise
It’s handy to know how a sheet worker is triggered, and that’s what we covered in the last post. In this post, we’ll look at an optional property, eventInfo, which you can use for some interesting things.
The eventInfo Object
When you create a sheet worker, you can create an eventInfo object, like so.
on('change:attribute', function(eventInfo) {
console.log(eventInfo)'
});
/*
Notice the extra thing here is the word between the function() brackets.
eventInfo will be an object like so:
eventInfo = {
newValue: 'whatever'the attributes new value after the change',
previousValue: ' the attributes value before the change happened',
sourceAttribute: 'attribute',
sourceType: 'will be "player" or "sheetworker" - who triggered it?'
}
*/
Code language: JavaScript (javascript)
When you declare an eventInfo object, the object includes the properties shown above. There are a couple of extra bits of information that might be included which you’ll learn about later, but these four are the bits you’ll use most often. Read on to find out what you might use each for.
Note that the name eventInfo is a variable. You can change the name. Here are a couple of examples:
on('change:attribute', function(event) {
console.log(event)'
});
on('change:attribute', function(e) {
console.log(e)'
});
Code language: JavaScript (javascript)
Just remember to always use the name you have declared, as in the console.log commands.
newValue – Getting Values without using getAttrs
If your sheet worker is monitoring a single attribute, you don’t need to use getAttrs. newValue tells you what that attribute’s value is. These two workers are functionally identical.
on('change:attribute', function() {
getAttrs(['attribute'],function(values) {
const score = +values.attribute || 0;
console.log({score});
});
Code language: JavaScript (javascript)
on('change:attribute', function(event) {
const score = +event.newValue || 0;
console.log({score});
});
Code language: JavaScript (javascript)
If you have multiple attributes, you may need to know which attribute triggered. That’s where sourceAttribute comes in.
sourceAttribute
Let’s say your game has three attributes, Body, Brains, and Charm. Each has an attribute modifier and you use a single sheet worker to calculate the bonus for all three.
on('change:body change:brains change:charm', function(event) {
// get the attribute's value
const score = +event.newValue || 0;
// calculate its new modifier
const mod = Math.floor(score/2);
// find out which stat it was
const which = event.sourceAttribute;
// save the stat. Since we are using a variable for the stat name, enclose it in [ ] brackets.
setAttrs({
[which + "_mod"]: mod
});
});
Code language: JavaScript (javascript)
The setAttrs uses the ability to add strings together (string concatenation). if you need a refresher, check out the initial Strings post.
Notice that this worker triggers when an attribute changes. You can’t use it to set initial values. So make sure you have the initial defaults for the stat modifiers set correctly.
Methods like this can simplify sheet workers – you can cut down on a lot of duplication. There’s three sheet workers in one here. But there are limits.
Let’s say you have a hit point stat calculated by adding Constitution and Size together. Since you need to know both stats, you must use getAttrs. eventInfo can only give you information about the one attribute that was changed.
previousValue – Calculating the Magnitude of a Change
When you want to know how much an attribute changed, you can use previousValue in combination with newValue. Let’s say you use a Hits stat to track hit points, but if any blow exceeds your Wound threshold it is a severe wound. So for this, you need to know how much damage a single blow did. This is where previousValue comes in.
on('change:hits', function(event) {
getAttrs(['threshold', 'hits', 'hits_max', 'wounded'], function(values) {
// create an object to hold the outputted attributes
const output = {};
// get the wound threshold we'll need to use later.
const threshold = +values.threshold || 0;
// get the new hits value. This is after the blow.
const hits = +values.wound || 0;
const max = +values.hits_max || 0;
// ensure the new hits total cannot exceed max. The character may have been healed.
output.hits = Math.min(hits, max);
// using Math.min here, we choose the lowest stat, so hits can never exceed max.
// the sheet uses a wounded attribute.
// this is a marker for if the character already has a severe wound.
const wounded = +values.wounded || 0;
// now for the event stuff.
const hits_new = +event.newValue || 0;
const hits_old = +event.previousValue || 0;
// this tells use the new and old attribute, but we need to know how much the stat changed
const change = hits_old - hits_new;
// the order matters here. By putting hits_old first, wounds are positive, and healing is negative.
// now we need to check if the hit at least eqals wound threshold.
if(change >= threshold && wounded === 0) {
// if the wound as bigger than the threshold, the character is now severely wounded.
// only do this check if the character is not already severely wounded.
output.wounded = 1;
} else {
// if the character did not become heavily wounded, make sure it is zero.
output.wounded = 0;
}
// update the sheet with both saved attributes
setAttrs(output);
});
});
Code language: JavaScript (javascript)
This is an involved sheet worker which handles whether a character was healed or suffered minor and major wounds. You’d mark whether the character is healed from severe wounded status separately, but once you do, the character can be wounded again.
sourceType – stopping Sheet Workers From Running
There are times you need to know if a sheet worker was triggered by the player or by a sheet worker. Let’s say characters have a special ability they can only use once a session. So, you create a checkbox, ‘ability’, to show this. The players can check this to mark it used, and when unchecked the ability is available to be used. So far so good.
But you decide it would be handy that players can only check the ability, not uncheck it. One way to do this:
on('change:ability', function(event) {
// need to get the value, because we need to know if the box is checked or unchecked
const value = +event.newValue || 0;
// find out if it is triggered by manual action or a sheet worker
const trigger = event.sourceType;
// remember the worker is only triggered if a change has occurred, so from 1 to 0.
if(trigger === player && value === 0) {
setAttrs({
ability: 1
});
}
});
Code language: JavaScript (javascript)
This worker allows players to check the box, but if they try to uncheck it, it will be checked again.
You will need another sheet worker to uncheck the box. Maybe there are several one/session resources, and you use a separate checkbox for all of them, and a separate worker that clears them all (sets them all to 0). The setAttrs in the above worker won’t be triggered by another sheet worker! This is the perfect job for an action button (see next post).
setAttrs and Silent Workers
When a sheet worker runs, it can create cascading effects. So if you change one stat, it can change that stat’s modifier, which then changes all skills or saving throws based on that stat. But there are times you don’t want this kind of cascade to run.
You can easily set sheet workers to be silent; that is, they don’t trigger other sheet workers.
// setAttrs normally looks like this.
setAttrs({
attribute: value
});
// but you can make it silent like this:
setAttrs({
attribute: value, {silent: true}
});
Code language: JavaScript (javascript)
Just add {silent:true} after all attribute changes, and that sheet worker will be silent.
Be careful – if you are relying on cascading effects, this stops them from happening. But that is sometimes desirable.
There’s more to discuss about setAttrs, but it seemed appropriate to mention this feature here.
Caveat
Some of us are a little wary about using eventInfo, specifically because of past issues with newValue and previousValue. In the past there was a bug with scripts that changed attributes sometimes not triggering sheet workers that relied on eventInfo, because scripts didn’t trigger newValue changes.
Right now, at the time of writing (March 2023), there seems to be a bug with newValue and previousValue being absent from changes to _max attributes.
EventInfo provides some very interesting features, but I avoid using it where there’s an alternative. GetAttrs usually does the job. But there are some things that can’t be done without eventinfo.