Character Listings#

One of the things NPC was created to do is to generate a public listing of characters. The listing system is a bit complex, but can output correctly formatted entries for every type of character in every system, with built-in support for hiding character attributes – and entire characters – from the output.

Listing Configuration#

Listing settings are stored within the campaign.characters namespace in the listing key. They define defaults, but most can be overridden by arguments to the list command.

Note

There is an additional listing.metadata key which is currently ignored.

listing.format #

type: string required: no

Name of the default output format. Built-in formats are markdown and html.

Example:

listing:
    format: markdown

listing.group_by #

type: list required: no

List of tag names which are used to group characters together using shared values. Subsequent tag names define subgroups. Groups and subgroups are given a header in the output.

This list accepts any defined tag, as well as a few special keywords:

last_name:

The last word of the character’s name.

last_initial:

The first letter of the last word of the character’s name.

first_name:

The first word of the character’s name.

first_initial:

The first letter of the first word of the character’s name.

full_name:

The character’s name.

Example:

listing:
    group_by:
        - location
        - last_initial

listing.sort_by #

type: list required: no

List of tag names which are used to sort characters within groups. Each tag is applied in order to resolve ties.

This list accepts any defined tag, as well as same special keywords as listing.group_by .

Example:

listing:
    sort_by:
        - last_name
        - first_name
        - full_name

listing.base_header_level #

type: int required: no

The first header level to use within a listing. When listing.group_by is used, this is the header level of the top-level group names. When groups are not used, this is the header level of the character names. Subgroups and characters are given lower numbered headers as appropriate.

The header level corresponds directly to the <h1> through <h6> tags in HTML. A value of 1 is most common when the listing will stand alone, while a value of 2 is useful when the listing will be embedded in a document that has its own top level header.

Example:

listing:
    base_header_level: 1

Templates#

NPC 2.0 uses the Jinja template engine to render character listings. Each character is rendered using a template, which is looked up based on the character’s type and the campaign’s game system.

The inner workings of NPC’s templates are written entirely using Jinja’s template language, so it’s a good idea to familiarize yourself with it if you want to customize your templates.

Default Templates#

The default templates display all information for the Default Tags in NPC. Various type-specific templates exist for the character types of the built-in Game Systems. If you’re using these systems, you may be able to use the default templates without any changes.

Custom Templates#

If you make a new system, or want to change how a certain character type is displayed, you’ll need to make a new template file. Name it using the character type key and use either .html or .md for the file extension, depending on the output format you’re targeting.

Templates for a single campaign can go in the campaign’s settings directory at <campaign>/.npc/templates/characters. Shared templates should go in your user settings at <user settings>/templates/characters/<game system key>.

For example, if you’re making a new template for the Changeling type to be shared by multiple campaigns, you’d create the file <user settings>/templates/characters/nwod/changeling.html.

If you want to generate everything from scratch, that’s it! You can write Jinja code as normal.

See also

See the Jinja Template docs for information on how to write templates in general.

NPC supplies view objects to each template, along with some new filters. See Data and Helpers for details on what is available from NPC.

File Locations#

Templates are named using the character type key and the output format’s extension. To find the correct template to use, NPC will check these directories in order. If the expected file does not exist in that directory (or the entire directory is not found), NPC moves on to the next in the list.

  1. <campaign>/.npc/templates/characters

  2. <user settings>/templates/characters/<game system key>

  3. <npc package>/templates/characters/<game system key>

  4. <user settings>/templates/characters

  5. <npc package>/templates/characters

This ensures the following order of precedence:

  1. Campaign-specific template

  2. System-specific user template

  3. System-specific internal template

  4. Generic user template

  5. Generic internal template

If no file is found for the character type, then the same directories are checked again for the generic character.<ext> template.

Base Templates and Blocks#

If you don’t want to start from scratch, you can inherit from one of the base character templates:

{%- extends "character-base.html" -%}

Putting this at the top of the file will give you access to various blocks which you can extend with new tags, or replace with custom content. To extend a block, declare it in your file and call super() to render the default contents, then insert your own additions.

{%- extends "character-base.html" -%}

{%- block aka -%}
    {{ super() }}
    {%- if has("court") -%}
        <div>
            {{ character.court.first() | title }} {% if character.court.has("role") -%}
                ({{ character.court.role.all() | join(", ") }})
            {%- endif -%}
        </div>
    {%- endif -%}
{%- endblock -%}

Tip

If you want your custom content to appear before the default block contents, just put your own code before the call to super(). If you want to hide the block, define it and don’t call super() at all.

These are the available blocks:

name:

The character’s name and a marker if they are dead.

portrait:

The portrait image, if @portrait is set.

aka:

Titles and other names the character has.

vitals:

Personal info, like race, age, location, and pronouns.

orgs:

Organization membership.

location:

Where the character is found.

employment:

Details about where the character works and what they do.

system:

Empty block for system-specific tags.

appearance:

The character’s @appearance.

description:

The character’s description.

dead:

Details about the character’s death.

Tip

Blocks marked with include tags whose data may be formatted in markdown. Use the md or mdi filters to convert the tag contents to HTML if desired.

Data and Helpers#

NPC supplies these properties to every character template:

header_level:

The current header level to use for this entry’s header text.

character:

The CharacterView object whose data should be displayed.

has:

A simple helper function to test if the character has a named tag.

The character is a CharacterView object with a few special conveniences to make writing templates less onerous.

The Character View#

Each template is passed a single character object, which is a CharacterView. This provides a read-only look at that character’s properties and tags, condensed down for ease of use.

On its own, printing the character will simply show its name. Convenient for indexes and the like. Added in NEW_VERSION

{{ character }}
Grete Mann

Each character will always have the following properties:

type:

The type key

description:

The description

realname:

Full character name

mnemonic:

Quick-reference mnemonic

Beyond these properties, a character is likely to have properties named after various tags. Since tags can (almost always) appear more than once, tag attributes are actually a collection of multiple tags. There are a number of convenience methods associated with these collections, as well as the tags they contain.

In addition, there are some helper methods on the character itself.

Tip

The global has() helper method is a shortcut for this same method, character.has().

Tag Collections#

When accessing a tag attribute on a character view, you get a tag collection. This special view contains a list of all tags with the given name. It has some specific behavior available to make views easier to write.

First, when printed, a tag collection displays the value of its first tag. For example:

Character file snippet with tags#
@org Red Riders
@org Blue Busters
Template snippet#
{{ character.org }}
Rendered output#
Red Riders

Then there are its helper methods. First, to iterate over everything in the collection, you must use the all() method:

To get the first tag view alone, use first():

And to get all tag values after the first, use rest():

Tag Views#

Once you have a single tag object, things are simple again. Printing the tag view by itself will render its value. You can also access the tag name if needed.

Finally, tags can have subtags. Accessing them behaves identically to accessing the tags of a character, complete with a tag.has().

Undefined Tags#

If a tag is not defined for a character, Jinja automatically makes use of a special Undefined object (see Undefined Types for more). NPC extends this special object so that it’s safe to call first(), all(), and rest() on these undefined results without causing an error.

A character with no class#
@org Rogues Gallery
Template snippet#
Character class: {{ character.class }}
Rendered output#
Character class:

Added in NEW_VERSION

New Filters#

NPC provides a few additional filters that can be used in character templates.

md#

This filter converts a string of markdown to a block of HTML.

Example:

{{ character.description | md }}
<p>This character, is a character.</p>

mdi#

This filter converts a string of markdown into inline HTML. It uses trim_tags to do so and is not at all clever or safe about which tags are stripped.

Example:

<div>{{ character.description | mdi }}</div>
<div>This character, is a character.</div>

trim_tags#

This filter removes the first and last tag in a string of HTML. It is very simple and does not check that the tags match.

{{ "<div>Something cool</div>" | trim_tags }}
Something cool

Showing and Hiding#

By default, all tags in a character are available to the template. However, since listings are intended to be viewable by your players, you may want to hide certain bits of information. Depending on what you’re hiding, there are a few different tags that can help.

Hiding a Character#

First is hiding an entire character. Adding @delist to a character file completely removes that character from all generated listings. This is ideal for characters whose very existence is secret: upcoming villains, sheets for the evil faceless army, etc.

Hiding the Character’s Type#

Next is hiding a character’s type. Putting @faketype type key in a character file causes all listings to show that type instead of the character’s real type. It’s useful for situations like when a powerful wizard is pretending to be a traveling fireworks salesman, or when a changeling’s Fetch is under cover as a mundane human.

Tip

Setting @faketype for a character changes the character’s apparent type throughout the entire listing process. This means that a different template may be used to render the character’s entry. However, it does not automatically hide any other tag values. Tags which are shared by the character’s real type and fake type will be available to the template. If those tags reveal secret info, use @hide to conceal them separately.

Hiding Tags#

Last up is hiding specific tag values. The @hide tag is flexible and can conceal every instance of a tag or a specific instance of a tag, and works on nested subtags, too. It’s great for hiding that the mayor is secretly a don in the local mafia family, or that a prominent thief is actually an undercover guardsman.

Tip

The @hide tag works just fine to hide the character’s type. A type-specific template may still be used, though, which can inadvertantly reveal information. To avoid that, it’s usually better to use @faketype instead of outright hiding the type.

The syntax of the hide tag takes this form:

@hide [tag name] >> [tag value] >> [subtag name] >> [subtag value] >> ...

Here are some examples to show it in action.

Hiding Every Tag Instance#

@dead Mr. Fritz died seven years ago, under mysterious circumstances.
@hide dead

This will cause the listing for Mr. Fritz to behave as though the @dead tag did not exist on his sheet. His entry will not mark him as deceased and the notes about his death will not appear.

Note

Behind the scenes, NPC uses the special value all to represent hiding all instances of a given tag. Thus, @hide dead is identical to @hide dead >> all. Be sure not to use all as the value for any tag, or you may end up with surprising results if you try to hide it.

Hiding One Tag Instance#

@name Eggsy
@name Chuckles
@hide name >> Eggsy

This will hide the @name entry Eggsy while leaving Chuckles available to the template.

@name Eggsy
@with Kingsmen

@name Chuckles
@with Family

@hide name >> Eggsy

This does the same thing. The subtags of a hidden tag are also unavailable to the template, so you don’t need to hide them separately.

Hiding A Whole Subtag#

@name Eggsy
@with Kingsmen

@name Chuckles
@with Family

@hide name >> Eggsy >> with

Instead of hiding the nickname Eggsy, this will only remove the @with info for Eggsy.

Note

Hiding @with for Eggsy does not hide the corresponding tag for Chuckles. There is currently no way to hide all instances of a subtag for all instances of a given parent.