Skip to content

How to add system support

Averrin edited this page Nov 27, 2022 · 11 revisions

Before we start

  • Install the Koboltworks Data Inspector. It's a "must have" for every developer. Also, Alpha Suit integrates with the inspector and adds "inspect" buttons to observe data structure of items. It is especially helpful for compendium entities.
  • Basic compendium filters can be done without any coding skills. But more advanced requires some js knowledge. Adding inline "info widgets" or "selected document info pages" needs at least basic Svelte skills.
  • The module is based on TyphonJS so you can use its documentation to start working with Alpha Suit. TLDR: npm install & npm run dev then open http://localhost:30001
  • Do not hesitate to ask me anything in Discord: Averrin#0374
  • Use dnd5e or pf2e as example. This documentation can be incomplete or outdated.
  • Filters are based on Filtrex lib. It's necessary to understand its syntax.
  • Enable Browser advanced mode in Compendium Browser settings. It will help you to debug your filters.
  • You may be confused by inconsistent using of data and system. In general, system is a modern v10 alternative to data. In most cases, you still can use data. But it doesn't work if we are speaking of compendium indexing. I made a couple of hacks to mitigate this difference. You can use either data and system everywhere, but direct getIndex calls.
  • Unfortunately, most of system-related change will cause full page reloading. And even worse, system-specific widgets requires manual page refresh to apply changes.

First steps

We will use pf1 as an example. Please note, at the moment of its writing, this system isn't supported.

  • create file src/systems/pf1.js (a file name will use only by you for importing)

With content like

import System from "../modules/system.js";

const pf1 = new System({
  id: "pf1",  //this one MUST be the same as your `game.system.id`
  tabs: [],
});

export default pf1;

It's a minimal viable configuration.

  • go to src/systems.js and add your system like others.
import pf1from "./systems/pf1.js";
addSystem(pf1);
  • In the Alpha Browser's tab line, the tag with system ID should become purple instead of red.
  • Congratulations, your system is now supported!
  • Profit!

Tabs

First of all, you may want to edit tab set for Compendium Browser. Let's see pf2e example.

  tabs: [
    ...
    {
      title: "Items", // Title
      icon: "fa-solid:suitcase", // Any Icon id from https://icon-sets.iconify.design
      type: "Item", // `metadata.type` of compendium. Will use to filter displayed items
      subtypes: ['equipment', 'consumable', 'armor', 'weapon', 'kit', 'treasure', 'backpack'], // list of `type` of items. Also using for filtering. You can omit this field to select all subtypes
    },
    ...
  ]

This one is quite straightforward but can boost your system experience.

Sortings

And again, pf2e for the win.

  sortings: {
    "Items": [ // reference to the tab's title (yeah, i know, i should make it id, but it's a title right now)
      {
        label: "type",  // displaying sorting's label
        query: "@type",  // e.g `@data.details.level.value` (see inspector for the right path. Tip: right click in the inspector window will show you path in the input) or it can be an "alias" (see below).
        asc: true, desc: true, // showing buttons for each direction
      },
    ],
    "NPC": [
      {
        label: "level",
        query: "@level",
        asc: true, desc: true,
      },
    ]
  },

Aliases

like here. It can be used in any filtrex query, even in search inputs. Beware, sometimes using a full path is better. I don't remember why and where, sorry.

Note: do not use @ sign in aliases, it using only to replace query part before compilation.

Filters

The most complicated part. Please use examples from the actual code, your brain and coding skills.

If you want to tweak some logic, do it here. Yeah, it's a mess, I know. =(

Structure

    filters: {
        "%TAB_NAME%": {
            "%FILTER_CATEGORY%": [
                {%filter here%}
            ]
        }
    }

vivaldi_NukuF9ikik

Compare number

It's a simple component, but there are some pitfalls.

        {
          label: "Price (gp)",
          control: "compare-int",
          attribute: "@data.price.value.gp" // filtrex query
        },

        {
          label: "Weight",
          control: "compare-int",
          attribute: "float(@data.weight.value)"
        },

Sometimes (more often than should be) numeric attributes are stored as strings. For correct results we should convert it into the number. Here is used float function. You can find list of supported functions here (please, do not click this link, if you wanna keep your mind safe. You are warned.)

String

If you want to search in somewhat like description, use control: "string". It's a very heavy operation, use it wisely.

        {
          label: "Desc",
          control: "string",
          attribute: "@data.description.value",
          template: "${attribute} ~= \"${value}\"",
        },

Here we can see a new "attribute - template" approach. template is using to build a query using two properties: attribute from an entity and value from the control. Watch the quotation marks!

Select

It's simple but flexible control, suitable for boolean comparisons or for the cases where no need for or logic. There are a couple of different ways to use this control.

        {
          label: "Attunement",
          control: "select",
          options: [
            { value: ``, label: "" },
            { value: `@data.attunement != 1`, label: "Not Required" },
            { value: `@data.attunement == 1`, label: "Required" },
          ]
        },

Just inline query as value.

        {
          label: "Env",
          control: "select",
          attribute: "@data.details.environment",
          options: () => environments,
        },

Familiar "attribute" approach and options as a function. It should to be a function because the environments variable isn't populated yet and will be built in init function of the System itself. Here you can find a couple of examples how to build complex options list from the existing compendium. Sometimes it just too big to be inline.

Multiselect

Now you are more or less familiar with all used concepts.

        {
          label: "Damage Type",
          control: "multiselect",
          attribute: "getDamageTypes(@data.damage)",
          template: "${value} in ${attribute}",
          options: [
            { value: ``, label: "" }, // TODO: add empty option automatically
            ...damageTypes
          ], logic: "or"
        },

Imported and inlined list of options. Just another way to do it.

logic is multiselect's specific property. It affects how to build the resulting query when more than one option is selected.

Extra info widgets

vivaldi_HMaibcP3U7

  extraInfo: {
    "Items": { component: ItemsData, index: ["data.price", "data.weight", "data.armor.value", "data.rarity"] },
    "spell": { component: SpellsData },
  },

There are two moments simultaneously.

  • "%TAB_NAME%": {} for the Browser. It's necessary to add all used fields to the index property (otherwise unimported entities will be empty).
  • "%SUBTYPE%": {} for the Tree. These entities are imported, so all properties are filled.

Use *Data components from src/systems/dnd5e as a reference.

Selected entity info

vivaldi_FyzZZwky8w

  selectedInfo: {
    "Actor": {
      "npc": { component: SelectedNPC },
      "character": { component: SelectedCharacter },
    },
    "Item": {
      "spell": { component: SelectedSpell },
      "weapon": { component: SelectedWeapon },
      "equipment": { component: SelectedItem },
      "loot": { component: SelectedItem },
      "tool": { component: SelectedItem },
      "consumable": { component: SelectedItem },
      "feat": { component: SelectedItem },
    }
  },

Here is "Type -> Subtype" structure. Please consider the fact that item is a svelte store. All examples support basic cases of reactive updates of the given item.

Use Selected* components from src/systems/dnd5e as a reference.