Sloppy MarkUp Language Specification 1.0

Table of Contents

Introduction

Sloppy MarkUp Language (SMUL) is a procedural markup language that primarily utilizes troff- or groff-like syntax, with tokens on specific lines dedicating all following text for a specific format. SMUL is designed primarily for use in community contributions to projects like In Search of Dangerous Sloppy, where passages are formed from discrete nodes that make up objects like paragraphs, images, and titles. SMUL is designed to be easily chunked into such nodes while also being easy to write and parse by eye.

SMUL is not meant to be used as a general-purpose markup language, nor is it meant to be processed into a general-purpose output like *.txt or *.html. It is meant only for processing into specialized formats stored in a viewer program’s memory to be freed after viewing. For example, in In Search of Dangerous Sloppy, SMUL documents are rendered into a Godot node tree containing RichTextLabel and TextureRect objects. Such objects, as invoked in SMUL, are referred to in this document as constructs.

This Specification

Sloppy MarkUp Language 1.0 is designed not to be a specification of a SMUL parser implementation. Rather, it is a specification of how a SMUL document should be written. In other words, this document serves as some overhead, and then a general reference. No specifics are given as to how a SMUL parser should interpret and render SMUL to an object tree.

Know your Structure

Projects like In Search of Dangerous Sloppy that use SMUL have a specific file structure that SMUL can use for constructer variables. It’s good to know which part of the project structure you’re accessing with a SMUL construct and therefore which parts of it are inaccessible. A basic rundown is as follows:

root
   ├data
   │   └characters
   │       └*.tres
   ├graphics
   │   ├landscapes
   │   │   └*.png
   │   └portraits
   │       └character_name
   │           └*.png
   ├scenes
   │   └*.txt
   └scripts
       ├*.gd
       └scene_scripts
           ├*.gd
           └character_name
               ├win.gd
               └loss.gd

As In Search of Dangerous Sloppy is made in Godot Engine, the example file tree here is given in the form of a godot project. The following subsections will explain the most relevant directories.

/data/characters

This directory contains character files that describe a character’s appearance and attributes. For example, it may contain the character’s name, gender, stats, and class.

/graphics/landscapes

This directory contains the images (in *.png format) that are designed to take up the full width of the in-game passage display. It is recommended to use images that are twice as wide as they are tall.

/graphics/portraits/character_name

This directory contains the portrait images for character_name. This doesn’t refer to a literal directory; it is many directories, each named after a character. This directory may be further subdivided to categorize characters. Valid directories are things like /graphics/portraits/witch/neutral.png and
/graphics/portraits/goblin/male/neutral.png.

/scenes

This is where SMUL documents themselves go.

/scripts

This is where scripts related to the project’s functions go. This has nothing to do with SMUL. The subdirectories here have little to do with SMUL, but are handy to know for various reasons when writing SMUL scenes.

/scripts/scene_scripts

This is where scene scripts go. Scene scripts are scripts (*.gd in the case of ISoDS) that are loaded alongside their matching scene. These are used to change the navigation buttons and perform complex logic, and are required such that each scene has an outlet somewhere.

It is recommended that scene scripts do not have any function upon load other than setting the navigational buttons, as most scenes can be saved at and reloaded an arbitrary number of times. In other words, if a scene raises the player’s stats, that should be done upon clicking a “Next” button or something similar, or the actual stat raising should be done when clicking the button in another scene that leads to this scene.

/scripts/scene_scripts/character_name

Much like the equivalent directory in /graphics/portraits, this is not one literal directory. This directory should contain at least two scripts: win and loss. These respective scripts are executed upon defeating or losing to character_name in battle. These scripts can be as simple as sending the player back to their base, or can invoke special scenes, or have a plethora of other effects.

Forming a SMUL Document

SMUL documents are written in plain *.txt files. This is done in the name of portability and compatibility, as programs like Godot Engine cannot access any file if their extension is unrecognized, and I don’t want to write a custom importer plugin.

By convention, the file name should be the snake_case equivalent of the scene’s title. If the scene lacks a title, it should be a short description of the scene or its purpose. For example, document_title.txt.

No particular construct is necessary in a SMUL document. You can have a scene with no title, no author, no paragraphs, dialogue, or anything else, though an empty scene is kind of pointless, isn’t it? This is all just to say that if you don’t want a title, you needn’t include one; titles are large and take up a lot of space, and for minor scenes can just look gaudy. Additionally, if you don’t want to be credited for a scene, don’t include an author construct - simple as.

Constructs

SMUL documents are made of constructs that indicate how content should be laid out and styled. All text rendered to the scene will belong to a construct. That is to say, any text laid out in the beginning of the document before a construct and any text laid out following a construct that does not collect text will be discarded by the SMUL parser. The following are the tokens supported in version 1.0, along with examples of their usage. A table is given below as referance.

Invocation Name Rendering
_TL Title Large font size
_AU Author Small font size
_HR Horizontal Rule Horizontal Rule
_PP Paragraph Indented paragraph
_LP Unindented Paragraph Unindented paragraph
_DL [character_name]/[character_expression] Left-Aligned Dialogue Dialogue box
_DR [character_name]/[character_expression] Right-Aligned Dialogue Dialogue box
_IM [image_name] Landscape Image Image filling passage view
_CH [character_name] Character Register None

Title: _TL

The constructor _TL is used to invoke a title construct. For example:

_TL
This is a title

Title constructs have a much larger font size than normal paragraphs.

Author: _AU

The constructor _AU is used to invoke an author construct. For example:

_AU
SloppyDev

Author constructs have a much smaller font size than normal paragraphs.

Horizontal Rule: _HR

Horizontal rule constructs appear as horizontal bars in the passage. Horizontal rules do not collect the text that follows them. It is recommended to use a horizontal rule after the passage’s title and author, as well as to denote narrative time skips. For example:

_TL
Example Title
_AU
SloppyDev
_HR
This text will be discarded and never show up in-game.

Indented Paragraph: _PP

Paragraph constructs are invoked with _PP. Paragraphs formed with _PP are automatically indented. It is recommended to use this construct for most applications. For example:

_PP
This is a paragraph. When rendered, it will be automatically indented. Any lines
that follow a paragraph constructor (or any other text-collecting constructor)
are automatically merged into the corresponding construct, with newlines being
replaced with spaces. This allows for documents to be arbitrarily broken when
written and appear in-tact when rendered - Vim (et al.) users rejoice!

Unindented Paragraph: _LP

Unindented paragraphs are identical to normal paragraphs, just without the indentation. This is most useful in conjunction with justification formattings through BBCode, where the indentation of a normal paragraph would cause an errant line-break to appear. For example:

_LP
[center]Despite being called an 'unindented' paragraph, this paragraph uses
BBCode to stay centered![/center]

For more about BBCode, see its section later on.

Dialogue: _DL & _DR

There are two types of dialogue constructs in SMUL, left-oriented and right-oriented, which are constructed in the following format:

_DL [character_name]/[character_expression]
Dialogue text.

_DR [character_name]/[character_expression]
Dialogue text.

In this context, [character_name] is the name of the directory in /graphics/portraits and [character_expression] is the name of the *.png file (without the extension) in the aforementioned directory. Lines following the invocation of the constructor are collected and formatted into the body of the dialogue construct.

Left-oriented dialogue should always be preferred to right-oriented dialogue, except in cases where multiple dialogue constructs follow each other, in which case they should alternate between left and right. For example:

_DL witch_apprentice/neutral
Give me your credit card information.
_DR goblin/male/confused
What's a credit card?
_DL goblin/female/resolute
It's just some witchy nonsense. It doesn't exist!
_DR witch_apprentice/flustered
N-no, it does exist. You just wouldn't understand it!

Landscape Image: _IM

The constructor _IM invokes a landscape image, which will fill the width of the in-game passage display. It is formed as _IM [image_name] where [image_name] is the name of the *.png file (without the extension) located in the /graphics/landscape directory. Landscape images do not collect the text that follows them. For example:

_IM desert_wastes
This text will be discarded and never show up in-game.

Character Register: _CH

The character register construct is a special construct that loads a character data resource from /data/characters directory into the scene managers. This character data is used to parse dollar-sign notation as described in the next section. Character register constructs do not appear in the passage and do not collect the text that follows them. For example:

_CH wolf_girl
This text will be discarded and never show up in-game.

Dollar-Sign Notation

Dollar-sign notation is used to replace an identifier expression with text corresponding to attributes of either the player or the character data loaded into the character register. These attributes include variables of the data, as well as pronouns (and other related things). These attributes are invoked with $ followed by either PLAYER for the player or CHAR for the character data loaded into the character register, followed by a . and the variable or pronoun feature you wish to access. For example, if the player’s name is “Joe”:

_CH wolf_girl
_PP
This is a paragraph. When rendered to the in-game passage view, "$PLAYER.name"
will become the player's name, and "$CHAR.ref" will be replaced with the current
loaded character's reflexive pronoun.

The above excerpt would render in-game as:

  This is a paragraph. When rendered to the in-game passage view, “Joe” will become the player’s name, and “herself” will be replaced with the current loaded character’s reflexive pronoun.

Pronoun Access

There are two types of attributes that can be accessed through dollar-sign notation: pronouns and character data. While called “pronouns” in the context of SMUL, they include third-person pronouns as well as some other terms that can be declined by gender. A full table is given below. In the “Invocation” column, $CHAR can be replaced with $PLAYER to refer to the player instead of the character register. Additionally, if the first letter of the attribute is capitalized (as in $CHAR.Nom), its rendering will be as well, this is useful for capitalizing the beginning of sentences.

Invocation Name Rendering
$CHAR.nom Nominative he, she
$CHAR.acc Accusative him, her
$CHAR.ref Reflexive himself, herself
$CHAR.pos Possessive his, her
$CHAR.gen Genitive his, hers
$CHAR.bln Blond(e) blond, blonde
$CHAR.dim Diminutive boy, girl
$CHAR.com Common Noun man, woman
$CHAR.for Formal Noun gentleman, lady
$CHAR.hon Honorific sir, ma’am

Character Data Access

If you try to access an attribute that isn’t 3 letters long, the parser assumes you are trying to access a variable from the character data file. This includes things like name and species, as well as more technical details of the character. A table of relevant attributes is given below. If you try to access an attribute that does not exist, no operation will be performed on it and it will render just as you typed it.

Invocation Rendering
$CHAR.name The character’s name
$CHAR.species Species name, lowercase
$CHAR.sex Male, female, herm, null, lowercase¹
$CHAR.appearance Masculine-feminine scale, lowercase
$CHAR.height Height in inches
$CHAR.tall Height in feet & inches
$CHAR.breast_size Flat-gigantic scale, lowercase
$CHAR.ass_size Same as above, lowercase
$CHAR.penis_size None-gigantic scale, lowercase
$CHAR.character_class Class name, uppercase
$CHAR.skin_color Skin color, lowercase
$CHAR.hair_color Hair color, lowercase

¹Sometimes unset; defaults to male; manually set on a basis of ‘seems right’.

BBCode

While not part of SMUL, In Search of Dangerous Sloppy uses rich text labels to display the text parsed by SMUL, and those labels support Godot Engine’s flavor of BBCode. A reference for it can be found here. Additionally, if you wish to use [color] tags for emphasis, please stick to Godot’s supported subset of X11 color constants, a reference for which can be found here.

With these tags, you can format text with italic, bold, and colored fonts, as well as control the text’s alignment and other features. As a general style guide, the primary emphasis over just a few words should be [b]bold[/b]. The stronger emphasis should be [color=webmaroon][b]'webmaroon' colored bold[/b][/color]. When emphasizing an entire sentence, only [i]italics[/i] should be used. As a particularly striking emphasis, [shake]shaking text[/shake] may be used.

Considerations

As it stands, more advanced logic, including setting ISoDS’s navigational buttons has to be done with scripting. When loading a SMUL scene, the parser checks /scripts/scene_scripts for a GDScript file that matches the name of the scene. If it exists, it is allowed to take over after the parser builds the scene in the passage display. With this, anything that is possible in GDScript can be done. This route has been chosen over including complex logic in SMUL for two reasons: it’s easier to simply use GDScript in conjunction with SMUL scenes, and anything that could be implemented in SMUL already exists in GDScript and is no doubt easier to use. SMUL exists to make common passage components easier to write, not to control the flow and logic of the entire game.

If the parser is instructed to load a scene file that does not exist, it still searches for a GDScript file of the same name and, if it exists, allows it to take over as usual. This allows for a scene transition to dump the player at a metaphorical hub that can instantly deide which scene the player is supposed to see next. For example, a scene transition call could be set to transition to a sceneless script that then decides randomly between several encounters.

Sample Scene

For completion, here is a sample of a scene written in SMUL:

_TL
Credit Card Fraud
_AU
SloppyDev
_HR
_PP
Once upon a time, a Witch Apprentice came across two free-range goblins babbling
over something unimportant. Seeing an opportunity, the witch apprentice
introduced herself.
_DL witch_apprentice/neutral
Hello, goblins. Give me your credit card information.
_DR goblin/male/neutral
What's a credit card?
_DL goblin/female/angry
It's just some witchy nonsense! It doesn't exist!
_DR witch_apprentice/angry
N-no, it does! Credit cards are, uh, um...
_DL goblin/female/angry
Doesn't exist! Doesn't exist!
_IM witch_apprentice_storming_off
Related
SMUL