A variety of systems in the Carrington Sheet

Characters have a number of resources – Aspects, Gear, Extras, Health, and Eldritch Powers. Together, these form the central trunk of the character sheet. In those post, we’ll look at the first three, and lay the foundation for the other two which are each a lot more complicated.

You can see the current version of this central trunk in the image – the appearance will likely change by the end, but you can see the structure and layout.

Aspects

Characters have either a fixed number of aspects, or a total that changes with character_rank. This is set in the sidebar (the next Carrington Sheet post). For now, there is a hidden attribute that sets the number of aspects. The HTML for the aspects block looks like this:

<div class="aspects grid-box">
   <button type="action" name="act_biotoggle" class="title">
      <h3>Aspects</h3>
   </button>
   <input type="hidden" name="attr_aspect_level" value="2">
   <input type="hidden" name="attr_aspect0_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect0" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect0_check" value="1">
      </summary>
      <textarea name="attr_aspects0_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect1_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect1" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect1_check" value="1">
      </summary>
      <textarea name="attr_aspects1_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect2_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect2" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect2_check" value="1">
      </summary>
      <textarea name="attr_aspects2_details"></textarea>
   </details>     
        <input type="hidden" name="attr_aspect3_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect3" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect3_check" value="1">
      </summary>
      <textarea name="attr_aspects3_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect4_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect4" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect4_check" value="1">
      </summary>
      <textarea name="attr_aspects4_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect5_hide" class="aspect-hide" value="0">
   <details class="aspect">
     <summary>
       <input type="text" name="attr_aspect5" placeholder="ASPECT" value="">
       <span class="header center">&#8595;</span>
       <input type="checkbox" class="standard" name="attr_aspect5_check" value="1">
     </summary>
     <textarea name="attr_aspects5_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect6_hide" class="aspect-hide" value="0">
   <details class="aspect">
     <summary>
       <input type="text" name="attr_aspect6" placeholder="ASPECT" value="">
       <span class="header center">&#8595;</span>
       <input type="checkbox" class="standard" name="attr_aspect6_check" value="1">
     </summary>
     <textarea name="attr_aspects6_details"></textarea>
   </details>     
   <input type="hidden" name="attr_aspect7_hide" class="aspect-hide" value="0">
   <details class="aspect">
     <summary>
       <input type="text" name="attr_aspect7" placeholder="ASPECT" value="">
       <span class="header center">&#8595;</span>
       <input type="checkbox" class="standard" name="attr_aspect7_check" value="1">
     </summary>
     <textarea name="attr_aspects7_details"></textarea>
   </details>     
</div>Code language: HTML, XML (xml)

Theres’s a lot of duplicated code here. It’s probably easy to see what’s happening with the Handlebars block used to construct this.

<div class="aspects grid-box">
   <button type="action" name="act_biotoggle" class="title">
      <h3>Aspects</h3>
   </button>
   <input type="hidden" name="attr_aspect_level" value="2">
   {{#repeat 8}}
   <input type="hidden" name="attr_aspect{{@index}}_hide" class="aspect-hide" value="0">
   <details class="aspect">
      <summary>
         <input type="text" name="attr_aspect{{@index}}" placeholder="ASPECT" value="">
         <span class="header center">&#8595;</span>
         <input type="checkbox" class="standard" name="attr_aspect{{@index}}_check" value="1">
      </summary>
      <textarea name="attr_aspects{{@index}}_details"></textarea>
   </details>     
   {{/repeat}}
</div>Code language: Handlebars (handlebars)

Here we create 8 sets of controls, each set starts with a hidden input, then a details element which displays the main elements: a place for the aspect name, a down arrow, and a checkbox. When you click the down area, a hidden textarea is revealed where you can write a more detailed description of the aspect.

This needs some CSS and a sheet worker. The character’s number of aspects starts at 2 but could be higher. There is an aspect_level attribute which sets the number of visible aspects. Each time this changes, certain attributes are set to 0, and the visible aspects are set to 1.

on('change:aspect_level sheet:opened', () => {
   getAttrs(['aspect_level'], values => {
      const aspects = int(values.aspect_level);
      const output = {};
      for(let i = 0; i < 9; i++) {
         output[`aspect${i}_hide`] = i < aspects ? 1 : 0;
         output[`aspect${i}_check`] = 0;
      }
      setAttrs(output);
   });
});Code language: JavaScript (javascript)

Then some simple CSS uses those stats to hide the entire aspect block.

div.aspects .aspect-hide:not([value="1"]) + .aspect {
    display: none;
}Code language: CSS (css)

And there we have it – the aspects block works properly. To be sure, we’ll need to add this to a sheet worker that responds to character_rank changes:

output.aspect_level = ranks.indexOf(rank) + 1 || 2;Code language: JavaScript (javascript)

This makes sure that characters have the appropriate number of aspects showing. Now, on to health.

Things, also known as Gear

Carrying and owning stuff is handled pretty abstractly, but there is a place for it on the sheet. While this is really simple by character sheet standards, it shows how much code is needed for the simplest things. First, look at the HTML:

<div class="trappings grid-box">
   <h3>
      Gear (<span name="attr_things">0</span>/
      <span name="attr_things_max">5</span>)
   </h3>
   <fieldset class="repeating_gear">
      <details>
         <summary>
            <span class="header center">&#8595;</span>
            <input type="text" name="attr_thing" value="0" placeholder="THING">
            <span class="header center">&#8595;</span>
         </summary>
         <textarea name="attr_thing_description" placeholder="DESCRIPTION"></textarea>
      </details>
   </fieldset>
</div>Code language: HTML, XML (xml)

This code creates a Gear heading, and in brackets, a number of things carried and a total number of things allowed. That will look like Gear (3/7).

Then there’s a repeating section using the details element. This is really handy, because the summary is shown all the time, and when you click a free space, the area inside the element is shown. It’s a very easy way to do a dropdown list, and one that cleans up after itself (the expanded sections are closed again next time you look at them).

So the repeating section includes a place for the name of a thing, whatever item you own, and when you click the down arrows at the side, you can enter more details on that thing is you want to (or need to).

Styling the Gear Section

The summary uses CSS grid to create 3 columns. Remember that 1fr creates 1 fraction width – it automatically fills all available space.

.trappings details summary {
    display: grid;
    grid-template-columns: 10px 1fr 10px;
    column-gap: 5px;
}
.trappings details summary input {
    width: 100%;
}
.trappings details textarea {
    width: calc(100% - 10px);
    height: 3em;
}Code language: CSS (css)

Sheet Workers Needed

Finally, there are two sheet workers. The first detects when the Prestige attribute changes (from the default of Poor), and changes the carrying capacity (things_max).

on('change:prestige', (event) => {
   getAttrs(['prestige'], v => {
      setAttrs({
         things_max: 4 + ranks.indexOf(v.prestige) || 5
      });
   });
});
on('change:repeating_gear remove:repeating_gear', () => {
   getSectionIDs('repeating_gear', ids => {
      const num = ids.length;
      setAttrs({
         things: num
      })
   });
});Code language: JavaScript (javascript)

The second counts how many rows of the repeating section are in use – this gives us the current things score. And there we have it.

Extras

Extras are special abilities and talents not covered by other rules. The space on the sheet is very similar to Gear. You have a repeating section to contain each extra, and a count of how many there are. Earlier code might be revisited to ensure the correct amount is owned.

The code for this is very similar to that used for earlier sections. Here’s the HTML for the layout.

<div class="extras grid-box">
   <h3>Extras (<span name="attr_extras"></span>/
               <span name="attr_extra_level"></span>)</h3>
   <fieldset class="repeating_extras">
      <details>
         <summary>
            <input type="text" name="attr_extra" value="" placeholder="EXTRA">
            <span class="center">&#8595;</span>
            <input type="checkbox" name="attr_extra_check" class="" value="1" />
         </summary>
         <textarea></textarea>                
      </details>
   </fieldset>
</div>Code language: HTML, XML (xml)

The extra_level attribute here is set in the xp-floater, where you buy your Extras. In a repeating section, you can pick any number of items, but here you are limited by how many Extras you have actually bought.

Styling The Extras

And here we have the CSS used to style relevant parts.

.charsheet .extras details summary {
    display: grid;
    grid-template-columns: 1fr 10px 30px;
    column-gap: 5px;
}
.charsheet .extras details summary input[type="text"] {
    width: 100%;
}
.charsheet .extras details textarea {
    height: 3em;
    width: calc(100% - 10px);
}Code language: CSS (css)

The Lone Sheet Worker

This uses the same sheet worker as Gear, counting how many items there are in the repeating section, with just the name removed.

on('change:repeating_extras remove:repeating_extras', () => {
   getSectionIDs('repeating_extras', ids => {
      const num = ids.length;
      setAttrs({
         extras: num
      })
   });
});Code language: JavaScript (javascript)

These two workers can easily be combined, especially if the repeating section name is changed to repeating_things.

['things', 'extras'].forEach(w => {
   on(`change:repeating_${w} remove:repeating_${w}`, () => {
      console.log({w});
      getSectionIDs(`repeating_${w}`, ids => {
         const num = ids.length;
         const output = {};
         output[w] = num;
         setAttrs(output);
      });
   });
});Code language: JavaScript (javascript)

Writing code this way doesn’t make any difference to performance, but it can make the code easier to maintain. You don’t have to worry about typos, and if you change each sheet worker, you change both at once. This makes the biggest benefit when you have many similar things (like, say, the stat modifier calculation in D&D, where there are 6 identical sheet workers, or calculating skill modifiers when there might be dozens).

However, when one item has significant differences to the other, it can be worthwhile keeping them as seperate workers. We ciould do that here, but choose not to. We limit the number of Extras to never exceed those bought by hiding the Add button the repeating section, using CSS.

Since CSS cannot perform arithmetic, we create a sheet worker to create an attribute with a value of 0 or 1, and place this in the code. This is the extras_not_allowed attribute.

Extras Add Button Sheet Worker

In this worker, level is the number allowed through the advance Extras button, while section is the number actually bought through the repeating section.

    on('change:extra_level change:extras', () => {
        getAttrs(['extra_level', 'extras'], v => {
            const level = int(v.extra_level);
            const section = int(v.extras);
            const permission = level <= section ? 1 : 0;
            setAttrs({
                extras_not_allowed: permission
            });
        });
    });
Code language: JavaScript (javascript)

Extras Add Button CSS Code

This creates a number of 1 or 0, which is then used by this CSS Code.

.charsheet .extras .extras-not-allowed[value="1"] ~ 
           .repcontrol[data-groupname="repeating_extras"] .repcontrol_add {
    display: none;
}Code language: CSS (css)

Affection repeating sections with CSS can be tricky, because you have to use the data_groupname attribute. The Add button has the class repcontrol_add, and is inside the repcontrol class.

Extras Add Button HTML

Finally, to bring this altogether you need a single line of html. Here it is added before the Extras heading.

<div class="extras grid-box">
   <input type="hidden" name="attr_extras_not_allowed" class="extras-not-allowed" value="0">
   <h3>Extras (<span name="attr_extras"></span>/
               <span name="attr_extra_level"></span>)</h3>Code language: HTML, XML (xml)

This little section took way longer than is reasonable, because of my typos!

In Conclusion

Next, we’ll look at Eldritch Powers, which has a lot of complexity to create the big block in the middle, and the Health and Conditions which is even more complex.

Series Navigation<< Buying an Ability in the Carrington SheetEldritch Powers in the Carrington Sheet >>

Leave a Reply

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