#Boilerplate Item Rolls

1 messages · Page 1 of 1 (latest)

zealous shard
#

@sour thistle I'm starting a thread since we may get a bit in the weeds on this. Can you post what your _onRoll() looks like (assuming it's been changed from what's in Boilerplate out of the box)?

sour thistle
#

Sounds good, I haven't changedit just yet, i've been reading it trying to understand it

#
    event.preventDefault();
    const element = event.currentTarget;
    const dataset = element.dataset;

    // Handle item rolls.
    if (dataset.rollType) {
      if (dataset.rollType == 'item') {
        const itemId = element.closest('.item').dataset.itemId;
        const item = this.actor.items.get(itemId);
        if (item) return item.roll();
      }
    }

    // Handle rolls that supply the formula directly.
    if (dataset.roll) {
      let label = dataset.label ? `[roll] ${dataset.label}` : '';
      let roll = new Roll(dataset.roll, this.actor.getRollData());
      roll.toMessage({
        speaker: ChatMessage.getSpeaker({ actor: this.actor }),
        flavor: label,
        rollMode: game.settings.get('core', 'rollMode'),
      });
      return roll;
    }
  }

}```
zealous shard
#

Gotcha, just a sec

sour thistle
#

I think that's all of it?

zealous shard
#

So the props that I mentioned should all be available in the dataset variable assuming they're named something like data-foobar. For example, if there was a data-roll="data.formula2" on the element, you could retrieve it by adjusting the first if statement in that method to look like this:

  // Handle item rolls.
  if (dataset.rollType) {
    if (dataset.rollType == 'item') {
      const itemId = element.closest('.item').dataset.itemId;
      const item = this.actor.items.get(itemId);
      // Retrieve the roll prop.
      const roll = dataset.roll ?? null;
      // Pass it in a new options object argument.
      if (item) return item.roll({roll: roll});
    }
  }
#

However, the item's roll() method doesn't know what to do with that yet, so it would need some changes too.

sour thistle
#

so that's replacing the first block for if dataset.rollType =='item'

zealous shard
#

Yeah

sour thistle
#

So looking at the roll() command in items.mjs

Since there is roll data, the roll() goes to the else { which creates 2 variables, rollData = this.getRollData() and roll = new Roll(rollData.item.formula)
But if we have 2 possibilities for what item.formula looks like

zealous shard
#

Here's an example of what the item class' revised roll() method might look like:

/**
 * Handle clickable rolls.
 * @param {Event} event   The originating click event
 * @private
 */
async roll(options = {}) {
const item = this.data;

// Initialize chat data.
const speaker = ChatMessage.getSpeaker({ actor: this.actor });
const rollMode = game.settings.get('core', 'rollMode');
const label = `[${item.type}] ${item.name}`;

// If there's no roll data, send a chat message.
if (!this.data.data.formula) {
  ChatMessage.create({
    speaker: speaker,
    rollMode: rollMode,
    flavor: label,
    content: item.data.description ?? ''
  });
}
// Otherwise, create a roll and send a chat message from it.
else {
  // Retrieve roll data.
  const rollData = this.getRollData();

  // Default to the item's formula.
  let formula = rollData.item.formula;

  // If there's an alternate field passed in, try to retrieve it instead.
  if (options?.roll) {
    formula = getProperty(item.data, options.roll) ?? formula;
  }

  // Invoke the roll and submit it to chat.
  const roll = new Roll(formula, rollData);
  // If you need to store the value first, uncomment the next line.
  // let result = await roll.roll({async: true});
  roll.toMessage({
    speaker: speaker,
    rollMode: rollMode,
    flavor: label,
  });
  return roll;
}
#

My changes in there are basically adding the argument and then lines 28-37.

#

It also assumes that the field you're passing in is a valid formula. So if you had the item object and could access it at item.data.data.formula2, something like this should work.

#

On your button, it would be something like:

<div class="rollable" data-roll-type="item" data-roll="data.formula2">{{item.data.formula2}}</div>
sour thistle
#

My buttons for both of my rolls look like

        <div class="item-formula item-prop rollable" data-roll-type="item" roll-value="{{item.data.formula2}}">{{item.data.formula2}}</div>
        <div class="item-formula item-prop rollable" data-roll-type="item" roll-value="{{item.data.formula}}">{{item.data.formula}}</div>
zealous shard
#

Right, so you would change

roll-value="{{item.data.formula2}}"

to

data-roll="data.formula2"
#

In this case, we can get by with a string since the changes to the roll() method use the getProperty() method

#

getProperty() is a Foundry built in that lets you retrieve a nested property from an object. For example:

getProperty(actor.data, 'data.abilities.str.mod')
#

With that all said, if you want to just pass the raw value in directly and not generate the roll from the item, you could change the roll type to on the button to the one that supports passing in raw roll formulas. The downside to that approach is that if you instead use the item.roll() method, you future proof it and allow yourself to call it in other contexts (e.g. macros).

sour thistle
#

I've run a test after making the changes and error seems to be persisting,

zealous shard
#

If you take the raw formula approach, this is what that would look like and shouldn't require any JS changes at all:

<div class="item-formula item-prop rollable" data-label="Formula 2 Roll" data-roll="{{item.data.formula2}}">{{item.data.formula2}}</div>
zealous shard
sour thistle
#

Both buttons are rolling the 1 formula associated with data.formula

sour thistle
#

seems to be working from the first test

zealous shard
#

Yeah, the raw formula setup is the easiest and comes built into Boilerplate as a convenience. The only real downside is that it's limited to only being callable from the sheet; if you want to call it from a macro or elsewhere, it's not really capable of that.

sour thistle
#

So how is it called instead of calling the itemroll() function?
Is it by not including data-roll-type="item" ?

zealous shard
#

Yeah, if you look at the _onRoll() method, that first if statement checks for the data-roll-type prop (in dataset.rollType) and rolls it using the item's roll() method instead. If that's missing, it assumes you're passing in a formula and label.

#

On that note, data props get converted into camelCase automatically ^

sour thistle
#

So does it function as an

     item.roll()
else: 
     use raw formula

?
I understand that it's a vast simplification, but I'm just checking

zealous shard
#

Yeah, the logic is

if (dataset.rollType) {
  // Roll from item, return early
}
if (dataset.roll) {
  // Construct a new Roll() from scratch
}
sour thistle
#

Awesome

Thank you so so so much for your help, I understand you've taken the time to help me with my issues, but I think that's the last major thing I needed to get done for the character sheet for the system.
Your help is truly invaluable, thank you so much for your time.

zealous shard
#

Thanks for the questions! This is helpful for me to, as it's showing a gap in the Boilerplate system/tutorial where I could probably expand on the concepts to show some alternate usages. I'll likely revisit that section in a month or two to make this easier for others in future.

sour thistle
#

I'm glad my 4 day coding marathon has at least been helpful in some regard.

zealous shard
#

If you do want to take another pass at the item roll setup now or as a feature enhancement at some point in the future, the dnd5e system has a much more advanced roll method on its item class that's way more powerful than what I show how to do in Boilerplate. That makes it harder to grasp at first, but if you step through the code's logic, you may be able to pull out some useful bits to use in your own system.

sour thistle
#

I started looking at the 5e system, but the only language I really learnt was python.
I've started working on the sheet because in my group, the person who was working on it, their pc broke and they didn't have a backup, so lost everything.
So I've essentially been on speedrun mode trying to get back to a working sheet, I only generally went over html, css and js during comp sci modules at A level, and it's been nice to work on it.
One of my plans for the summer was to get back into coding again, so it's been a convenient reentry into programming.

#

Looking at more complicated Js is quite hard for me, I can sort of understand the general concepts of what things are doing in Js looking back to the general programming principals etc.

sour thistle
zealous shard
#

That's understandable, and dnd5e does have a lot of more advanced coding techniques going on in it (in particular, stuff like reduce() on arrays can be a hard syntax to follow).

#

I would definitely recommend keeping several systems installed locally to pilfer for ideas, it's how I got started back in the day when it was just a more limited version of the API documentation. Having both dnd5e and Simple Worldbuilding installed to run quick searches on in my code editor was invaluable.

#

And also this channel! Asking here when you get stuck or when you're just not sure what to even search for is always a good call.

sour thistle
#

Thank you so much for your time, your work is really incredible and your help you provide to the community is invaluable to so many people like me trying to get their heads around foundry and it's api etc.

#

❤️

pallid sequoia
#

I'm just stalking here to learn new things. Although I just started to develop my system, I'd like to say that your tutorial is helping me a lot too @zealous shard . I'm a frontend developer, so some JS stuff are not alien to me, but there's a lot of things (specially OOP) that are really hard to grab.

zealous shard
#

I'll also make a v10 branch of the Boilerplate system at some point in the next few weeks to show a good example of what things have to be changed to make a v10 upgrade of an existing system.

pallid sequoia