Monospace Engine Manual
Written for Monospace Engine Version 0.1.0.
For more information and the latest updates, visit monospace.games/engine.
Copyright © 2024 Monospace Games
Intro
Hello!
Thank you for your interest in Monospace Engine.
Monospace Engine is a 2D game engine designed to make making games simple and fun, and this manual contains everything you need to know to make games with it, or modify any Monospace Engine game you want.
It is split into two sections: Basics and Reference. The Basics section explains the central ideas behind the engine and how it works. It's a relatively short tutorial for anyone looking to get started. The Reference section on the other hand contains a complete list of available features, alongside examples and explanations.
This manual is written for Monospace Engine Version 0.1.0. To keep up with the development of Monospace Engine visit monospace.games/engine.
If you have any suggestions or need any help feel free to send me an email at monodev@monospace.games.
I hope you like the engine and have fun creating things with it!
Basics
The central idea of Monospace Engine is that games are made up of files that anyone can access and edit. These files can be things like images, sounds, plaintext files, or Lua code, but the important thing is that it's all user configurable. Anyone can take any game and edit, remove or replace any part of it, or add new things as they want.
Let's take a look at a Monospace Engine game to see how this works. Open the directory in which you have the engine installed, and you'll see that it contains the following:
- monospace.exe
- The engine executable
- user-manual.html
- This manual
- games
- A directory containing installed games
- common
- A directory containing common data
Now open the games directory, and you'll see that it contains a directory for each game you have installed. There is one thing that is common across these directories: they all must have a main.txt file in them. These main.txt files are plaintext files that can be opened (and edited!) with any text editor, such as notepad. Let's go into the samplonia directory, and take a look at its main.txt file:
GAME OBJECT [SAMPLONIA] FILES: tilesets.txt maps.txt actors.txt keymaps.txt PLAYER: Sampson CONTROLS: player_keymap
You already know what this describes! A game called SAMPLONIA, that's made up of the listed files, where the player controls a character named Sampson, and the controls are determined by player_keymap. You could add another file to that list and it'd become part of the game. Or you could go into any of the files listed and change them to your liking. Or you could change the player character, or the controls, etc. It's all configurable!
But before we get ahead of ourselves let's create our first game, so we'll have an independent project to work on. Go back to the games directory, and create a copy of the samplonia directory by copy-pasting it. Then open the main.txt of the new directory and change the name of the game (the [SAMPLONIA] part) to something new, e.g. [SAMPLONIA 2].
Congratulations! You just made your first game! That's all it takes to create a game in Monospace Engine: a directory under the games directory, which contains a main.txt file, which describes a GAME OBJECT as shown above. Launch the engine and you'll see that your new game is now listed in the games menu. However it's still identical to SAMPLONIA, and to change that we need to know what Elements are.
Elements
The main.txt file we looked at in the previous section started with the words GAME OBJECT and contained the description of a game named SAMPLONIA. Take a look at any other text file listed in that description, such as tilesets.txt and maps.txt, and you'll notice that they have a similar form. They also start with a type declaration at the top ( TILESET OBJECTS or MAP OBJECTS ) and contain definitions of tilesets and maps that are used by the engine. These assets are all called elements, and they provide Monospace Engine everything it needs to know to run games.
All elements have a name, a type, and a description. The available types are the following:
- GAME OBJECTS
- Games that can be run by the engine.
- MAP OBJECTS
- Maps which make up game worlds.
- TILESET OBJECTS
- Tilesets, used in maps.
- FONT OBJECTS
- Fonts used to display text.
- MENU OBJECTS
- Menus for user interaction and HUD.
- CAMERA OBJECTS
- Cameras determine how players are tracked.
- KEYMAP OBJECTS
- Keymaps define how characters are controlled.
- ACTOR OBJECTS
- Actor objects determine the types of characters in the game.
- ACTOR INSTANCES
- Actor instances are specific characters in the game world.
We build our games out of elements belonging to these types. There are many rules that determine how an element of each type is described, but the good thing is that you don't need to know them all! That's what the Reference section is for. As long as you know what each type of element does you'll be able to look up how to edit them easily.
How Elements Are Built
Remember that the central idea of Monospace Engine is that it loads all assets from files that are accessible and editable by users. We now know these files to contain elements. But how does the engine know which files to load? And what types of files can it work with? We'll explain these in this section.
When you launch the engine it starts looking for files in the following manner:
First it looks at the directory named common next to the executable.
This directory is expected to contain a main.txt similar to the game directories we've talked about in the previous sections. But the game object found in this file is special: it's named COMMONS, and as its name suggests it contains not a game but the common assets that are required for basic functionality, such as the main menu and the fonts used in it. The files belonging to this game object are loaded immediately.
Then the engine looks at each directory in the games directory, and loads their main.txt files. These files contain game objects, and the files belonging to these games are not loaded until the game they belong to is launched.
Aside from the main.txt files in the special locations listed above, a file is only loaded if it's part of a GAME OBJECT. And there are two kinds of files that a GAME OBJECT can contain: plaintext files and Lua scripts.
The files we've looked at so far have been plaintext files. They're just regular text files that you can open and edit with any text editor, and the description of an element in them looks like this:
[ELEMENT_NAME] PROPERTY_1: VALUE_1 PROPERTY_2: VALUE_2 VALUE_3
We can load elements from Lua scripts as well. These scripts are also regular text files that you can open with any text editor, they just have the ".lua" extension to signify that the text they contain is Lua code. Lua is a programming language, but don't worry if you're not familiar with programming, it's notoriously small and straightforward. As an example here's how the above element definition looks like in Lua:
CreateElement("ELEMENT_NAME", "ELEMENT TYPE", {PROPERTY_1 = "VALUE_1", PROPERTY_2 = { "VALUE_2", "VALUE_3" }})
Return to SAMPLONIA
Let's go back to the game we created at the beginning of this tutorial to put our new knowledge to use. We'll do something simple, but not too simple: we'll create a menu that launches when the player presses a button.
To figure out how to do this let's think back to the element types we've seen. You may recall the types MENU OBJECT and KEYMAP OBJECT. Since we want to create a menu, we'll create a new element of type MENU OBJECT. And since we want this menu to launch whenever a key is pressed, we'll edit a KEYMAP OBJECT. It usually pays to think in terms of elements.
Let's start with the menu. It'll be a simple one: a fullscreen menu with a button that says "Hello!", and when you press it the menu closes and the game resumes. Create a new empty text file in our game's folder, e.g. mymenu.txt, and insert the element's type and name into it:
MENU OBJECT
[MY_MENU]
We now need to write the description. For that let's take a look at the Reference section of the manual, I'll link to the relevant part here. Feel free to take a moment here to read a bit into how menus are described.
The necessary information we want to provide in the description is the following:
We want our menu to be fullscreen, so we'll use the IS_FULLSCREEN property.
A button mint. A mint is something that you can interact with in a menu, and its name is a portmanteau of menu + interaction. These are usually called widgets in other programs.
For the mint itself we need the following information:
Type - A mint's type determines how it can be interacted with. We want a button.
Position and dimensions - Where to place the mint, and its width and height.
Text - We want the button to say "Hello!".
A command that executes when we press the button.
Putting all this together, here's what our menu looks like:
MENU OBJECT [MY_MENU] IS_FULLSCREEN MINTS: MY_BUTTON: TYPE: BUTTON POSITION: (0 PIXELS, 0 PIXELS) DIMENSIONS: (4 TILES x 1 TILE) TEXT: Hello! PRESS_SCRIPT: CloseSelf()
In the position and dimension values we've used PIXELS and TILES, which are length units similar to centimeters and inches in real life. You can read more about the available length units here. Our button is 4 tiles wide and 1 tile long, and to keep things simple I've chosen to set the position of our mint to the origin, which corresponds to the upper left of our screen. After you finish this section feel free to experiment with these values!
Now that our menu object is ready, it's time to make the file it's in a part of our game. Open the main.txt file of the game and add mymenu.txt to FILES. Take note of the PLAYER section as well: you'll see that the value associated with the CONTROLS property is the name of a keymap object. This is the element we'll need to edit to make our new menu launch when the player presses a button. We'll find that element in the keymaps.txt file.
Open the keymaps.txt file and look for the KEYMAP OBJECT named player_keymap. Then add the following line to its description:
X: LaunchMenu("MY_MENU")
And that's it! This line instructs the keymap to run the LaunchMenu("MY_MENU") command when it detects an X keypress. And since the keymap is used by the player, whenever the player presses X our menu will launch.
Wrapping Up
We've covered the core concepts of Monospace Engine in this tutorial. What games are, what elements are, how elements are built, how to create new elements or edit existing ones - these make up our big picture. Now it's time to start looking into the finer details, which are explained in the Reference section. But before you go I'd like to make a few suggestions.
First, remember that the Reference section aims to be complete. This means that it's pretty long and written more to be consulted than read front to back. You should skim it so that you have a general idea of what's available, read the bits you find interesting, and consult it when you're actively working on things.
Second, start small and learn by doing. Make minor tweaks to other games. SAMPLONIA in particular has been written to be as small and instructive as possible, so feel free to continue experimenting with it.
Finally you may benefit from installing a fully featured text editor if you're not already using one. Default text editors such as Notepad on Windows and Gedit on Linux are fine but very lightweight. They lack helpful features like syntax highlighting and code completion. This will not matter much at first when you're mostly dealing with plaintext files, but as you move on to working with Lua scripts the difference these features make will be night and day. There's a wide variety of text editors, each with differing capabilities. More technically inclined people tend to prefer Emacs or Neovim, but be aware that these can take a fair bit of time and effort to learn. If you're new to programming I'd suggest using NotePad++ on Windows and Kate on Linux.
And that's it for this section of the manual! Thank you for reading, and I hope you have fun creating things with the engine.
Reference
Initialization
Each time the engine is launched it goes through an initialization process. During this process the engine parses files found in certain locations relative to the engine executable, with the purpose of building elements. These files are the following:
First the file commons/main.txt is parsed. This file is expected to contain a GAME OBJECT named COMMONS. This game object is intended to contain essential information such as the main menu and the fonts used in it, and the files associated with it are loaded immediately.
Afterwards the engine looks at each directory in the games directory, and parses their main.txt files. These directories are expected to contain assets belonging to individual games, and their main.txt files are expected to contain elements of type GAME OBJECT. Unlike COMMONS, the files belonging to these game objects are not loaded immediately, but are loaded only when the game they represent is launched.
After this process the engine will look for a menu object named MAIN_MENU, and launch an instance of it. This menu is where user interaction begins.
Outside of the initialization process mentioned above, the engine will only parse files when a game is launched. It is capable of parsing two types of files: Plaintext Files and Lua Scripts.
Plaintext Files
Plaintext files use the .txt extension and contain element descriptions in the following basic form:
TYPE DESCRIPTION [ELEMENT_1_NAME] TYPE DESCRIPTION property1: value1 property2: value2 property3: value3 property4: property5: value4 [ELEMENT_2_NAME] ...
Type Descriptions
The first non-whitespace, non-comment line of a plaintext file can be a type description, such as ACTOR OBJECTS. This type description can also be incomplete (e.g. just OBJECTS, or ACTOR), or can simply be omitted.
Elements can also have type descriptions on their first lines, and these can also be incomplete or omitted. The main rule is that the combination of file and element type descriptions must provide a full type for the element, and must not conflict.
Some examples:
If the type description of a file is ACTOR OBJECTS, none of the elements in that file need to have type descriptions, they'll all be interpreted as describing actor objects. They can still have the type description ACTOR OBJECT, as it will not conflict with the file type.
If the type description of a file is OBJECTS, elements in that file need to have type descriptions that provide the missing part of the type, e.g. ACTOR, MAP. Elements in the file can have different types provided that they're all objects. They can also specify that they are objects, but it's not necessary.
If a file has no type description at the top, all elements in that file must have full type descriptions. These type descriptions can differ from each other, and there are no restrictions to what type of elements such a file can contain.
Plaintext files with full type descriptions at the top have less parsing overhead.
Element Descriptions
Element descriptions in plaintext files begin with unindented lines that contain the element's name in brackets. They end when a new description starts, or the file ends, so they can contain empty or commented out lines.
These descriptions contain the properties that describe the element according to the syntax its type requires (see elements). These properties can be nested through indentation, or be provided on the same line by being separated by colons. Omitting colons after a property indicates that it expects no nested elements after it, and an increase in indentation in such a case will result in an error. More than one colon on a single line will also result in an error. These rules are intended to make the tree structure of descriptions obvious at a glance:
[ELEMENT_1] foo: bar: baz // Wrong, multiple colons on the same line foo: bar: baz // Wrong, same reason [ELEMENT_2] foo: bar: baz // Correct foo: bar: baz // Correct
Both tabs and spaces can be used for indentation. Combining them is not a good idea, but if they are combined tabs are interpreted as a skip to the next tab stop with the tab width of eight.
Line Breaks
If a line ends with the backwards slash character \, it'll be parsed with the line below as if they form a single line (in slightly more technical terms the line break will be escaped). This is helpful when a line gets too wide.
Comments
Plaintext files support C style comments, meaning multi-line comments that start with /* and end with */, and single line comments that start with // and end at the line end.
Multi-line comments will also escape line breaks.
Semicolon Fallback
Colons have syntactic significance in plaintext files as separators, but there are certain contexts in which interpreting a colon as just a colon and not a separator is desired (e.g. in font characters, or Lua scripts that use the object oriented function call syntax).
To support such contexts, the engine uses the semicolon as the separator instead of the colon for the line being parsed only if it satisfies the following two criteria:
The line being parsed does not make sense when parsed with a colon (e.g. starts with a colon, or has multiple colons)
The line being parsed contains a semicolon.
Note that this is strictly just a fallback, and the semicolon carries no syntactic significance outside of such cases.
Moreover, if turning a colon into a semicolon syntactically "fixes" the line, you can put an extra colon after the semicolon, which will be ignored:
EXAMPLE_SCRIPT: self:Foo(x) // Wrong due to multiple colons. EXAMPLE_SCRIPT; self:Foo(x) // We used the semicolon to fix it, // but now it has only one colon // and the semicolon doesn't work! EXAMPLE_SCRIPT;: self:Foo(x) // Adding a colon after the semicolon // causes the fallback behavior to // kick in. EXAMPLE2_SCRIPT; self:Foo(x) \ self:Bar(y) // Doesn't need ;: due to having two // colons already, semicolon is enough.
Lua Scripts
Lua scripts use the .lua extension and contain Lua code. These scripts can use the basic functionality of the Lua programming language such as variables, loops, if/else statements and functions, and they also have access to Lua's basic library and the string, table and math libraries.
Similar to plaintext files, the main purpose of Lua scripts is to create elements. Thus they are evaluated once during engine initialization or when a game is launched, and have access to a restricted subset of the engine's overall scripting functionality. See initialization contexts for details.
Lua scripts are evaluated in the same global environment, and in the order they appear in the game object that contains them. This means that definitions from file A will be visible to file B granted that file A is listed before file B in the game object. (This feature can be useful, but for readability purposes its a good idea to not rely on it too much, and prefer lexical scoping and variables declared local whenever possible)
The engine uses LuaJIT, so the scripts should be compatible with Lua version 5.1.
Elements
Game Objects
Game objects represent games that can be run by the engine. The main information they contain is a list of files that make up the game, and they also specify how the game is to be displayed on the screen and the players that exist in it.
Scripting
Games are launched via the LaunchGame function, which creates a game instance variable named ActiveGame in the global environment. See game instances for the scripting interface of this variable.
Examples
TODO: Insert examples
Syntax
FILES
Paths to files containing elements that make up the game. These files can either be plaintext files or Lua scripts.
File paths must be relative to the main.txt that contains the game object, e.g. for the following directory structure:
example_game ├── main.txt ├── file1.txt ├── script1.lua └── directory └── file2.txt
The expected file paths in main.txt would be:
[EXAMPLE_GAME] FILES: file1.txt script1.lua directory/file2.txt
SCREENS
Monospace Engine is capable of split-screen up to four screens, and the following properties control how each screen behaves, as well as how many screens there are, and how to split the screen.
If you wish to have a single screen that tracks the player, use this property as follows:
[EXAMPLE_GAME] SCREENS: SCREEN1: TRACKS: <PLAYER_NAME>
This property is optional; if no screen information is provided there will be a single screen using the camera object with the name DEFAULT_CAMERA. This screen will not render anything until its camera is attached to something, or explicitly moved to a specific position in the game world (through the camera instance API).
COUNT
1/2/3/4Number of screens the game will be split into.
Optional; if unprovided will default to the number of the screen with the highest number for which information has been provided (e.g. with COUNT unprovided, if any information is registered for SCREEN4, COUNT will be set to 4). If no screen information is provided for any of the individual screens, count will default to 1.
Count is always strictly obeyed, so if you set COUNT to 2 any information provided for SCREEN3 and SCREEN4 will be discarded. Likewise setting COUNT to 4 will always result in four screens, even if no information is provided for screens SCREEN1-4.
SPLIT
HORIZONTAL/VERTICALThe direction in which the screen will be split in.
Optional; will default to VERTICAL if unprovided. Only matters when COUNT is 2 or 3.
SCREEN1/SCREEN2/SCREEN3/SCREEN4
Screens are numbered first from left to right and then top to down. For example if there are four screens, top left will be screen 1 and bottom right will be screen 4.
TRACKS
<PLAYER OR ACTOR INSTANCE NAME>Determines what the camera belonging to the screen will be tracking.
Optional; if unprovided the screen will not render anything until its camera is attached to an instance or a player, or moved to a specific position in the game world.
CAMERA
<CAMERA OBJECT NAME>Name of the camera object to be used by the screen.
Optional; if unprovided will default to DEFAULT_CAMERA.
PLAYER/PLAYERS
Optional. There is no upper or lower limit on the number of players a game can have; you can provide no players at all, or as many as you want.
<PLAYER_NAME>
Name of the player.
<INSTANCE_NAME>
Name of the actor instance that will be controlled by the player.
Optional, players that don't control any instance are valid; they will still be able to use their keymaps.
Multiple players can control the same instance, but one player cannot control multiple instances.
CONTROLS
See keymap parsing.
Optional; providing no keymap information will result in a player with an empty keymap. Controls can still be registered at runtime through the scripting interface.
Scripts associated with player keymaps will be evaluated in player contexts.
Overlapping controls between different players should be avoided (e.g. an A press having meaning for two different players). In case of such overlaps there are no guarantees on who gets to capture the overlapping input.
SCRIPTS
Scripts belonging to the game, optional. See script parsing.
These scripts will be evaluated in game instance contexts.
INIT
Evaluated as the last step of game initialization, after all files belonging to the game have been parsed and evaluated, and the arguments passed to the game have been loaded into its environment.
ACTIVE
Evaluated each frame the game progresses.
RETURN
Evaluated when the game is returned to via a CloseUntil call.
EXIT
Evaluated as the first step of game exit, before the game world and the elements belonging to the game start being unloaded.
Map Objects
Map objects represent individual environments of the game world. They contain information about their size, their bounds, and have canvases on which tiles are placed to determine the appearance and physical environment of the map. They can also specify how they connect to other maps to build interconnected game worlds.
Scripting
When a game is launched, a game world containing all maps available in the game is created. This game world can be accessed through the ActiveGame.world table containing map instances. See map instances for the scripting interface of these variables.
Examples
TODO: Insert examples
Syntax
DIMENSIONS
Dimensions of the map. Width and height must be integers in tile units.
GRAVITY
Gravity to be applied within the map. Must be an integer value in subpixel units.
Optional; defaults to 0.
CAMERA_BOUNDS
See rectangle parsing.
Optional; will default to map size.
Camera bound rectangles tolerate unbound rectangles and omitted values. Omitted values will default to appropriate map dimensions. Integer values provided within the definition are interpreted as being in tile units.
MOVEMENT_BOUNDS
See rectangle parsing.
Optional; will default to map size.
Movement bound rectangles tolerate unbound rectangles and omitted values. Omitted values will default to appropriate map dimensions. Integer values provided within the definition are interpreted as being in tile units.
CANVAS
See canvas parsing.
BACKGROUND_COLOR
See color parsing.
Optional; will default to WHITE.
If provided in RGB format, does not expect an alpha value to be provided.
Tileset Objects
Tileset objects contain tiles, which are placed on canvases to make up the interactable environments of maps, or the decorations of menus. Tilesets can contain two types of tiles: static tiles and sequence tiles. Static tiles use the below listed syntax, while sequence tiles use the sequence syntax and are composed of static tiles.
Static Tile Syntax
DIMENSIONS
Dimensions of the tile. Width and height must be integers in tile units.
Optional; will default to detected dimensions if a texture path is provided.
IS_SOLID
Solidity of the tile.
Optional; if possible will be inherited from the default tile, otherwise will default to false.
Note that tiles must be solid and also be placed on z levels that have base parallax speed to be interactable within maps (see canvas parallaxes).
TEXTURE_PATH
Path to the image containing the appearance of the tile.
Optional for default tiles, required for named tiles.
Examples
A tileset can have any number of tiles. Below tileset has three; two of which are static, and one is a sequence.
[MY_TILESET] STATIC_TILE_1: IS_SOLID: YES TEXTURE_PATH: my_tileset/tile1.png STATIC_TILE_2: TEXTURE_PATH: my_tileset/tile2.png // STATIC_TILE_2 does not have solidity information, // so it'll default to non-solid. SEQUENCE_TILE: STATIC_TILE_1: 30 STATIC_TILE_2: 30 // Shows STATIC_TILE_1 for half a second (30 frames), // then STATIC_TILE_2, repeats. // The solidity of a sequence tile is determined // by the first tile in the sequence, thus due // to STATIC_TILE_1 being solid this tile will // also be solid.
The tile with the name DEFAULT is special: its properties can be listed directly under the tileset itself. Thus the following two tilesets are equivalent:
[MY_TILESET] DEFAULT: DIMENSIONS: (1x1) IS_SOLID: YES TEXTURE_PATH: my_tileset/default.png
[MY_TILESET] DIMENSIONS: (1x1) IS_SOLID: YES TEXTURE_PATH: my_tileset/default.png
The default tile enables tilesets composed of a single tile to be described more concisely, and it can also be used to provide defaults to the other tiles in a tileset as follows:
[MY_TILESET] TEXTURE_PATH: my_tileset/default.png IS_SOLID: YES TILE_1: TEXTURE_PATH: my_tileset/tile1.png // TILE_1 has no solidity info of its own, // so it'll inherit from the default tile // and be solid. TILE_2: TEXTURE_PATH: my_tileset/tile2.png IS_SOLID: NO // TILE_2 has its own solidity info, so it'll // override the default. TILE_ANIM: DEFAULT: 30 TILE_1: 30 TILE_2: 30 // The default tile can be used in animations.
(At the moment only solidity information is inherited.)
Default tiles are allowed to be incomplete (missing information that's required for static tiles, such as the TEXTURE_PATH), and in such cases their purpose is to project information to other tiles in the tileset:
[MY_TILESET] IS_SOLID: YES TILE_1: TEXTURE_PATH: my_tileset/tile1.png TILE_2: TEXTURE_PATH: my_tileset/tile2.png // Both TILE_1 and TILE_2 will be solid.
Incomplete default tiles are not treated as real tiles: they can't be placed on canvases, and are not listed under the tiles of a tileset in the object scripting interface.
Font Objects
Font objects determine the appearance of text. They contain information regarding the characters available in the font and how each character is to be displayed, and are used in the appearance settings of menus and mints.
Monospace Engine uses its own font format and text rendering functionality - this means that it does not use other fonts that may be found on your computer, but instead expects to find fonts in the form of FONT OBJECTS. Just like other element types, FONT OBJECTS allow users to easily create or configure fonts.
The engine ships with three fonts by default: C64, ARCADE and BUBBLES. C64 and ARCADE are general purpose fonts, and BUBBLES is for stylized text. All three are included in the COMMONS game object.
Syntax
DEFAULT_SIZE
Default size for the characters in the font. Width and height must be integers in scaled pixel units.
Required, sets the dimensions of the default whitespace characters, as well as any explicitly provided whitespace characters that lack dimensions.
CHARACTERS
Characters in the font.
<CHARACTER>
A character in the font. Can be any character, e.g. a, b, ç, 啊. Compatible with ASCII and UTF-8 encodings.
TEXTURE_PATH information can be provided directly after the character, skipping below properties.
The following are two special character names:
- UNKNOWN
- Used to represent characters that are not in the font
- CURSOR
- Used to represent the text cursor
Both UNKNOWN and CURSOR are optional, if they're not provided they'll be represented as whitespace.
Whitespace characters space, tab, newline are inserted to all fonts by default and don't have to be explicitly provided. Tabs have tabstop behavior and a maximum length of 8 spaces.
TEXTURE_PATH
<PATH> or WHITESPACE
Path to the image containing the appearance of the font character.
Optional; if unprovided character is assumed to be whitespace.
SIZE
( <WIDHT> x <HEIGHT> )Size of the font character. Width and height must be integers in scaled pixel units.
Optional; if TEXTURE_PATH is provided will default to the detected dimensions of the given image. If not will default to DEFAULT_SIZE.
OFFSET
( <X> , <Y> )Offset of the font character. X and Y must be integers in scaled pixel units.
Optional; will default to zero.
Offset is used during rendering to adjust character position, can be negative or positive. Offsets do not interfere with the "normal flow" of text rendering, meaning that they have no effect on the positioning of other characters.
Examples
Here's how the font C64 is defined in common/fonts.txt, abbreviated for the sake of the example:
FONT OBJECTS [C64] DEFAULT_SIZE: (8x8) CHARACTERS: // Uppercase characters A: textures/font/c64/uppercase/A.png B: textures/font/c64/uppercase/B.png // ... Z: textures/font/c64/uppercase/Z.png // Lowercase characters a: textures/font/c64/lowercase/a.png // ... // Numbers 0: textures/font/c64/numbers/0.png // ... // Punctuation &: textures/font/c64/punct/ampersand.png =: textures/font/c64/punct/equal.png !: textures/font/c64/punct/exclamation.png _: textures/font/c64/punct/underscore.png :; textures/font/c64/punct/colon.png // ... // Special characters CURSOR: textures/font/c64/punct/underscore.png OFFSET: (0,1) UNKNOWN: textures/font/c64/unknown.png
This is pretty straightforward, but there are three points of note:
The CURSOR character uses the same texture as the underscore, but offsets it by 1 pixel vertically to make it appear slightly below other characters.
The : character (last punctuation character included in the example) utilizes the semicolon fallback.
Instead of putting all textures in the same folder, they're sorted into subfolders such as uppercase and lowercase. This is not just for the sake of organization - Windows has trouble distinguishing path names that only differ due to capitalization, e.g. A.png and a.png, so it's necessary to put such files into different folders for them to be accurately accessed by the engine.
Menu Objects
Menu objects represent menus, which provide a generic way to display information and provide user interactivity. Menus can be fullscreen or windowed, interactive or non-interactive, and their appearance can be customized through their APPEARANCE and CANVAS settings.
User interaction in menus happens primarily through mints, which represent menu items such as buttons and text boxes. Menus can also contain keymaps that associate key presses with scripts.
Scripting
Menus are launched via the LaunchMenu function, which takes a menu object and creates a menu instance. This menu instance can then be accessed through the variable named self in the menu's scripting context.
Syntax
POSITION
Position of the menu. X and Y must be viewport lengths.
Providing POSITION and DIMENSIONS information to a menu makes it windowed. Providing one makes the other a requirement, and providing neither will make the menu fullscreen, which is the default. If the menu is explicitly set to be fullscreen (via the IS_FULLSCREEN property), providing POSITION or DIMENSIONS will result in an error.
DIMENSIONS
Dimensions of the menu. WIDHT and HEIGHT must be viewport lengths.
Providing POSITION and DIMENSIONS information to a menu makes it windowed. Providing one makes the other a requirement, and providing neither will make the menu fullscreen, which is the default. If the menu is explicitly set to be fullscreen (via the IS_FULLSCREEN property), providing POSITION or DIMENSIONS will result in an error.
IS_FULLSCREEN
Makes the menu fullscreen.
Menus with the IS_FULLSCREEN property should not be provided POSITION or DIMENSIONS information.
If a menu is not provided any POSITION, DIMENSIONS or IS_FULLSCREEN information it'll default to fullscreen.
IS_OVERLAY
Makes the menu an overlay menu, so that it will not stop the input capture, evaluation and rendering of the menu, game or stage that exists before it.
For a more detailed explanation see window stack.
Overlay menus can have keymaps, but are otherwise uninteractable, meaning they have the following restrictions compared to regular menus:
their button and input mints are not interactable, and will behave as labels
they do not use the default menu controls (arrow keys, enter, ESC)
HIDES_WHEN_INACTIVE
Makes the menu hide when it is inactive.
A menu is inactive when there is a window newer than it on the window stack that prevents the user from interacting with it (i.e. anything that's not an overlay menu). Inactive windows are usually not rendered, but if the newer window is a non-fullscreen menu, they will still render. Normally this is intended behavior, but if the newer menu uses a transparent background, it may make sense to hide the older menu while the newer menu exists to improve legibility. This option enables the hiding behavior.
APPEARANCE
See menu appearance.
Optinal; missing appearance information will be inherited from MAIN_MENU.
CANVAS
See canvas parsing.
Optional.
MINTS
Mints in the menu, optional.
<NAME>
Name of a mint in the menu. See mints.
SCRIPTS
Scripts belonging to the menu, optional. See script parsing.
These scripts will be evaluated in menu instance contexts.
INIT
Evaluated after the menu is initialized, and the menu's arguments have been loaded into its environment.
ACTIVE
Evaluated each frame the menu is active.
RETURN
Evaluated when the menu is returned to via a CloseUntil call.
EXIT
Evaluated when the menu is closed.
KEYMAP
See keymap parsing.
Optional; all menus share the following basic controls:
- Arrow Keys
- Change selected mint
- Esc
- Close menu
Moreover mints can capture input depending on their type as well.
Keymap bindings will capture input events before default controls, meaning that in case of overlap the default controls will be overshadowed.
Scripts associated with menu keymaps will be evaluated in menu instance contexts.
CAMERA
Camera information for the menu, optional.
All menus have their own cameras; the camera can either be attached to the menu, in which case it will automatically track the center of its active mint, or it can be detached and set to point to a specific coordinate. See camera instances for details.
Menu cameras start as attached to their menu by default, but unless the camera is provided custom bounds they will appear as static.
NAME
<CAMERA NAME>Name of the camera object to be used by the menu.
Optional; if unprovided will default to DEFAULT_CAMERA.
BOUNDS
See rectangle parsing.
Movement bounds for the menu's camera. Optional; will default to menu dimensions.
Camera bound rectangles tolerate unbound rectangles and omitted values. Omitted values will default to appropriate menu dimensions. Given values must be viewport lengths.
Mints
Mints are things in menus that the user can interact with. There are three types of mints:
Label mints, which only display text.
Button mints, which can be pressed.
Input mints, which can capture text input.
Scripting
When a menu instance is created, its mints are initialized as Lua variables and stored in the menuInstance.mints table. See mint scripting for the scripting interface of mints.
New mints can also be created at runtime independent of menus through the CreateMint function.
Syntax
POSITION
DIMENSIONS
APPEARANCE
See mint appearance.
Optional; missing appearance information will be inherited from the parent menu.
TEXT
<TEXT CONTENT>Text to be rendered by the mint, optional.
TYPE
LABEL/BUTTON/INPUTType of the mint, required.
- Labels
- Only serve to display text; cannot be selected or pressed.
- Buttons
- Can be selected and pressed.
- Input
- Can be selected, pressed, and can capture text input.
Pressing happens by clicking on a mint, or pressing enter when it's selected.
While an input mints is selected, all keymap bindings are disabled temporarily for clarity, and the following additional controls are defined:
- BACKSPACE
- Remove character before cursor
- DELETE
- Remove character at cursor
- SHIFT + ENTER
- Enter newline
- SHIFT + ARROW KEYS
- Move cursor
SCRIPTS
Scripts belonging to the mint, optional. See script parsing.
These scripts will be evaluated in menu instance contexts.
PRESS
Evaluated when the mint is pressed. A press can be either an enter keypress or a right click.
SELECTION
Evaluated when the mint is selected.
DESELECTION
Evaluated when the mint is deselected.
ACTIVE
Evaluated each frame the mint is available and the menu it's in is active.
Examples
TODO: Insert examples
Appearance
Below properties can be provided in the APPEARANCE settings of menus to customize their appearance. They can also be used in mints, but with one distinction; only the appearance settings concerning mints (which are listed after the MINTS property below) can be provided to mints. For example:
[MY_MENU] APPEARANCE: MINTS: ACTIVE_COLOR: WHITE
This makes the mints in MY_MENU use WHITE as their color when active. To achieve this using the appearance setting of a mint, we would do:
[MY_MENU] MINTS: MY_MINT: APPEARANCE: ACTIVE_COLOR: WHITE
Such appearance settings that are directly provided to mints have precedence over the appearance settings of menus.
If a menu is missing any of its appearance information, it is inherited from the MAIN_MENU. The required/optional behavior of properties stated below is only applicable after inheritance - for example FONT is required, but if MAIN_MENU includes a FONT in its appearance settings (and it does by default) you don't have to specify it for your menus, as they'll inherit the information.
Syntax
BACKGROUND_COLOR
MINTS
Appearance settings for the mints in the menu.
The settings listed here can also be provided in the APPEARANCE settings of mints directly.
ACTIVE_COLOR
INACTIVE_COLOR
TEXT
Appearance settings for text to be rendered by mints.
ACTIVE_COLOR
INACTIVE_COLOR
FONT
<FONT OBJECT NAME>Name of the font object to be used by the text.
Required if the relevant mint has text to be rendered.
POSITION
( <X> , <Y> )Position of text within the mint. X and Y must be viewport lengths. Viewport lengths in this context have scaled pixel resolution.
Optional; will default to zero.
If provided alongside ALIGNMENT, positional adjustment will happen after alignment.
ALIGNMENT
LEFT/RIGHT/CENTEROptional, will default to LEFT.
Examples
TODO: Insert examples
Camera Objects
Camera objects define how screens track their targets. They do this using the following concepts:
- Easers
- Which determine how an initial value approaches a target value.
- Definitions
- Rules about where the camera seeks to place its target on the screen.
- Trackers
- Collections of definitions.
Scripting
Camera objects are used to generate camera instances - which are used by menu instances, game screens and stages. Camera instances behave according to the rules of the camera object associated with them, and they also have a scripting interface capable of operations such as changing the target they're tracking, or being moved to specific positions. See camera instances for details.
Syntax
RESOLUTION
The smallest distance which the camera is capable of moving. Screen pixel resolution results in smoother motion, while scaled pixel resolution gives a more consistent pixelated appearance.
For more information on screen & scaled pixels see lengths.
Optional; will default to SCREEN_PIXELS.
DEFINITION_EASING
POSITION_EASING
Easer to be used when the target of the camera is out of focus according to the rules of the camera's tracker, see easers.
Required.
POINT_TRACKER
Conditions are optional, see conditions for details. X, Y must be viewport lengths. Viewport lengths in this context use relative slices.
Point trackers seek to keep their target on a single point on the screen. For example, a point tracker with a definition of ( 180 SLICES, 180 SLICES ) will seek to keep its target in the center of the screen. When the target moves, the camera will follow it according to the easer given in the POSITION_EASING property.
Point trackers can have multiple definitions, e.g.
[EXAMPLE_CAMERA] POINT_TRACKER: IF ( FACING_RIGHT ) ( 120 SLICES, 180 SLICES ) IF ( FACING_LEFT ) ( 240 SLICES, 180 SLICES ) ( 180 SLICES, 180 SLICES )
In each frame the conditions associated with these definitions will be sequentially tested, and the first one that holds will be used. Omitting the condition will make the definition always applicable.
When the active definition changes from one frame to another, the easer given in the DEFINITION_EASING property will be used.
REGION_TRACKER
Region trackers keep their target within the bounds of a region on the screen.
TODO: To be implemented
Easers
Easers determine how an initial value approaches a target value. There are three types of easers: INSTANT, CONSTANT and BERNSTEIN:
INSTANT
Instant easers go from initial value to target value instantly.
CONSTANT
<VALUE>Value is expected to be an integer in range [1,100].
Constant easers go from initial value to target value by the given constant amount each frame. For example a constant easer with value 25 would go from 0 to 100 as 0 -> 25 -> 50 -> 75 -> 100
BERNSTEIN
Bernstein easers go from an initial value to a target value using Bernstein polynomials, which makes it possible to create easers of arbitrary complexity. Such easers can display ease-in, ease-out, ease-in-out behavior and more.
FORMULA
(<STEP-VAL> [(<COEFF-VAL-1> <COEFF-VAL-2> ...)])Given values are expected to be floating point numbers.
A step value is mandatory and has range (0,1]. The easing is done by incrementing from 0 to 1 by step value each frame, so it determines the duration (e.g. with a step value of 0.1, the easing process takes 10 frames to complete). Coefficient values can be omitted entirely.
If there are n coefficients, a Bernstein polynomial that is a combination of n + 2 basis polynomials with degree n + 1 will be generated. The basis polynomials and will have coefficients 0 and 1. These only exist to guarantee that the easing process begins from source, and ends at destination. For more information on Bernstein polynomials see wikipedia.
E.g. for a formula of (0.1 (0.5)), the coefficient list is (0.5), and the resulting Bernstein polynomial is
The FORMULA value can be provided directly after BERNSTEIN, e.g.
[MY_CAMERA_OBJECT] POSITION_EASING: BERNSTEIN: FORMULA: (0.1 (0 1))
is equivalent to
[MY_CAMERA_OBJECT] POSITION_EASING: BERNSTEIN: (0.1 (0 1))
TIME_REVERSAL
YES/NO/TRUE/FALSEOptional; will default to false.
Easing processes have an initial and target value. If the target value changes while the process is ongoing, the process is normally interrupted and restarted with the new value. But if this option is set to true, such an interruption will result in the process reversing rather than restarting IF the new target value equals the initial value with which the process started.
This can be useful in places like cameras with point trackers that have two definitions and have an ease in-out formula like (0.1 (0 1)).
Examples
TODO: Insert examples
Keymap Objects
Keymap objects bind input signals such as keys and mouse events to game buttons and scripts. They're used to specify the controls of players and menus.
Unlike any other element type keymap objects can be inlined, meaning that they can be provided as part of another element instead of a standalone one. (See keymap inlining)
Scripting
Keymaps have a highly flexible scripting interface - everything about a keymap can be redefined at runtime. See keymap scripting for details.
Syntax
<KEYMAP NAME>
Name of a keymap object to inherit bindings from.
This feature is only available to inlined keymaps (see inlining). Keymaps defined as standalone elements cannot inherit.
Inheriting from multiple keymaps is not possible, and inheritance must happen before the registration of any bindings.
If any bindings provided to a keymap conflict with bindings that have been inherited, directly provided bindings will have precedence.
<INPUT SIGNAL>
See input signals.
An input signal can only be assigned to one thing, whether it's a script or a game button.
<SCRIPT>
See scripts.
If the input signal is a mouse event, the x and y positions of the mouse are passed to the script as arguments. The passed positions are in scaled pixels, and with respect to the upper left position of the context that has evaluated the script. (See keymap examples/mouse event)
The context in which these scripts are evaluated depends on the keymap's role: e.g. keymaps that belong to menus will be evaluated in menu instance contexts, while keymaps that belong to players will be evaluated in player contexts.
<GAMEBUTTON>
See game buttons.
Multiple input signals can not be assigned to the same game button.
Not all game buttons can be used here; the FORWARD and BACKWARD buttons are considered "virtual" (they don't just reflect key presses, but also depend on the direction of the controlled instance), thus are not allowed to be inserted into keymaps.
Input Signals
Input signals represent various types of input that the user can provide. They are divided into two main categories: keys and mouse events.
Keys
Number Keys
0 1 2 3 4 5 6 7 8 9 Alphabetical Keys
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Arrow Keys
LEFT RIGHT UP DOWN Other
ENTER SPACE BACKSPACE DELETE HOME END ESCAPE LCTRL RCTRL LALT RALT LSHIFT RSHIFT TAB LCTRL / RCTRL, LALT / RALT, LSHIFT / RSHIFT correspond to the left/right versions of the keys on the keyboard.
Mouse Events
MOTION | LPRESS | LRELEASE | RPRESS | RRELEASE |
Which correspond to the following:
- MOTION
- Mouse motion
- LPRESS
- Left mouse button press
- LRELEASE
- Left mouse button release
- RPRESS
- Right mouse button press
- RRELEASE
- Right mouse button release
Inlining
Keymap objects are capable of being inlined - they can be provided as part of other elements as well as on their own. For example:
[MY_KEYMAP] KEYMAP OBJECT X: Foo() [MY_MENU] MENU OBJECT KEYMAP: MY_KEYMAP
is equivalent to:
[MY_MENU] MENU OBJECT KEYMAP: // Inlined keymap X: Foo()
As stated in the keymap syntax section, inlined keymaps differ from standalone keymaps in that they're capable of inheritance. For example:
[MY_KEYMAP] KEYMAP OBJECT X: Foo() [MY_MENU] MENU OBJECT KEYMAP: MY_KEYMAP Y: Bar()
In this example the keymap of MY_MENU will have all bindings of MY_KEYMAP, as well as the bindings defined in its own body.
Examples
TODO: Insert examples
Mouse Event
Actor Objects
Actor objects define the types of characters in the game world. They describe what characters look like, how they move in the game world, and how they can be controlled. To do this they contain sprites, animations, actions, and controls that can be associated with those actions.
Note that actor objects represent the types of characters and not characters themselves - to place specific characters in the game world use actor instances.
Syntax
SPRITES
Sprites belonging to the actor object.
Actor objects can contain any number of sprites, which are in turn used to make up their animation sequences.
<SPRITE_NAME>
See sprite parsing.
ANIMATION_SEQUENCES
Animations belonging to the actor object.
Actor objects can contain any number of animations, which are in turn used by actions to determine what characters look like as they move in the game world.
<ANIMATION_NAME>
See sequence parsing.
Sequences should be made of sprites.
ACTIONS
Actions belonging to the actor object.
Actor objects can contain any number of actions, and there are no restrictions on the types of actions they may contain.
<ACTION_NAME>
See action parsing.
CONTROLS
Controls of the actor object.
Optional; no controls being provided for an actor is valid.
<CONTROL_NAME>
The name of the control.
INPUT
<GAMEBUTTON(S)>See game button parsing.
Can be a single button, or a combination of buttons composed through the operators + and >.
Buttons combined with + need to be pressed in the same frame. Buttons combined with > need to be pressed consecutively, within 10 frames of each other.
ACTION
<ACTION_NAME>The name of the action to start.
This shouldn't be a response or an idle action, but currently there are no checks.
SET_DIRECTION
LEFT/RIGHT/UP/DOWN/FRONT/BACK/HORIZONTAL/VERTICALSets the new direction of the actor instance. Optional.
AI
TODO: To be implemented
Examples
TODO: Insert examples
Actor Instances
Actor instances are individual characters in the game world. They specify which actor object they're an instance of, and where the character is located in the game world.
Scripting
The scripting interface of actor instances is currently under development. The features available at the moment are their scripting contexts, and the SearchWorld function which can look up actor instances in the game world. See actor instance scripting for details.
Syntax
INSTANCE_OF
Name of the actor object which the actor instance is an instance of.
Required, must be the first property registered.
DIRECTION
Direction of the actor instance.
Optional, will default to RIGHT.
IN_MAP
Name of the map the actor is in. Required.
POSITION
Position of the actor instance. Given values must be in scaled pixel units. Required.
VELOCITY
Velocity of the actor instance. Given values must be integers in subpixel units.
Optional; will default to zero.
The effect of velocity on position is as follows: each frame (60 times a second) the velocity value is added to the position value. So for example:
A velocity value of (1, 0) adds 1 subpixel to the X position each frame, and in one second (60 frames) a total of 60 subpixels (equal to 1 scaled pixel as described in the units section) will be added, causing the instance to move one pixel to the right each second.
A velocity value of (60, 0) adds 60 subpixels to the X position each frame, causing the instance to move one pixel each frame and 60 pixels each second.
So the velocity value can both be seen as subpixels per frame and pixels per second.
ACCELERATION
Acceleration of the actor instance. Given values must integers be in subpixel units.
Optional; will default to zero.
Acceleration's effect on velocity is the same as velocity's effect on position: each frame the acceleration value is added to the velocity value. So for example an acceleration value of (1, 0) adds 1 subpixel to the horizontal velocity each frame, and in 60 frames the instance will go from stationary to moving at a velocity of (60, 0).
Examples
TODO: Insert examples
Common Syntax
This section lists syntax rules for components that are used in multiple elements.
Colors
Colors can be specified in two ways: RGB values and color names.
RGB Values
As RGB values, colors can be expressed using the following syntax:
(<R>, <G>, <B> [, <A>])R, G, B, A are expected to be integer values within range [0,255]. A is optional, if unprovided will default to 255, which corresponds to zero transparency. For more information about the RGBA model see wikipedia.
In certain contexts providing an alpha value is not allowed. These contexts (such as the BACKGROUND_COLOR setting of map objects) will explicitly state this restriction in their documentation.
Color Names
The following color names can be provided to the engine in places where a color value is expected:
Name | RGB Value | Color |
---|---|---|
BLACK | (0,0,0) | |
WHITE | (255,255,255) | |
RED | (255,0,0) | |
GREEN | (0,255,0) | |
BLUE | (0,0,255) |
Lengths
Length Units
Monospace Engine uses the following units to describe lengths:
Screen Pixels
Length of one pixel on the display device being used.
Scaled Pixels
Length of one screen pixel times the scale multiplier.
Scaled pixels are screen pixels "scaled up" - they're the larger pixels that you see in pixel art, and they provide the engine with a consistent pixelated appearance at higher resolutions.
Tiles
One tile is equivalent to a length of 16 scaled pixels.
Tiles are typically used to describe the geometry of map objects.
Subpixels
One subpixel is equal to 1/60th of a scaled pixel.
Subpixels are used to describe values that relate to the motion of instances in game worlds, such as velocity and acceleration.
Screen Slices
One screen slice is equivalent to 1/360th of the engine window's length.
Screen slices are useful in positioning UI elements such as menus and mints - they make it possible to describe lengths such as middle of the screen (180 SLICES) or bottom of the screen (360 SLICES) in a way that's independent of the actual size of the engine window.
Unlike other length units, the length of a screen slice depends on the direction in which it's used: if used in a horizontal context (e.g. as a WIDTH value) one slice will equal 1/360 of the window's width, and if used in a vertical context (e.g. HEIGHT) it'll refer to 1/360 of the window's height.
Viewport Lengths
Viewport lengths are lengths composed using the units listed above. The basic syntax for them is as follows:
[<SIGN>] <MAGNITUDE> <UNIT>Where:
Sign can be + or -. It is optional, and if unprovided assumed to be +.
Magnitude must be an integer.
Unit must be one of the following:
SCREEN_PIXELS/SCREEN_PIXEL for screen pixels
SCALED_PIXELS/SCALED_PIXEL/PIXELS/PIXEL for scaled pixels
SCREEN_SLICES/SCREEN_SLICE/SLICES/SLICE for screen slices
TILES/TILE for tiles
Combinations of viewport lengths are also valid viewport lengths, e.g. 360 SLICES - 10 PIXELS.
If a viewport length is stated to have scaled pixel resolution, it means that this context is not sensitive to lengths smaller than scaled pixels, and will round them to the nearest scaled pixel value.
If a viewport length is stated to use relative slices, it means that the lengths of screen slices in this context are calculated using the active rendering area and not the whole window.
Rectangles
x1, y1 correspond to the position of the upper left edge of the rectangle, while x2, y2 correspond to the lower right.
Depending on the context, the INF keyword can be used in descriptions to create rectangles that extend to infinity, called unbound rectangles. INF can also have a sign, such as -INF, +INF. For example ((-INF, 2), (+INF, 5)) represents a rectangle that is horizontally infinite, but vertically restricted between y = 2 and y = 5 lines.
Again depending on context, values being omitted in rectangle descriptions may be tolerable. These will default to context-specific values. As an example ((,),(+INF,)) when used as camera bounds in a map will yield a camera that is bound by the edges of the map in all directions except right.
Canvases
Canvases are visual environments on which tiles can be placed. They're used to describe the appearances of map and menu objects, and in case of map objects they also determine the physical environments of maps.
For the scripting capabilities of canvases see canvas scripting.
Syntax
TILES
List of tiles within the canvas.
<TILE NAME>
Name of a tile to be placed on the canvas. Has the following form:
<TILESET_OBJECT_NAME> [<TILE_NAME>] [<TRANSFORMATION>]Where:
TILESET_OBJECT_NAME is the name of a tileset object,
TILE_NAME is optional, and is the name of a tile within the tileset,
TRANSFORMATION is optional, and is the name of a transform to be applied to the tile (see transformations)
For example, with MY_TILESET being the name of a tileset that contains the tiles TILE_1 and TILE_2 in it (see tileset examples) the following are some valid tile names:
MY_TILESET TILE_1
MY_TILESET TILE_2
MY_TILESET TILE_2 VERTICAL_FLIP
If the tileset has a default tile, it can be referenced both as MY_TILESET and as MY_TILESET DEFAULT. Similar to named tiles, transformations can be applied to the default tile too, e.g. MY_TILESET VERTICAL_FLIP.
<PLACEMENTS>
( <X>, <Y>, <Z> )Where the tile will be placed on the canvas.
For map canvases X, Y must be integers in tile units, and for menu canvases they must be viewport lengths with scaled pixel resolution. Z must be an integer representing a parallax level in both cases.
Multiple placements can be provided on the same line, and any text outside parentheses will be ignored. For example in maps (0,0,0) hello (1,0,0) is valid placement information.
PARALLAX
List of parallax levels of the canvas.
Parallax levels are like the "layers" of the canvas - each parallax level has a Z value and a parallax speed associated with it. Z values determine the parallax levels' order of appearance, and parallax speeds determine how fast the levels move as the camera scrolls.
Parallax levels are optional for menu canvases - they can tolerate placements with Z values that are not associated with any known parallax level, and will assign them a default parallax speed of 100. Map canvases are more rigid and expect all Z values used in placements to correspond to a known parallax level.
<Z VALUE>
Z value of the parallax level. Must be an integer.
Parallax levels with higher Z values are rendered in the background compared to levels with lower Z values. Things that are not part of canvases but are rendered alongside them will render as if they are between Z levels 0 and -1. This means that tiles with negative Z levels can obscure players and actor instances in maps, and mints in menus.
<PARALLAX SPEED>
Parallax speed of the level. Must be an integer.
Parallax speeds determine how fast levels move as the camera scrolls, and the base parallax speed is 100. For example in games a parallax level with a speed of 100 will move as fast as the player, 50 will move half as fast, and 0 will be completely stationary. Higher speed values are also possible, e.g. a parallax level with 200 will move twice as fast as the player.
Examples
TODO: Insert examples
Transformations
Transformations are visual operations that can be applied to textures. Currently they're only used in tile names.
Available transformations are the following:
NONE
VERTICAL_FLIP / VF
HORIZONTAL_FLIP / HF
QUARTER_ROTATION / QR
HALF_ROTATION / HR
THREE_QUARTERS_ROTATION / TQR
Rotations are done clockwise.
Conditions
Game Buttons
Sprites
Sprites define how actor instances look and interact with the world at a given moment.
PATH
Path to the image containing the sprite's appearance, required.
If a property given to a sprite does not match any of the properties listed here, it's interpreted as a PATH, e.g. the following are equivalent:
[MY_ACTOR] SPRITES: TEST_SPRITE: PATH: path-to-sprite.png
[MY_ACTOR] SPRITES: TEST_SPRITE: path-to-sprite.png
DIMENSIONS
Dimensions of the sprite. Width and height must be integers in scaled pixel units.
Optional, if unprovided will default to the dimensions of the image given in PATH.
DIRECTION
Direction in which the image of the sprite is facing by default.
Used to apply transforms as necessary, e.g. a sprite with DIRECTION set to RIGHT will be horizontally flipped when it's being used to render an instance facing left.
Optional, will default to RIGHT.
CENTER
Center of the sprite.
Center is kept consistent from frame to frame, which allows the engine to keep the positions of instances consistent when they shape change and size during animations. You can think of it as the "heart" of the sprite.
Optional, will default to the center of the sprite's dimensions.
HITBOX
See rectangle parsing.
Hitbox of the sprite. The instance will take damage if its hitbox comes into contact with another instance's hurtbox.
Optional, none or multiple can be provided.
HURTBOX
See rectangle parsing.
Hurtbox of the sprite. The instance will inflict damage if its hurtbox comes into contact with another instance's hitbox.
Optional, none or multiple can be provided.
COLLISIONBOX
See rectangle parsing.
Collisionbox of the sprite. Used in interactions with map geometry, and represents the "solid" part of the sprite that can't go through tiles.
Optional, and unlike HITBOX and HURTBOX at most one COLLISIONBOX is allowed per sprite.
Sequences
Actions
Actions are things that actor instances can do. Actor instances always have an active action - whether it's one initiated at will (either by the player or through the AI), one that happens in response to something (like being hit), or just an idle action.
Syntax
TYPE
RESPONSE/IDLE/MOVEMENT/ATTACK/DEFENDType of the action. Determines whether the action can be started at will; RESPONSE and IDLE can't, but the others can.
When an actor instance has no active action, the first IDLE action whose REQUIREMENTS property is satisfied is activated. Finding no suitable idle action will result in an error, so all actor objects must include at least one idle action.
ANIMATION
See sequence parsing.
Sequences should be made of animations.
MOTION
INIT/DURING/END
<MOTION MODIFIER>
[CONST/MAX/MIN/INCREMENT/DECREMENT] HORIZONTAL/VERTICAL POSITION/VELOCITY/SPEED/ACCELERATION [BY] <VALUE> [IF <CONDITION>]The first token is optional; will default to CONST if unprovided.
[BY] is optional syntactic sugar for INCREMENT, DECREMENT cases, and is not expected for the other cases.
If the motion modifier describes an effect on position, the effect types MAX/MIN/CONST are considered invalid.
If the motion modifier is horizontal, the effect is "mirrored" when the corresponding instance is facing left, so that positive modifier values always correspond to movement towards the direction the instance is facing horizontally. No such adjustment is made for vertical modifiers.
Value is in subpixel units.
Optional.
REQUIREMENTS/TRIGGERS
<CONDITION>See conditions.
If type is response a condition is required.
If type is not response this is optional; will default to TRUE so that the action will always be available.
DURATION
INDEFINITE/ANIMATION_LENGTH/<VALUE>Value refers to the number of frames the action will be active.
Optional; will default to indefinite.
INTERRUPTION
INTERRUPTIBLE_AT_WILL
YES/NO/TRUE/FALSEOptional; if not a response will default to yes, if a response will default to no.
INTERRUPTIBLE_BY_RESPONSES
YES/NO/TRUE/FALSEOptional; if not a response will default to yes, if a response will default to no.
CONDITION
<CONDITION>Optional; will default to false so that no extra interruption condition will exist.
Examples
TODO: Insert examples
Action Stacking
Actions can be stacked, meaning that multiple actions can be active at the same time. This is only possible with actions that are interruptible at will. At most five actions can be active at the same time, activation of any new actions will cause older ones to be shifted off, their ending consequences will not register.
With such stacked actions, the consequences of the latter ones are emphasized, but the consequences of earlier ones will also apply as long as they're not overshadowed by the more recent actions.
Appearance is determined by the most recent action only.
If an action is interruptible by responses, responses only kick in if it's the most recent action, and all stacked actions will be removed in such an event.