Ars Magica 5th
Usage
This sheet uses python to generate the sheet's HTML and CSS from templates. Repetitive parts of the sheet are generated by python which ease the sheet's making.
Therefore, the files Ars_Magica_5th.html and Ars_Magica_5th.css are programmatically generated. Do not modify it direclty, as all changes will be overwritten. Instead, edit template.html and template.css, and possibly the python code found in the folder arm5_py_integration/ if you which to use the python integration (more details below).
The general workflow is the following: 0. Install python and required packages (see below Python Integration > Installation)
- Modify
template.htmland/ortemplate.css(You can use python integration to make things easier, see below) - Run
make(On windows, runmake.batwhich mimics whatmakedo on unix).- This will execute
fileval.pywith proper argumens
- This will execute
- Commit your changes
Warning
Always execute
makewhile the current working directory is this directory
Currently, the files generated are:
Ars_Magica_5th.htmlfromtemplate.html,documentation.htmland additional code inarm5_py_integration/Arsm_Magica_5th.cssfromtemplate.cssand additional code inarm5_py_integration/
Python Integration
The python integration allows to use python code to generate some parts of the sheet. It useful for repetitive sections (characteristics, arts, arts option, simple/stress rolls etc.) and integrating the markdown documentation directly into the sheet.
Installation
You need Python version 3.8 or higher to run the script. You can install it from the Python Website, or using your system's package manager such as apt, brew or others such as conda/miniconda to name a few.
The integration requires some additional package to convert the markdown documentation into HTML. To install them, use the pip commands provided by python (with some package manager, pip might require an additional package). Navigates to this directory and execute:
pip3 -r requirements.txt
Note:
On windows, you might need to run
python -m pip requirements.txtto access to pip if it was not added to your PATH
The pip command will read the requirements.txt file alongside this Readme and install all the listed packages.
Quick overview
The python integration is provided by the script fileval.py. Simply put, it reads an input file, looks for python expressions between specific delimiters and then write an output file where the python expressions are replaced by their value.
For this sheet, the delimiter used is $$, in both template.html and template.css. It must be present at the start and end of an integrated expression (e.g. <span value=$$expression$$></span>). The expression are evaluated using python's eval function. The value of the expression must be a string (python type str).
The expression can be used anywhere in the html/css. The script does not parse the html/css but simply replaces delimited expressions by their value. For the sheet to be valid, the result must obviously be valid css/html, but fileval.py will not ensure that.
Basic usage
The simplest way to the integration is to use very simple expressions made of a single variable name, that will be replaced by the variable's value (e.g. $$my_name$$). You then write some python code to generate the value in the arm5_py_integration/ folder, and makes your value available to the fileval.py script. Most integrations in both template.html and template.css work this way.
The Makefile is currently configured so that fileval.py loads the arm5_py_integration directory as a python package (this is, executes the arm5_py_integration/__init__.py file) then loads all values from the GLOBALS variable of that package (defined in arm5_py_integration/__init__.py). Any value defined in GLOBALS will be available, be it a python package, a function, a value etc.
Note
You can put most of your code direclty in
arm5_py_integration/__init__.pyas most code is likely to be short. If you need longer code, you can also create another file inarm5_py_integration/and import it in__init__.py. Look atarm5_py_integration/translation.pyfor an example of this.
Note
Since most code is basically writing a template a bunch of times with one or two value varying, you will find useful helpers in the file
arm5_py_integration/helpers.py. Refer to the function's docstring and the examples inarm5_py_integration/__init__.pyfor how to use them.
Advanced usage
The python integration allows for all kind of python expression to be integrated into the template file, not just variables. You can use arithmetics, function calls, packages, the walrus operator etc. This raises the question of which values are availabes in the expressions.
The way the eval() function works (and any expression evaluation in python, for that matter), there are two namespaces that maps identifiers to their values:
- the global namespace contains functions, variable, etc... that are available outside of the current scope, but nevertheless readable. It cannot be modified.
- the local namespace contains things that are available, and will also store variables, functions etc... defined by the expression (local definitions; e.g. with the walrus operator)
When looking for an identifier, python will first look into the local namespace, and then into the global namespace. This means you can mask a global identifier with a local one, so be careful with that.
When starting, the fileval.py script will build the local and global namespaces to use for all evaluations. They are created empty, but can be populated with the --global-namespaces and --local-namespaces arguments in the command-line.
Note
Note that the same two namespaces are used for all expression inside the processed file. This means you can modify the local namespace with the walrus operator and re-use the value latter in the same file.
Those argumentes take a list of import names (the name written after import in a python import statement, e.g. re for import re and typing.Any in import typing.Any) and populate their respective namespace with the content of the package. You can also add a final dot and a name at the end of the dotted chain, with the name of a value inside the package, to populate the namespace with the content of that value (it must be a mapping, e.g. a dict).
For instance, in the Makefile (and make.bat), the values in arm5_py_integration/__init__.py:GLOBALS are imported with --global-namespaces arm5_py_integration.GLOBALS.
For further details, refer to the help message of fileval.py.
Documentation
This sheet includes a documentation in the file documenation.md that details how it works and provides some inner details (e.g. the available rolltemplates). When you generate the final sheet, the documentation is converted to HTML and embedded into the sheet, so that players can easily access it in-game.
Translation Notes
Some of the translation tag name have suffixes to indicate where they are used. If a particular tag is used in several places, I haven't added any suffixes.
-rt: Used in a roll template. Example: "defend-rt"-m: Used in a roll button macros or similar. Example: "weakness-m"
Deferred attribute lookup
The sheet uses deferred attribute lookup for spells and abilities. This is simply a clever way of writing roll20's rolls, so that you fetches the value of an attribute named according to another attribute value. That is, if your spell's art is Creo, you store the value 'creo' in the attribute linked to the spell (so that you can use the name, e.g. for inline labels), but you're able to lookup the Creo_Score attribute for the roll.
The Roll20 dice engine accepts up to 99-levels of nested attributes lookup (using the @{attr_name} syntax). If you use adjacent attributes lookup that, when resolve, yields a new attribute lookup, the next pass will resolve that attribute lookup normally. This is exactly like having an attribute lookup inside another, but the inner lookup is spread into several attributes. The important things is that all parts are resolved during the same pass.
Example
An example and a table makes things easier to understand. The "system" attributes used to build the query have the same name as they do in the sheet. We use "NAME" as a placeholder for the character's name.
Initial roll formula:
@{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_Score@{sys_rbk}First Pass
Input:
@{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_Score@{sys_rbk}
@{sys_at} @{character_name} @{sys_pipe} @{spell_tech_name} _Score @{sys_rbk} VALUE @{ NAME | Creo _Score } Output:
@{NAME|Creo_Score}Second Pass
Input:
@{NAME|Creo_Score}Output: the character's score in Creo
This makes it possible to use inline labels that shows the name of the attribute that was looked up, e.g.
@{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_Score@{sys_rbk} [@{spell_tech_name}]
Assuming you have a Creo score of 3, this yields 3 [Creo], which tells you where that +3 comes from. The sheet pushes this further, as the inline labels use the same deferred attribute lookup technique, to translate the labels to you local langages: a sheet worker creates attributes ending in _i18n (short for internationalization) -- such as Creo_i18n -- that contain the local translation of the word. It then uses deferred attribute lookup to translate the inline label.
While this is not very useful for Arts (ince many langage just use the Latin word, it is indeed useful for translating characteristics names in ability rolls, or words & gestures in spell rolls etc.