Charactermancer Development
From Roll20 Wiki
Page Updated: 2023-10-21 |
This is related to Editing(coding) Character Sheets, which require Pro info to be able to use.Main Page: Building Character Sheets |
The Charactermancer is subsystem of the character sheets, that enables the creation of a step-by-step character creation & levelup system for users. It was first implemented on the D&D 5E by Roll20 and Roll20 Official Pathfinder character sheets.
Character Sheet Development
Getting Started
- Using Custom Sheets
- Building Sheets
(Main Page) - Glossary
- Code Restrictions
- Best Practice
- Common Mistakes
- Tutorials
- Examples, Templates
- Pattern Libraries
- HTML & storing data
- CSS & Styling
General
- Updates & Changelog
- Known Bugs
- Character Sheet Enhancement(CSE)
- Custom Roll Parsing
- Legacy Sheet(LCS)
- (upcoming) Beacon SDK
Reference
- Buttons
- Repeating Sections
- Sheetworkers
- Roll Templates
- sheet.json
- Translation
- Auto-Calc
- Advanced
- All SheetDev Pages
Tools & Tips
Other
Creating a Charactermancer for a sheet requires the sheet to have some form of Compendium Integration.
These tips & examples are made to work with Legacy Sheet, so might need adjustments to work with newer CSE-sheets. See the Character Sheet Enhancement-page for more info. Andreas J. (15 April 2021) |
Examples
Sheets that contain charmancer code
- Shadowrun5thEdition (only used for sheet import from text file)
- Esper Genesis 5E
- Mothership Official
- Zafir
- "By Roll20" sheets (public code is old)
- D&D 5E by Roll20
- Starfinder by Roll20 (incomplete)
- Pathfinder by Roll20
- more "By Roll20" sheets have Charmancers, but their sourcecode isn't publicly available.
Translation
Main Page: Character Sheet Translation
Like the rest of the areas of the Character Sheet, Sheet Standards should be followed in the Charactermancer. This includes Internationalization (or i18n translation keys). Any element can be designated to translate by adding the data-i18n
-tag to most html elements such as <span>
, <h1>
, <div>
.
<span data-i18n="adv-u">ADVANTAGE</span>
Entities
Slides
Intro Slide
<charmancer class="sheet-charmancer-intro">
The Charactermancer functions by loading a series of slides in order. The slides are blocks of HTML included in the character sheet's HTML file. They're designated with a <charmancer>
-element. Each slide needs to be named in order to be called or referenced which is done from inside the <charmancer>
-element by adding a class prefaced by sheet-charmancer-
followed by the name of the slide. Slides may contain all of the same HTML elements and inputs as the character sheet along with some notable additions and exceptions outlined below. Slides are either called by a Sheetworker via the startCharactermancer(<slide name>)
-function, or by another slide via the the "next" button.
Compendium iFrame
<div class="sheet-compendium-page sheet-example" accept="CategoryIndex:Backgrounds"></div>
Available inside slides is the Compendium iFrame which allows you to display pages from the character sheet's Compendium. If a <div>
-element inside a slide is given the class sheet-compendium-page
and is also given an accept parameter, when the slide is rendered it will also include the Compendium iFrame embedded inside that. The accept parameter must match a Compendium page from the book being used by the character sheet or data will not be displayed. The Compendium iFrame can be dynamically updated by Sheet Worker by using the changeCompendiumPage
-function.
Next Button
<button type="submit" value="example">Next</button>
The Next Button transitions the Charactermancer to the next slide. The button type must be "submit" and the value parameter must match the name of another slide. Additionally, a check will be done to verify that all Charactermancer Inputs that are both visible and Required have a value. The operation will return highlighting the required inputs without values, and will not progress to the next slide.
Back Button
<button type="back" value="example">Back</button>
The Back Buttons work identically to the Next Buttons, with the exceptions of requiring "back" as the type and not validating Required fields.
Cancel Button
<button type="cancel" value="example">Cancel Charactermancer</button>
The Cancel Buttons close the Charactermancer, returning the user to the Character Sheet. The button type must be "cancel". It can pass its value to through an event that clicking the button fires, which can be heard by the on "mancer:cancel"
-listener.
Finish Button
<button type="finish" value="example">Apply Changes</button>
The Finish button closes the Charactermancer, returning to the Character Sheet. The button type must be "finish". Clicking the Finish Button creates an event that can be heard with the on "mancerfinish:name"
listener, which contains the Charactermancer Data Object as an argument.
Final Slide
The name "final" is reserved for a special slide. The Final Slide acts as a transition between the Charactermancer and the Character Sheet. The Charactermancer is disabled once the Final Slide is called but will still be visible displaying the content of the Final Slide until finishCharactermancer()
is called. This slide allows the Character Sheet and Workers to take all of the results of the Charactermancer Data Object and apply them before returning control to the player.
Charactermancer Inputs
<input type="text" name="comp_example">
Attributes in the Charactermancer use a different naming convention than the Character Sheet. Charactermancer Inputs begin with "comp_"
. Inputs and Selects named with the convention will automatically pass their values to the Charactermancer Data Object. The Data Object can be referenced on current and future slides by calling the getCharmancerData()
function and will also be output for character sheet use in the mancerfinish
event.
Charactermancer Data Object
{ "first-slide": { "data": {}, "values": {} }, "second-slide": { "data": { "choice1": { "Number of Widgets": { "Widgets": 2 }, "Type of Widgets": { "Type": "Spongy" } } }, "values": { "choice1": "Widget:Example" }, "repeating": ["<repeating_id1>", "<repeating_id2>"] } }
The Charactermancer Data Object automatically stores data from inputs and selects on each slide through the process. The values of those inputs and selects are saved in JSON format under the slide's name and the "values" key. Additionally "data" can be passed to the slide namespace of the Data Object from the getCompendiumData
function. If any repeating sections are used in the slide, the repeating ids are saved under that slide's "repeating" key. The entire object can be fetched anytime by using the getCharmancerData
function.
Required
<input type="text" name="comp_example" required>
Inputs and Selects marked as "required" in their tag will not allow users to complete the current slide and move to the next one until that field is given a value. Required fields that were left blank when the next button was used will be given the "sheet-hilite"
class and the user will not progress to the next slide. Required inputs do not enforce requirement if they've been hidden inside a Conditional Section. Groups of elements can be made required. If the Required is added to the tag of a parent <div>
, all child <input>
and <select>
inside become required as well.
Compendium Populated Selects
<select name="comp_race" accept="Category:Items">
Selects inside slides can be auto populated with options from the Roll20 Compendium. The select needs the Accept
-parameter, which includes a Compendium compatible search string. Complex multi-attribute searches can be used to provide specific filtered results. Options populated in this way will have the source expansion included after the result in parentheses.
Conditional Sections
<div class="sheet-choice sheet-example">
Conditional Sections are HTML tags with the sheet-choice
-class. These sections are hidden by default whenever the slide is rendered. They can be shown or hidden with the showChoices(<class name>)
and hideChoices(<class name>)
functions respectively.
Action Button
Main Page: Buttons
<button type="action" name="act_starting_gold"></button>
Introduced in the Charactermancer update are Action Buttons. These buttons can be listened for with the on clicked
event listener. They are not limited to the Charactermancer and can be used on the Character Sheet as well. The <button>
must be type="action"
and the button's name must be prefixed with act_
in order to be heard.
Listeners
Charactermancer Listeners
These listeners work for Charactermancer changes, and have access to Charactermancer functions.
mancerchange
on("mancerchange:choice_1 mancerchange:choice_3", function(eventinfo) {...});
The mancerchange listener triggers when Charactermancer Inputs have their values changed. It passes along an argument containing the following properties:
- currentStep:"charmancer-intro" The current slide the user is on
- newValue:"Races:Elf" The value the Charactermancer Input was changed to
- sourceType:"player" Whether the change was triggered by a "worker" or the "player"
- triggerName:"race" The name of the Charactermancer Input that was changed
page:<slide name>
on("page:charmancer-intro", function(eventinfo) {...});
The page listener triggers on slide open when called by either a next button or startCharmancer function. It passes along an argument containing the following properties:
- sourceType:"player" Whether the change was triggered by a "worker" or the "player"
- triggerName:"charactermancer-intro" The name of the Charactermancer Slide that was just opened
clicked:<action button name>
on("clicked:action_button", function(eventinfo) {...});
The clicked listener triggers when an Action Button is clicked. It passes along an argument containing the following properties:
- triggerName:"clicked:action_button" The name of the Action Button that was clicked
mancerroll:<roll button name>
on("mancerroll:roll_stats", function(eventinfo) {...});
This listener triggers when a roll button is clicked in the charactermancer. It passes along an argument containing the following properties:
-
currentStep:"charmancer-intro"
The current slide the user is on - roll: A JSON object containing the results of the roll.
- triggerName:"roll_stats" The name of the Charactermancer roll that triggered the listener
Sheet listeners
These listeners will fire after the charactermancer exits, and have access to the normal set of sheetworkers.
mancer:cancel
on("mancer:cancel", function(eventinfo) {...});
The mancer:cancel listener triggers when a player exits the Charactermancer via the Cancel Button, this can be used when a User restarts the Charactermancer. It passes an object as an argument with a value of the name of the slide the user exited the Charactermancer on:
- value: "slide1"
mancerfinish:<name>
on("mancerfinish:l1-mancer", function(eventinfo) {...});
The mancerfinish listener triggers when a player exits the Charactermancer via the Finish Button. It passes an object as an argument with the same value as the Charactermancer Data Object. This is where the choices from the Charactermancer will be applied to the sheet itself. The setCharmancerText()
function is available here to allow you to update the"final" slide while the changes are being applied.
Functions
Normal sheetworkers are disabled while the Charactermancer is active, with the following exceptions:
setAttrs()
is still usable while the Charactermancer is active, though its function is different, it will only be able to set values for the current Charactermancer slide Functions that access sheet attributes without modifying them (like getAttrs()
and getSectionIDs()
) will work as normal. getCompendiumPage()
and getCompendiumQuery()
work for both sheet and Charactermancer.
A note about selectors: Many of these functions use a css selector to find the relevant html element on the slide. The first part of this selector must be a class, but the rest of this selector can use any valid JQuery selector syntax.
These functions are available only when the Charactermancer is active:
startCharactermancer(<slide name>)
startCharactermancer("first-slide");
The startCharactermancer function launches the Charactermancer. This hides the character sheet and loads the Charactermancer. It will load in the HTML of the named slide passed in as the first argument and trigger the page open listener for that slide.
setCharmancerText(<update object>);
setCharmancerText({"class_example":"Hello world!"});
The setCharmancerText
function allows you to update sections of your current slide with new text. Passed as an argument to the function is an object where the keys are selectors for the elements to be updated and the values to be the text to replace the existing content of those sections. This function allows the use of html tags, all of the text is passed through the same sanitize function that is used on the rest of the character sheet (which, for example, prepends sheet-
to class names).
changeCompendiumPage(<IFrame selector>, <Compendium Page Name>)
changeCompendiumPage("sheet-iframe", "Welcome Page");
The changeCompendiumPage function allows you to dynamically change the compendium page loaded into a Compendium iFrame on your current slide. The first argument must match the name of the iFrame's class and the second argument passes the Compendium page to be rendered. It is recommended to use the page's category to ensure a unique match. For example, "Druid" may match a page in both the "Classes" category and the "NPC" category, but "Classes:Druid" will be a unique page.
changeCharmancerPage(<slide name>, <callback>)
changeCharmancerPage("options_slide", function() {...});
This function changes the current slide of the Charactermancer. The first argument is the name of the slide. It accepts a callback, which is called after the slide loads.
setAttrs(<update object>)
setAttrs({"test_attribute": 2})
The setAttrs function works the same for Charactermancer Inputs as it does Character Sheet Attributes with the following exceptions. While the Charactermancer is active it can only be used to update Charactermancer Inputs and it will clear out radio and checkbox inputs if the value they're set to does not match their given value(s). These attributes must have an input on the slide, or their values will be scrubbed from the Charactermancer data object upon moving to the next page.
setCharmancerOptions(<Class Name>, <Select Options>, <Settings>, <Callback>);
setCharmancerOptions("select-class", "Category:Items Type:Weapons", {add:["Magic Longsword","Axe of Amazingness"], category:"Items"});
The setCharmancerOptions function allows you to update Charactermancer Selects or Radio Inputs with dynamic options as needed. The function requires the first two arguments but can accept up to four. The first argument is the class name of the Charactermancer Select you wish to populate with options. The second argument can be passed an array of pre-defined options or be passed with a Compendium Search string to generate a dynamic list of options from the Compendium. The third argument accepts several different optional settings detailed below. The final argument is a callback to be executed after setCharmancerOptions returns. This callback gets a parameter which is an array of the values that were found/filled. When used with type='radio' Inputs the function works the same but if it finds all spans with that name on the slide and fills them with <label><input type="checkbox"><span>content</span></label>
instead.
- category: Category name of options
- disable: Array of options to disable, which must match the values
- selected: Value of initial selected option
- add: Array of addition or miscellaneous options to include at the bottom of the select options
- show_source: Displays options with Compendium Expansion initials in parentheses (only available when using a compendium query as a select option)
disableCharmancerOptions(<Class Name>, <Select Options>, <Settings>);
disableCharmancerOptions("select-class", ["one","two"]);
The disableCharmancerOptions
functions similarly to setCharmancerOptions
. It only accepts an array of options to disable either Charactermancer Selects or Radio Inputs. The function will not disable a currently selected option. If two or more selects have the same values, it disables on all of them at once. Passing in an empty array will re-enable everything.
showChoices([<class name>]);
showChoices(["sheet-test","sheet-test2]);
The showChoices function reveals hidden Conditional Sections with the sheet-choice
class. It accepts an argument as an array of class names for Conditional Sections to reveal.
hideChoices([<class name>]);
hideChoices(["sheet-test","sheet-test2]);
The hideChoices function hides currently visible Conditional Sections with the sheet-choice
class. It accepts an argument as an array of class names for Conditional Sections to hide. If no argument is given it will hide all choices on the current slide.
getCompendiumPage(<Compendium Page Name>, [<Values>], <Callback>)
getCompendiumPage("Compendium Name", function(data) {...});
The getCompendiumPage function pulls the data attributes of a Compendium Page and allows you to pass that data to both the Charactermancer Data Object and a callback function. The first argument the function takes is the name of the Compendium Page you want to pull the data from, or an array of pages. Again, it is recommended to use the format <category>:<pagename>
to ensure the correct result. The third option is a callback that passes the data object as an argument.
If this function is called in the mancerchange:
event for an an attribute, all of the data will be saved in the "data" section of the current slide's JSON object, under a key matching that attribute's name. To ensure that data saves properly, it's recommended to make sure that getCompendiumPage
is the first asynchronous sheet worker called in that event. If multiple pages are fetched with this function, only the first one will be saved in the data. If an empty string is used for the first argument, it will clear the currently saved data for that attribute (but not the attribute's value).
This data is not saved to Firebase. Rather, all of the data fields are fetched at once from the compendium when the charactermancer is first opened.
getCompendiumQuery(<Compendium Query String>, <Callback>)
getCompendiumQuery("Category:Spells Level:1|2|3 Class:*Bard", function(data) {...});
This function functions similarly to getCompendiumPage, except it uses a query string (or array of queries) to fetch results, and it will not save any data to the Charactermancer object.
getCharmancerData()
var data = getCharmancerData();
The getCharmancerData returns the current value of the Charactermancer Data Object.
deleteCharmancerData([<pages>], <Callback>)
deleteCharmancerData(["slide1","slide3","slide4"], function(data) {...})
The deleteCharmancerData
function accepts two arguments. The first argument is an array of slide names to have all associated Charactermancer Data Object data and values deleted, clearing out the slide as though it had never been visited. If no slides are passed as an argument the function will remove all data and choices. The second argument is a callback performed after the deleteCharmancerData
function is completed.
finishCharactermancer() =
finishCharactermancer()
The special case finishCharactermancer function transitions the Final Slide back to the Character Sheet.
setSectionOrder(<Repeating Section Name>, <Section Array>, <Callback>);
(Regular sheet worker function, not a Charactermancer function)
setSectionOrder("proficiencies", final-array, callbackFunction);
The setSectionOrder function allow the ordering of repeating sections according to your preference. The function accepts a repeating section ( repeating_
is prepended to this value), the method in which to order based on the repeating item data, and an optional callback function.
WARNING
This function's behavior does not match its documentation. The function does not appear to have any callback functionality. Additionally, reordering sections via this function can cause significant graphical glitches and even some data corruption depending on what a user does in reaction to the graphic glitches.
Charactermancer Repeating Sections
Main Page: Repeating Sections
Repeating sections in the Charactermancer function differently than the normal repeating sections in character sheets. Rather than being defined to a particular element on the page (the <fieldset>
), repeating sections in the Charactermancer are snippets of HTML code that can be appended to any element in the active Charactermancer slide (including inside another repeating section). This HTML is sanitized the same way as the rest of the sheet.
When a section is added to the slide, a unique rowid is generated for that row. This id is applied as a class to the parent element of this section, and is used to rename any inputs, action buttons or roll buttons inside that section. The naming convention is: repeating_123456789_row_input
, where row
is the name of the repeating section, and "input" is the name of the input. The values from the inputs in repeating sections are saved under the "values" key in the Charactermancer data object with the rest of the values from that slide. The repeating section name (the name of the parent section, not each value inside), is added to the "repeating" array for each slide.
The mancerchange
, mancerroll
, and clicked listeners can listen to changes to a repeating section. For a change to repeating_123456789_row_input
, a change will be triggered for the attribute itself, mancerchange:repeating_row_input
, and the section it's contained in, mancerchange:repeating_row
. If that section is inside of another repeating section, an event will be fired for the parent repeating section as well. The event info for a repeating section change will contain a couple of new elements, in addition to the normal ones mentioned above:
-
sourceAttribute:"repeating_123456789_row_input"
The specific attribute that was changed -
sourceSection:"repeating_row"
The name of the repeating section the sourceAttribute is in -
triggerName:"repeating_row"
The name of the listener that was triggered
These sections are not persistent, meaning, when the user leaves the slide and revisits it, the slide will load without the repeating sections and will rely on the automatic change events to fire workers to add these sections back to the page (see the section below about revisiting slides).
Section Definition
<charmancer class="sheet-repeating-row">
<charmancer class="sheet-repeating-row">
To define a repeating section, place the code snippet inside a >charmancer<
tag, using a class starting with the keywords "sheet-repeating-". In the above example, the code within the >charmancer<
tags will be defined as a repeating section called "row".
addRepeatingSection(<class_name>, <section_name>, <new_name>, <callback>)
addRepeatingSection("sheet-class", "row", "spellrow", function(sectionid) {...});
This function appends the specified repeating section to the specified target element. The first argument is the selector for the target element, the second specifies which repeating section to add. The third argument is optional, it defines a new section name for this specific section, if it is omitted the section name will just use the second parameter as its name. The callback for this section receives the section id that was assigned to the section.
clearRepeatingSections(<target>, <callback>)
clearRepeatingSections("sheet-class", function() {...});
This function clears all repeating sections from the element specified by the first argument. It removes the HTML itself, along with any corresponding values in the Charactermancer data object.
clearRepeatingSectionById([<section_ids>], <callback>)
clearRepeatingSectionById([<id1>, <id2>, ...], function() {...});
This function is similar to the clearRepeatingSections function, but it only clears specific repeating sections, defined by the array in the first argument. It will also clear any repeating sections inside those specified.
getRepeatingSections(<target>, <callback>)
getRepeatingSections("sheet-class", function(repeating) {...});
This function returns more detailed information about repeating sections on the current slide. The first argument is a selector for the target element. This is used to get a list of the repeating sections within that element. If this is omitted, the function returns all repeating sections on this slide. The data returned to the callback also contains a JSON object that shows the structure of the repeating sections, detailing which sections are inside other sections.
Revisiting Slides
If the user leaves a slide, then revisits it later, the Charactermancer will automatically trigger change events for the saved values on the page. This is to allow for the sheetworkers to fire and rebuild the slide to its previous state. This is necessary, because the changes made by the functions that manipulate HTML (like setCharmancerText
and addRepeatingSection
) do not persist. It is important to keep in mind that these workers may be firing when revisiting the slide. Plan them accordingly. Note that when revisiting a slide, Previous repeating section ids are reused for sections with matching names, so these id's should remain constant.
Pseudo Attribute charactermancer_step
The charactermancer_step
attribute is a pseudo attribute like character_name
. This allows you to set the step the Charactermancer will open to next time it is a launched. This attribute can be set by a normal setAttrs
function and cannot be set while the Charactermancer is active.