System creation
Prerequisites
In order to create systems for the app, you will need a couple things. These might seem technical, but if you need help a quick Google (or question to your AI of choice) might help a lot:
- A copy of the RPG Companion App (you can download it from here).
- A cool text editor (any text editor will work, but Visual Studio Code is recommended as you will be able to use some special snippets that we've created for you, to make your task easier).
- The RPG Companion App dev tool, which will help you test your system with the app.
a. You need to have the app in "Developer Mode" to be able to develop systems. You can toggle this in the app's settings. - A Wi-Fi network, which you should have both your laptop and phone connected to.
That's it! I know it seems complicated, but this was the hardest part.
You can clone a working folder structure and example system.rpg.json from the
Open Systems Repository:
github.com/blastervla/rpg-companion-app-systems
Visual Studio Code shortcuts (optional, recommended)
If you choose to use Visual Studio Code, optionally, you can go through the following steps to make your development experience a bit better:
- Open up the editor
- Use the command palette (
cmd + option + p) to and choose the optionPreferences: Open Keyboard Shortcuts (JSON). - Insert the following block within the brackets:
{
"key": "shift+cmd+i",
"command": "editor.action.insertSnippet",
"when": "!editorReadonly"
} - Once again, open up the command palette and choose
Snippets: Configure Snippets. ChooseNew Global Snippets file. - Name the file
rpg.json. - Copy and paste the contents of the following json to the new created file.
rpg.jsonsnippets file content{
// Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"statStat": {
"prefix": "stat",
"scope": "json",
"body": [
"{",
"\t\"type\": \"stat\",",
"\t\"stat\": \"\"",
"}"
]
},
"definedStat": {
"prefix": "statDefined",
"scope": "json",
"body": [
"{",
"\t\"type\": \"defined\",",
"\t\"stat\": \"\"",
"}"
]
},
"metaStatStat": {
"prefix": "metaStat",
"scope": "json",
"body": [
"{",
"\t\"type\": \"metaStat\",",
"\t\"meta_stat\": {}",
"}"
]
},
"defaultIfNullStat": {
"prefix": "statdefaultIfNull",
"body": [
"{",
"\t\"type\": \"defaultIfNull\",",
"\t\"components\": {},",
"\t\"default_components\": {}",
"}"
]
},
"mapperStat": {
"prefix": "statmap",
"body": [
"{",
"\t\"type\": \"map\",",
"\t\"components\": {},",
"\t\"mapper\": {}",
"}"
]
},
"flatMapStat": {
"prefix": "statFlatMap",
"body": [
"{",
"\t\"type\": \"flatMap\",",
"\t\"components\": {},",
"\t\"mapper\": {}",
"}"
]
},
"filterStat": {
"prefix": "statfilter",
"body": [
"{",
"\t\"type\": \"filter\",",
"\t\"components\": {},",
"\t\"filter\": {}",
"}"
]
},
"findFirstStat": {
"prefix": "statfindFirst",
"body": [
"{",
"\t\"type\": \"findFirst\",",
"\t\"components\": {},",
"\t\"filter\": {}",
"}"
]
},
"sortByStat": {
"prefix": "statsortby",
"body": [
"{",
"\t\"type\": \"sortBy\",",
"\t\"order\": ?\"asc|desc\",",
"\t\"sort_by_value_key\": ?\"\\$sortByValue\",",
"\t\"components\": {},",
"\t\"sort_by_selector\": {}",
"}"
]
},
"enumeratedNameStat": {
"prefix": "statEnumeratedName",
"scope": "json",
"body": [
"{",
"\t\"type\": \"enumeratedName\",",
"\t\"enumerated_type\": \"\",",
"\t\"id\": {}",
"}"
]
},
"enumeratedAbbreviationStat": {
"prefix": "statEnumeratedAbbreviation",
"scope": "json",
"body": [
"{",
"\t\"type\": \"enumeratedAbbreviation\",",
"\t\"enumerated_type\": \"\",",
"\t\"id\": {}",
"}"
]
},
"joinToStringStat": {
"prefix": "statJoinToString",
"scope": "json",
"body": [
"{",
"\t\"type\": \"joinToString\",",
"\t\"separator\": \"\",",
"\t\"components\": {}",
"}"
]
},
"splitStat": {
"prefix": "statSplit",
"scope": "json",
"body": [
"{",
"\t\"type\": \"split\",",
"\t\"separator\": \"\",",
"\t\"components\": {}",
"}"
]
},
"concatStat": {
"prefix": "statConcat",
"scope": "json",
"body": [
"{",
"\t\"type\": \"concat\",",
"\t\"components\": {}",
"}"
]
},
"isEmptyStat": {
"prefix": "statIsEmpty",
"scope": "json",
"body": [
"{",
"\t\"type\": \"isEmpty\",",
"\t\"components\": {}",
"}"
]
},
"lengthStat": {
"prefix": "statLength",
"scope": "json",
"body": [
"{",
"\t\"type\": \"length\",",
"\t\"components\": {}",
"}"
]
},
"trimStat": {
"prefix": "statTrim",
"scope": "json",
"body": [
"{",
"\t\"type\": \"trim\",",
"\t\"components\": {}",
"}"
]
},
"containsStat": {
"prefix": "statContains",
"scope": "json",
"body": [
"{",
"\t\"type\": \"contains\",",
"\t\"haystack\": {},",
"\t\"needle\": {}",
"}"
]
},
"maxStat": {
"prefix": "statMax",
"scope": "json",
"body": [
"{",
"\t\"type\": \"max\",",
"\t\"components\": {}",
"}"
]
},
"minStat": {
"prefix": "statMin",
"scope": "json",
"body": [
"{",
"\t\"type\": \"min\",",
"\t\"components\": {}",
"}"
]
},
"copyResourceStat": {
"prefix": "statCopyResource",
"scope": "json",
"body": [
"{",
"\t\"type\": \"copyResource\",",
"\t\"components\": {}",
"}"
]
},
"addStat": {
"prefix": "statAdd",
"scope": "json",
"body": [
"{",
"\t\"type\": \"add\",",
"\t\"components\": {}",
"}"
]
},
"subtractStat": {
"prefix": "statSubtract",
"scope": "json",
"body": [
"{",
"\t\"type\": \"subtract\",",
"\t\"components\": {}",
"}"
]
},
"multiplyStat": {
"prefix": "statMultiply",
"scope": "json",
"body": [
"{",
"\t\"type\": \"multiply\",",
"\t\"components\": {}",
"}"
]
},
"divideStat": {
"prefix": "statDivide",
"scope": "json",
"body": [
"{",
"\t\"type\": \"divide\",",
"\t\"rounding_method\": \"down|up|closest|none\",",
"\t\"components\": {}",
"}"
]
},
"equalsStat": {
"prefix": "statEquals",
"scope": "json",
"body": [
"{",
"\t\"type\": \"equals\",",
"\t\"components\": {}",
"}"
]
},
"andStat": {
"prefix": "statAnd",
"scope": "json",
"body": [
"{",
"\t\"type\": \"and\",",
"\t\"components\": {}",
"}"
]
},
"orStat": {
"prefix": "statOr",
"scope": "json",
"body": [
"{",
"\t\"type\": \"or\",",
"\t\"components\": {}",
"}"
]
},
"notEqualsStat": {
"prefix": "statNotEquals",
"scope": "json",
"body": [
"{",
"\t\"type\": \"notEquals\",",
"\t\"components\": {}",
"}"
]
},
"notNullStat": {
"prefix": "statNotNull",
"scope": "json",
"body": [
"{",
"\t\"type\": \"notNull\",",
"\t\"components\": {}",
"}"
]
},
"isNullStat": {
"prefix": "statIsNull",
"scope": "json",
"body": [
"{",
"\t\"type\": \"isNull\",",
"\t\"components\": {}",
"}"
]
},
"notStat": {
"prefix": "statNot",
"scope": "json",
"body": [
"{",
"\t\"type\": \"not\",",
"\t\"components\": {}",
"}"
]
},
"greaterThanEqualsStat": {
"prefix": "statGTE",
"scope": "json",
"body": [
"{",
"\t\"type\": \"greaterThanEquals\",",
"\t\"components\": {}",
"}"
]
},
"greaterThanStat": {
"prefix": "statGT",
"scope": "json",
"body": [
"{",
"\t\"type\": \"greaterThan\",",
"\t\"components\": {}",
"}"
]
},
"lessThanEqualsStat": {
"prefix": "statLTE",
"scope": "json",
"body": [
"{",
"\t\"type\": \"lessThanEquals\",",
"\t\"components\": {}",
"}"
]
},
"lessThanStat": {
"prefix": "statLT",
"scope": "json",
"body": [
"{",
"\t\"type\": \"lessThan\",",
"\t\"components\": {}",
"}"
]
},
"statToNum": {
"prefix": "statToNum",
"scope": "json",
"body": [
"{",
"\t\"type\": \"toNum\",",
"\t\"components\": {}",
"}"
]
},
"statToSignedString": {
"prefix": "statToSignedString",
"scope": "json",
"body": [
"{",
"\t\"type\": \"toSignedString\",",
"\t\"components\": {}",
"}"
]
},
"statToLowerCase": {
"prefix": "statToLowerCase",
"scope": "json",
"body": [
"{",
"\t\"type\": \"toLowerCase\",",
"\t\"components\": {}",
"}"
]
},
"statToUpperCase": {
"prefix": "statToUpperCase",
"scope": "json",
"body": [
"{",
"\t\"type\": \"toUpperCase\",",
"\t\"components\": {}",
"}"
]
},
"statReplace": {
"prefix": "statreplace",
"body": [
"{",
"\t\"type\": \"replace\",",
"\t\"string\": {},",
"\t\"occurrence\": {},",
"\t\"replace\": {}",
"}"
]
},
"statRoll": {
"prefix": "statRoll",
"body": [
"{",
"\t\"type\": \"roll\",",
"\t\"formula\": {}",
"}"
]
},
"statRollable": {
"prefix": "statRollable",
"body": [
"{",
"\t\"type\": \"rollable\",",
"\t\"formulas\": {},",
"\t\"text\": {}",
"}"
]
},
"statList": {
"prefix": "statlist",
"scope": "json",
"body": [
"{",
"\t\"type\": \"list\",",
"\t\"components\": []",
"}"
]
},
"statFlatAppend": {
"prefix": "statFlatAppend",
"scope": "json",
"body": [
"{",
"\t\"type\": \"flatAppend\",",
"\t\"components\": []",
"}"
]
},
"statConstant": {
"prefix": "statconst",
"scope": "json",
"body": [
"{",
"\t\"type\": \"constant\",",
"\t\"value\": ,",
"\t\"value_type\": ",
"}"
]
},
"statWhen": {
"prefix": "statwhen",
"scope": "json",
"body": [
"{",
"\t\"type\": \"when\",",
"\t\"clauses\": [",
"\t\t{",
"\t\t\t\"condition\": {},",
"\t\t\t\"components\": {}",
"\t\t},",
"\t\t{",
"\t\t\t\"condition\": {",
"\t\t\t\t\"type\": \"constant\",",
"\t\t\t\t\"value\": true,",
"\t\t\t\t\"value_type\": \"bool\"",
"\t\t\t},",
"\t\t\t\"components\": {}",
"\t\t}",
"\t]",
"},"
]
},
"whenClause": {
"prefix": "wclause",
"scope": "json",
"body": [
"{",
"\t\"condition\": {},",
"\t\"components\": {}",
"},"
]
},
"baseStat": {
"prefix": "base",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"name\": \"\",",
"\t\"type\": \"base\",",
"\t\"value_type\": \"\",",
"\t\"default_value\": ",
"},"
]
},
"calcStat": {
"prefix": "calc",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"name\": \"\",",
"\t\"type\": \"calculated\",",
"\t\"value_type\": \"\",",
"\t\"components\": {}",
"},"
]
},
"textView": {
"prefix": "vtext",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"text\",",
"\t\"text\": ?{},",
"\t\"edit_stat\": ?,",
"\t\"style\": \"text|title|section|subhead|headline|caption\",",
"\t\"max_length\": ?,",
"\t\"signed\": ?false,",
"\t\"hint\": ?{},",
"\t\"label\": ?{},",
"\t\"bold\": ?,",
"\t\"color\": ?{},",
"\t\"resource_id\": ?,",
"\t\"resource_instance_ids_stat\": ?,",
"\t\"resource_display_stat\": ?",
"},"
]
},
"diffTextView": {
"prefix": "vdiffText",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"diffText\",",
"\t\"original_text\": {},",
"\t\"new_text\": {},",
"\t\"added_color\": ?\"\",",
"\t\"deleted_color\": ?\"\"",
"},"
]
},
"listView": {
"prefix": "vlist",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"list\",",
"\t\"padding\": ?,",
"\t\"alignment\": ?\"start|center|end\",",
"\t\"subviews\": []",
"},"
]
},
"statView": {
"prefix": "vStat",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"stat\",",
"\t\"stat\": \"\",",
"\t\"edit_stat\": ?\"\",",
"\t\"change_amount\": ?1,",
"\t\"min_value_allowed\": ?0,",
"\t\"max_value_allowed\": ?999999,",
"\t\"calculated_min_value_allowed\": ?{},",
"\t\"calculated_max_value_allowed\": ?{},",
"\t\"on_tap_event\": ?{},",
"\t\"on_long_tap_event\": ?{},",
"\t\"bottom_sheet_view\": ?{},",
"\t\"bottom_sheet_in_edit_mode\": ?false,",
"\t\"sheet_title\": {}",
"\t\"stat_color\": ?{}",
"\t\"name_color\": ?{}",
"\t\"style\": ?\"block|list\"",
"\t\"always_show_sign\": ?false",
"}"
]
},
"resourceSectionView": {
"prefix": "vResSection",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"resourceSection\",",
"\t\"resource_stat\": \"\",",
"\t\"resource_id\": \"\",",
"\t\"header_view\": ?{},",
"\t\"resource_set_stat\": ?\"\",",
"\t\"contentItemMinWidth\": ?1,",
"\t\"alignment\": ?\"start|center|end\",",
"\t\"read_only\": ?false,",
"\t\"tap_displays_resource\": ?true",
"},"
]
},
"sectionView": {
"prefix": "vSection",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"section\",",
"\t\"header\": [],",
"\t\"content\": [],",
"\t\"show_divider\": false,",
"\t\"content_item_min_width\": ?100,",
"\t\"collapsible\": ?false,",
"\t\"start_collapsed\": ?false,",
"\t\"always_show_collapse_icon\": ?false,",
"\t\"alignment\": ?\"start|center|end\"",
"}"
]
},
"avatarSectionView": {
"prefix": "vAvatarSection",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"avatarSection\",",
"\t\"bottom_left_stack\": ?[],",
"\t\"bottom_right_stack\": ?[],",
"\t\"center_content\": [],",
"\t\"bottom_left_button_title\": ?{},",
"\t\"bottom_left_button_event\": ?{},",
"\t\"bottom_right_button_title\": ?{},",
"\t\"bottom_right_button_event\": ?{}",
"}"
]
},
"avatarView": {
"prefix": "vAvatar",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"avatar\",",
"\t\"photo_stat\": \"\",",
"\t\"name_stat\": \"\",",
"\t\"size\": \"massive|medium|small\",",
"\t\"interactable\": ?false",
"}"
]
},
"selectView": {
"prefix": "vselect",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"select\",",
"\t\"stat\": \"\",",
"\t\"options\": ?\"\",",
"\t\"allowed_options\": ?{},",
"\t\"resource_options\": ?{},",
"\t\"max_selection_amount\": ?{},",
"\t\"options_display_stat\": ?\"\",",
"\t\"multi_select\": ?false",
"\t\"style\": ?\"dropdown|list\"",
"},"
]
},
"checkboxView": {
"prefix": "vcheck",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"checkbox\",",
"\t\"stat\": \"\",",
"\t\"label\": ?,",
"\t\"label_alignment\": ?\"left|right\",",
"\t\"style\": ?\"checkbox|switcher\",",
"\t\"should_auto_save\": ?false",
"},"
]
},
"compositeView": {
"prefix": "vcomp",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"composite\",",
"\t\"row_only\": ?true,",
"\t\"alignment\": ?\"start|center|end\",",
"\t\"subviews\": []",
"},"
]
},
"resourceArrayView": {
"prefix": "vresarr",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"resourceArray\",",
"\t\"resource_id\": \"\",",
"\t\"stat\": \"\",",
"\t\"view_type\": \"list|display|edit\",",
"\t\"auto_add_first\": ?true|false,",
"\t\"left_view\": ?,",
"\t\"pop_up_type\": ?\"bottomSheet|window\",",
"\t\"pop_up_button_text\": ?{},",
"\t\"hide_line\": ?false",
"},"
]
},
"iconView": {
"prefix": "vIcon",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"icon\",",
"\t\"name\": \"\",",
"\t\"size\": \"massive|xLarge|large|medium|small|xSmall\",",
"\t\"color\": ?\"\"",
"},"
]
},
"menuButtonView": {
"prefix": "vMenuButton",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"menuButton\",",
"\t\"icon_name\": \"\",",
"\t\"icon_size\": \"massive|xLarge|large|medium|small|xSmall\",",
"\t\"icon_color\": ?\"\",",
"\t\"options\": \"\",",
"\t\"allowedoptions\": ?{}",
"},"
]
},
"resourceView": {
"prefix": "vreso",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"resource\",",
"\t\"view_type\": \"display|edit|list\",",
"\t\"resource_id\": \"\",",
"\t\"stat\": \"\",",
"\t\"create_if_empty\": ?false",
"},"
]
},
"popUpButtonView": {
"prefix": "vPopUp",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"popUpButton\",",
"\t\"text\": ?{},",
"\t\"icon\": ?{},",
"\t\"pop_up_view\": {},",
"\t\"pop_up_type\": \"bottomSheet|alert\",",
"\t\"dismissible\": ?true|false,",
"\t\"pop_up_title\": {},",
"\t\"display_cancel_button\": ?true,",
"\t\"cancel_text\": ?{},",
"\t\"display_save_button\": ?false,",
"\t\"save_text\": ?{},",
"\t\"should_persist_on_save\": ?false,",
"\t\"extra_buttons\": ?[{\"text\": {}, \"event\": {}}],",
"\t\"in_edit_mode\": ?false",
"},"
]
},
"selectResourcesView": {
"prefix": "vselres",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"selectResources\",",
"\t\"stat\": \"\",",
"\t\"resource_id\": \"\",",
"\t\"view_type\": ?\"list|icon\",",
"\t\"should_append\": ?false,",
"\t\"should_save_on_selection\": ?false,",
"\t\"create_in_place\": ?false,",
"\t\"creation_pop_up_type\": ?\"bottomSheet|window\",",
"\t\"filters\": ?{}",
"}"
]
},
"tickerView": {
"prefix": "vtick",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"ticker\",",
"\t\"stat\": \"\",",
"\t\"style\": ?\"text|title|section|subhead|headline|caption\",",
"\t\"button_location\": ?\"left|right|sideToSide\",",
"\t\"button_look\": ?\"plusMinus|upDown\",",
"\t\"should_auto_save\": ?false,",
"\t\"change_amount\": ?1,",
"\t\"min_value_allowed\": ?-99999,",
"\t\"max_value_allowed\": ?99999,",
"\t\"label\": ?{},",
"\t\"label_alignment\": ?\"left|right\",",
"\t\"edit_bottom_sheet_title\": ?{}",
"},"
]
},
"chipView": {
"prefix": "vChip",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"chip\",",
"\t\"text\": {},",
"\t\"on_tap_event\": ?{},",
"\t\"background_color\": ?\"\",",
"\t\"border_color\": ?\"\",",
"\t\"style\": ?\"compact|fullSize\",",
"\t\"resource_id\": ?\"\",",
"\t\"resource_instance_ids_stat\": ?\"\",",
"\t\"resource_display_stat\": ?\"\"",
"},"
]
},
"buttonView": {
"prefix": "vbutt",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"button\",",
"\t\"text\": {},",
"\t\"event\": ?{},",
"\t\"event_names\": ?{}",
"},"
]
},
"collapsibleView": {
"prefix": "vCollapsible",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"collapsible\",",
"\t\"title\": {},",
"\t\"subtitle\": ?{},",
"\t\"start_expanded\": ?false,",
"\t\"subviews\": []",
"}"
]
},
"viewPagerView": {
"prefix": "vviewpager",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"viewPager\",",
"\t\"names\": {returns list[string]},",
"\t\"color\": ?{returns string},",
"\t\"subviews\": []",
"}"
]
},
"listItemView": {
"prefix": "vlistItem",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"listItem\",",
"\t\"title\": {},",
"\t\"title_color\": {},",
"\t\"subtitle\": {},",
"\t\"subtitle_color\": {},",
"\t\"first_line_extra\": {},",
"\t\"first_line_extra_color\": {},",
"\t\"second_line_extra\": {},",
"\t\"second_line_extra_color\": {},",
"\t\"chips\": {}",
"}"
]
},
"spacerView": {
"prefix": "vSpacer",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"spacer\"",
"},"
]
},
"dividerView": {
"prefix": "vDivider",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"type\": \"divider\",",
"\t\"vertical\": ?false",
"},"
]
},
"addToStatEffect": {
"prefix": "eAddToStat",
"body": [
"{",
"\t\"type\": \"addToStat\",",
"\t\"stat\": ?\"\",",
"\t\"meta_stat\": ?{},",
"\t\"value\": {},",
"\t\"aggregation_type\": ?\"min|max|sum|set|none\",",
"\t\"calculated_aggregation_type\": ?{}",
"}"
]
},
"setStatEffect": {
"prefix": "eSetStat",
"body": [
"{",
"\t\"type\": \"setStat\",",
"\t\"stat\": ?\"\",",
"\t\"meta_stat\": ?{},",
"\t\"new_value\": {},",
"\t\"aggregation_type\": ?\"min|max|sum|set|none\",",
"\t\"calculated_aggregation_type\": ?{}",
"}"
]
},
"whenEffect": {
"prefix": "eWhen",
"scope": "json",
"body": [
"{",
"\t\"type\": \"when\",",
"\t\"clauses\": [",
"\t\t{",
"\t\t\t\"condition\": {},",
"\t\t\t\"effect\": {}",
"\t\t},",
"\t\t{",
"\t\t\t\"condition\": {},",
"\t\t\t\"effect\": {}",
"\t\t}",
"\t]",
"}"
]
},
"eWhenClause": {
"prefix": "ewclause",
"scope": "json",
"body": [
"{",
"\t\"condition\": {},",
"\t\"effect\": {}",
"},"
]
},
"delayedEffect": {
"prefix": "eDelayed",
"scope": "json",
"body": [
"{",
"\t\"type\": \"delayed\",",
"\t\"millis\": 100,",
"\t\"effect\": {}",
"}"
]
},
"dismissResourceEffect": {
"prefix": "eDismissResource",
"scope": "json",
"body": [
"{",
"\t\"type\": \"dismissResource\",",
"\t\"id\": ?{}",
"}"
]
},
"fireEventEffect": {
"prefix": "efire",
"body": [
"{",
"\t\"type\": \"fireEvent\",",
"\t\"event\": ?{},",
"\t\"event_names\": ?{return list}",
"}"
]
},
"forwardEventEffect": {
"prefix": "eforward",
"body": [
"{",
"\t\"type\": \"forwardEvent\",",
"\t\"new_event_names\": ?{return list}",
"}"
]
},
"sequenceEffect": {
"prefix": "eseq",
"body": [
"{",
"\t\"type\": \"sequence\",",
"\t\"effects\": []",
"}"
]
},
"forEachEffect": {
"prefix": "eforEach",
"body": [
"{",
"\t\"type\": \"forEach\",",
"\t\"components\": {},",
"\t\"apply\": {},",
"\t\"for_each_value_key\": ?\"\"",
"}"
]
},
"showResourceEffect": {
"prefix": "eShowResource",
"body": [
"{",
"\t\"type\": \"showResource\",",
"\t\"resource\": {}",
"}"
]
},
"showMessageEffect": {
"prefix": "eShowMessage",
"body": [
"{",
"\t\"type\": \"showMessage\",",
"\t\"message\": {},",
"\t\"message_type\": ?\"info|error|success\"",
"}"
]
},
"showPopUpEffect": {
"prefix": "eshowPopUp",
"body": [
"{",
"\t\"type\": \"showPopUp\",",
"\t\"pop_up_view\": {},",
"\t\"pop_up_type\": \"bottomSheet|alert\",",
"\t\"dismissible\": ?true|false,",
"\t\"pop_up_title\": {},",
"\t\"display_cancel_button\": ?true,",
"\t\"cancel_text\": ?{},",
"\t\"display_save_button\": ?false,",
"\t\"save_text\": ?{},",
"\t\"should_persist_on_save\": ?false,",
"\t\"extra_buttons\": ?[{\"text\": {}, \"event\": {}}],",
"\t\"in_edit_mode\": ?false",
"}"
]
},
"rollEffect": {
"prefix": "eRoll",
"body": [
"{",
"\t\"type\": \"roll\",",
"\t\"rolls\": {},",
"\t\"on_result_effect\": ?{}",
"}"
]
},
"mechanic": {
"prefix": "mech",
"body": [
"{",
"\t\"id\": \"\",",
"\t\"name\": ?\"\",",
"\t\"description\": ?\"\",",
"\t\"event_names\": ?\"\",",
"\t\"calculated_event_names\": ?{returns list},",
"\t\"revert_event_names\": ?\"\",",
"\t\"calculated_revert_event_names\": ?{returns list},",
"\t\"revert_on_remove\": ?{returns bool},",
"\t\"effects\": {}",
"}"
]
},
"searchFilters": {
"prefix": "searchFilter",
"body": [
"{",
"\t\"name\": \"\",",
"\t\"stat\": \"\",",
"\t\"true_name\": ?\"\",",
"\t\"false_name\": ?\"\",",
"\t\"options\": ?\"\",",
"\t\"options_display_stat\": ?\"\"",
"}"
]
},
} - You can utilize the
Snippets: Configure Snippetsoption and choose therpg.json.json.code-snippetsoption whenever you want to update / enhance these if needed.
The system
You will be mainly working with the system definition called system.rpg.json.
There are now two supported workflows:
- Monolithic (original): keep everything in a single
system.rpg.jsonfile at the system root. - Split (new, optional): place a
system/folder inside your system and split large sections into smaller files and folders. The Dev Tool will compose these parts into a single system at build time.
Both approaches are supported. If both exist, the Dev Tool prefers the split folder.
The system file structure
You can host multiple systems side-by-side under a top-level systems folder.
Monolithic (legacy)
systems
|—system1
| |—system.rpg.json
| |—resources/ # legacy instances folder (see below)
|—system2
|—...
Split system (recommended)
systems
|—system1
| |—system/
| | |—system.rpg.json # optional base (can be empty/minimal)
| | |—character_stats.rpg.json # list
| | |—mechanics/ # multiple files (each a mechanic object)
| | |—enumerated_types/ # multiple files (each a type object)
| | |—character_creation_flow/ # ordered files
| | |—character_sheet_sections/ # ordered files
| | |—resources/ # resource DEFINITIONS (schemas, views, stats, mechanics)
| | | |—armor/
| | | | |—index.rpg.json
| | | | |—stats.rpg.json # array of stats
| | | | |—mechanics/ # files → mechanic objects
| | | | |—display_view.rpg.json
| | | | |—edit_view.rpg.json
| | | | |—list_view.rpg.json
| | | | |—search_item_view.rpg.json
| | | |—... (other resources)
| |
| |—resource_instances/ # PRE-MADE RESOURCE INSTANCES (exported from the app)
| | |—armor_armor_of_invulnerability.rpg.json
| | |—...
|
|—system2
|—...
Notes
system/holds definitions (what a resource is: stats, views, mechanics, etc.).resource_instances/holds instances (your prebuilt content).- For backwards compatibility, if
resource_instances/is missing the Dev Tool will fall back toresources/at the system root (legacy name for instances).
Split mode: how files and folders map to JSON keys
When you use the system/ folder, the Dev Tool composes the final JSON by merging keys from files and folders.
1) Top-level files in system/
A file named <key>.rpg.json (or .json) sets/merges the value of key in the final system.
Examples:
character_stats.rpg.json→ contributes tosystem.character_statsmechanics.rpg.json→ contributes tosystem.mechanics(if you prefer a single file instead of a folder)
Tip:
system.rpg.jsoninsidesystem/is optional and can hold any keys not provided by other files/folders (e.g.,id,name,abbreviation,version,min_app_version). If a key appears in both, they are merged according to the rules below.
2) Top-level folders in system/
A folder named <key>/ becomes the value of key built from its contents.
- If
index.rpg.json(orindex.json) is an ARRAY: the folder is treated as an array that begins with the items inindex.*. Child files/folders are appended (child arrays are concatenated). - If there is NO
index.*array and ALL immediate children are JSON files (no subfolders): the folder is treated as an array made by concatenating each file’s content (in lexicographic filename order). - Otherwise: the folder is treated as an object.
- Each file is wrapped under its base filename (without extension).
Example:display_view.rpg.json→"display_view": { ... } - Each subfolder is wrapped under its folder name.
Example:mechanics/→"mechanics": [ ... ]or{ ... }depending on its own internal rules (see below).
- Each file is wrapped under its base filename (without extension).
3) Nested folders (recursion)
The same rules apply recursively inside subfolders (resources/armor/…, etc.).
Merge rules (how multiple parts combine)
- Objects (maps): deep-merged by key. When two files provide the same key, values are merged recursively.
- Arrays (lists):
- By default, arrays are concatenated (stable: earlier files first).
- For specific keys, items are merged by
id(keeping first-seen order):resources(i.e., resource definitions undersystem/resources)stats(e.g., resourcestatslists)
If an array item is an object with anid, items with the sameidare deep-merged instead of duplicated.
Ordering-sensitive arrays:
character_creation_flowandcharacter_sheet_sectionskeep the lexicographic file order when treated as arrays. A good way to enforce the order you want is by prefixing file names with xx_, where xx is the number in the order (e.g: 00_status_section, 02_abilities_section, etc)
Resource definitions vs resource instances
-
Resource definitions live under
system/resources/<resource_id>/…and describe what a resource is (itsstats,mechanics, and views likedisplay_view,edit_view,list_view,search_item_view).- Example layout for
armor:system/resources/armor/
index.rpg.json # resource metadata (id, name, plural, color, filters, etc.)
stats.rpg.json # array of stat definitions
mechanics/ # each file is one mechanic object
display_view.rpg.json
edit_view.rpg.json
list_view.rpg.json
search_item_view.rpg.json
- Example layout for
-
Resource instances live under
resource_instances/at the system root (or legacyresources/if you haven’t migrated the folder name). These are the pre-made entries you export from the app (e.g., specific armors, spells, monsters). The Dev Tool bundles them intoresources.rpg.gzip+ an index automatically.
Filename note (instances): the Dev Tool normalizes instance filenames and ensures there’s exactly one leading
<type>_prefix. If an instance name already begins with<type>_, one occurrence is removed before the tool prefixes again, keeping filenames stable across rebuilds.
Where do I get the resourceX.rpg.json files?
Once you create your system and run it in your copy of the RPG Companion App, you can use the app to create pre-made resources. Use the share button on the top-left of the resources screen (orange section of the app) to export a gzip with your resources. Unzip it and copy the resulting files into the system’s resource_instances/ folder.
If you’re keeping a legacy system, you can still use the old
resources/folder name for instances; the Dev Tool will accept it as a fallback. New systems should preferresource_instances/.
How do I test the system?
If you have enabled "Developer Mode" in the app, you can use the RPG Companion App dev tool
to
get your system to your app and try it out. Just select the input folder to be your systems
folder, click on "Start server" and wait for the system to build, then tap on the "Update systems"
button in either the app's home page or the app's settings.
With split systems, the Dev Tool automatically composes
system/into a singlesystem.rpgat build time and bundlesresource_instances/(or legacyresources/) into the resources index.
I see pop-ups with problems in my system.
A lot of the information in the pop up will help you diagnose what the issue is. However, if you still need help and are feeling lost, please feel free to contact Vlad (the app developer) if you need any help or run into issues. He's always happy to help. You can reach him at feedback@blastervla.com.
What next?
Once you're done working with your system, you can host it by following the instructions in Hosting a system
Contributing your system
If you’d like to share your system with the community:
- Ensure it builds locally and loads in the app (using the Dev Tool).
- Follow the folder structure in this guide.
- Open a Pull Request to the Open Systems Repository: github.com/blastervla/rpg-companion-app-systems
Your contributions will be shared under CC BY-NC-SA 4.0 so everyone can improve and remix non-commercially.