Theming
A game's behaviour is its configuration and its look is its theme. Both are plain data, set the same way from every surface:
| Surface | Configuration | Theme |
|---|---|---|
| Script | define config.text_speed = 60 |
define theme.text_size = 40 |
| C# | game.SetConfig("text_speed", "60") |
game.SetTheme("text_size", "40") |
| Divooka, procedural | Set Config | Set Theme |
| Divooka, functional | With Config | With Theme |
From C# and nodes, a value is written as a script would write it: a number, true or false, a list in brackets ([40, 40, 40, 40]), or text. A setting made from code (SetConfig, SetTheme, SetResolution and SetTitle, or the Set Config, Set Theme, With Config and With Theme nodes) holds over a script's define config. or define theme. of the same key, whichever comes first, so a program can load a game's scripts and then change one of their settings. The title and resolution given to the constructor are only defaults, which a script's define replaces. The lint warns about any key Novella does not know.
Everything in the theme is optional. Without images, the engine draws its dialogue window, name tab, choices and dialogs as panels in the theme's colours, which is what a game looks like before it has art.
Configuration
| Key | Default | Meaning |
|---|---|---|
title |
Novella Game |
The game's name: the window title and the main menu heading. |
version |
1.0 |
The version, shown on the main menu and in About, and stored in saves. |
developer |
Who made the game, shown in About. | |
about |
Text for the About screen. | |
id |
A short identifier for the save folder; empty uses the title with spaces and punctuation removed. | |
icon |
An image file for the window icon. | |
width, height |
1280, 720 |
The resolution everything is laid out at; the window scales it. |
start_label |
start |
The label Start runs. |
splash_label |
splashscreen |
A label run once at launch, before the main menu, if it exists. |
main_menu_label |
main_menu |
A label run instead of the built-in main menu, if it exists; when it returns, a new game starts. |
after_load_label |
after_load |
A label called after a save is loaded, if it exists. |
fall_through |
true |
Whether reaching the end of a label continues into the next label in the same file, rather than ending the game. |
rollback_limit |
100 |
How many steps the player can roll back. |
history_limit |
250 |
How many entries the history keeps. |
allow_rollback |
true |
Whether the player can roll back at all. |
rollback_past_choices |
true |
Whether rolling back may undo a menu choice and let the player choose again. |
text_speed |
40 |
The default typing speed, in characters per second; 0 shows text at once. |
auto_forward_delay |
2.0 |
The default wait, in seconds, after a line finishes before auto-forward advances (plus a little per character). |
skip_unseen |
false |
The default of the Unseen Text skip preference. |
skip_after_choices |
false |
The default of the After Choices skip preference. |
fullscreen |
false |
Whether a fresh install starts fullscreen. |
window |
auto |
The dialogue window's default visibility: auto (shown for dialogue, hidden otherwise), show or hide. |
auto_voice |
A voice file pattern such as voice/{id}.ogg; a line plays the file named by its id (or {character}, the speaker's voice tag) when it exists. |
|
main_menu_music |
Music played on the main menu. | |
start_transition |
fade |
The transition from the main menu into the game. |
after_load_transition |
dissolve |
The transition after loading a save. |
end_game_transition |
fade |
The transition back to the main menu when a game ends. |
original_language |
English |
The name of the script's own language, as the language preference lists it. |
default_language |
The language a fresh install starts in; empty for the original. | |
autosave |
true |
Whether the game saves to the autosave slots at menus and on quitting. |
autosave_slots |
6 |
How many autosave slots rotate. |
save_pages |
6 |
How many pages of save slots there are. |
slots_per_page |
6 |
How many save slots a page holds. |
show_quick_menu |
true |
Whether the quick menu (Back, History, Skip, Auto, Save, Q.Save, Q.Load, Prefs) shows under the dialogue window. |
confirm_quit |
true |
Whether quitting asks first. |
show_title_on_main_menu |
true |
Whether the main menu draws the title; turn it off when the background art carries it. |
developer_mode |
false |
The developer tools: Shift+O opens the console and Shift+R reloads the scripts. |
text_speed, auto_forward_delay, skip_unseen, skip_after_choices, fullscreen and default_language are the defaults of the player's preferences; once a player changes a preference, their choice is kept in the persistent data and wins.
Theme
Pixel sizes are written for a game 1080 pixels tall and are scaled by the game's height (or by scale, when it is set), so one theme fits every resolution. Colours are #rgb, #rrggbb or #rrggbbaa. Insets, margins and paddings are four numbers (left, top, right, bottom), two numbers (horizontal, vertical) or one number for all four sides.
Scale and fonts
| Key | Default | Meaning |
|---|---|---|
scale |
0 |
A multiplier for every pixel size; 0 scales by the game's height relative to 1080. |
font_files |
[] |
Font files among the assets, registered before the game starts so the families below can name them. |
text_font |
Font families for dialogue; empty uses the default stack. | |
name_font |
Font families for speaker names; empty uses interface_font. |
|
interface_font |
Font families for menus and buttons. | |
title_font |
Font families for the main menu title and screen headings; empty uses interface_font. |
|
text_size |
34 |
Dialogue text size. |
name_size |
36 |
Speaker name size. |
interface_size |
30 |
Menu and button text size. |
small_size |
22 |
Small text: the quick menu, slot details, notes. |
heading_size |
48 |
Screen headings (Save, Load, Preferences). |
title_size |
96 |
The main menu title. |
line_spacing |
1.25 |
Dialogue line height as a multiple of the font size. |
ruby_scale |
0.5 |
Ruby text size relative to its base. |
Colours
| Key | Default | Meaning |
|---|---|---|
text_color |
#f7f2e8 |
Dialogue. |
name_color |
#f2c879 |
Speaker names, for characters without their own colour. |
accent_color |
#d9a441 |
Selected items, slider fills, borders. |
interface_text_color |
#ebe4d6 |
Menu text. |
idle_color |
#d8cfbf |
Button text when idle. |
hover_color |
#ffd98a |
Button text under the pointer. |
selected_color |
#ffffff |
Button text when selected. |
insensitive_color |
#7d7568 |
Disabled button text. |
muted_color |
#a59c8c |
Secondary text. |
background_color |
#16121a |
Behind menus that have no background image. |
frame_color |
#120e14e8 |
Panels: slots, preference groups, dialogs. |
frame_border_color |
#d9a44166 |
Panel borders. |
text_outline_width |
0 |
An outline around dialogue text, in pixels. |
text_outline_color |
#000000 |
The dialogue outline's colour. |
text_shadow |
true |
Whether dialogue casts a soft shadow, which keeps it legible over any background. |
text_shadow_color |
#000000b0 |
The shadow's colour. |
The dialogue window
| Key | Default | Meaning |
|---|---|---|
textbox_image |
An image for the dialogue window, stretched as a nine-patch; empty draws a panel. | |
textbox_borders |
[40, 40, 40, 40] |
The image's nine-patch insets, in image pixels. |
textbox_color |
#0f0b12d0 |
The drawn panel's fill. |
textbox_border_color |
#d9a44188 |
The drawn panel's border. |
textbox_corner_radius |
14 |
The drawn panel's corner radius. |
textbox_height |
270 |
The window's height. |
textbox_margin |
[60, 0, 60, 30] |
Space between the window and the screen's edges. |
textbox_padding |
[70, 62, 70, 30] |
Space between the window's edge and its text. |
namebox_image |
An image behind the speaker's name (nine-patch); empty draws a tab. | |
namebox_borders |
[20, 10, 20, 10] |
The name image's nine-patch insets. |
namebox_color |
#2a1a1ce8 |
The drawn name tab's fill. |
namebox_offset |
[50, -26] |
Where the name sits relative to the window's top-left corner. |
namebox_padding |
[26, 8, 26, 8] |
Space around the name inside its tab. |
side_image_width |
300 |
The width kept for side images inside the window. |
ctc_image |
A click-to-continue indicator; empty draws a small animated arrow. |
Choices
| Key | Default | Meaning |
|---|---|---|
choice_image |
An image behind each menu choice (nine-patch); empty draws a panel. | |
choice_hover_image |
The choice image under the pointer. | |
choice_borders |
[30, 12, 30, 12] |
The choice images' nine-patch insets. |
choice_width |
1100 |
Choice button width. |
choice_height |
76 |
Choice button height. |
choice_spacing |
20 |
Space between choices. |
choice_color |
#140f14e0 |
The drawn choice fill. |
choice_hover_color |
#4a2a1ef0 |
The drawn choice fill under the pointer. |
choice_center_y |
0.42 |
The vertical centre of the choice list, as a fraction of the screen. |
Menus
| Key | Default | Meaning |
|---|---|---|
button_image |
An image behind menu buttons (nine-patch); empty draws text-only buttons. | |
button_hover_image |
The menu button image under the pointer. | |
button_borders |
[20, 10, 20, 10] |
The button images' nine-patch insets. |
main_menu_background |
An image name or file for the main menu; empty draws a gradient. | |
game_menu_background |
An image name or file behind Save, Load, Preferences and the rest; empty dims the game. | |
main_menu_xalign |
0.08 |
Where the main menu's buttons sit horizontally. |
main_menu_yalign |
0.86 |
Where the main menu's buttons sit vertically (their bottom). |
main_menu_panel |
true |
Whether a translucent panel sits behind the main menu's buttons. |
logo_image |
An image drawn above the main menu's buttons instead of the title text. | |
frame_image |
An image behind dialogs such as confirmations, stretched as a nine-patch with the textbox's insets; empty draws a panel. | |
hover_sound |
A sound played when the pointer enters a button. | |
click_sound |
A sound played when a button is pressed. | |
notify_color |
#120e14ee |
Notification fill. |
nvl_background |
#000000b8 |
The NVL page's backdrop. |
nvl_text_size |
32 |
NVL text size. |
menu_transition_time |
0.25 |
How long menus take to fade in. |
Fonts
Font settings take a family list: names separated by commas, tried in order for each character, so a Latin face can come first and a Chinese or Japanese face fill in the characters it lacks. The engine's default stack always follows the list: Noto Sans, Segoe UI, Helvetica Neue and Arial for Latin text, then Noto Sans CJK, Source Han Sans, Microsoft YaHei, PingFang SC, Yu Gothic UI, Hiragino Sans, Malgun Gothic, SimSun and DejaVu Sans, so Chinese, Japanese and Korean text renders out of the box on a machine that has any of them.
To ship fonts with the game, put the files among its assets, list them in font_files, and name their families:
define theme.font_files = ["fonts/EBGaramond-Regular.ttf", "fonts/EBGaramond-Italic.ttf", "fonts/EBGaramond-SemiBold.ttf", "fonts/LXGWWenKaiLite-Regular.ttf", "fonts/MaShanZheng-Regular.ttf"]
define theme.text_font = "EB Garamond, LXGW WenKai Lite"
define theme.name_font = "EB Garamond, LXGW WenKai Lite"
define theme.interface_font = "EB Garamond, LXGW WenKai Lite"
define theme.title_font = "Ma Shan Zheng, LXGW WenKai Lite"
.ttf, .otf and .ttc files are recognised. Register the italic and bold files of a family too, so {i} and {b} use real faces. A character can have its own font (character q "Qinglan" font "Ma Shan Zheng"), and a run of text can switch with {font=Family}. Ruby text above CJK characters is sized by ruby_scale. Use openly licensed fonts (SIL Open Font License or Apache) and ship each font's licence with the game.
Nine-patch images
The dialogue window, the name tab, choices, menu buttons and dialogs can each be an image stretched as a nine-patch: the image is cut into a 3×3 grid by four insets, the corners are drawn at their own size, the edges stretch along their length, and the centre stretches both ways. A small image with decorated corners therefore fills any size without distorting them.
define theme.textbox_image = "gui/textbox.png"
define theme.textbox_borders = [48, 48, 48, 48]
define theme.textbox_height = 280
define theme.textbox_padding = [80, 66, 80, 34]
define theme.choice_image = "gui/choice_idle.png"
define theme.choice_hover_image = "gui/choice_hover.png"
define theme.choice_borders = [28, 28, 28, 28]
Insets are measured in the image's own pixels and are not scaled; they say where the corners end. Margins, paddings and sizes are in game pixels and are scaled with the game. To prepare an image, keep everything that must not stretch (ornaments, rounded corners) inside the corner regions, and make the edge and centre regions plain enough to stretch. Edges are stretched, not tiled.
| Image | Insets |
|---|---|
textbox_image |
textbox_borders |
namebox_image |
namebox_borders |
choice_image, choice_hover_image |
choice_borders |
button_image, button_hover_image |
button_borders |
frame_image |
textbox_borders |
Preferences
The Preferences screen offers the player: Display (Window or Fullscreen), Skip (Unseen Text, After Choices, Transitions), Language (when the story has translations), Text Speed, Auto-Forward Time, Text Size, Music Volume, Sound Volume, Voice Volume, Window Opacity and Mute All. Preferences are kept in the persistent data, never in saves.
Interface translations
The engine's own words (main menu, game menu, preferences, save slots, confirmations, notifications, the help screen) are built in for three languages beyond English:
| Language | Codes that select it | Coverage |
|---|---|---|
| Simplified Chinese | zh, zh-Hans, zh-CN and any other zh- code not listed below, schinese, chinese, simplified chinese |
Every interface word, message and help line. |
| Traditional Chinese | zh-Hant, zh-TW, zh-HK, tchinese, traditional chinese |
Menus, preferences, the quick menu, confirmations and the common notifications. |
| Japanese | ja, ja-JP and other ja codes, japanese |
Menus, preferences, the quick menu and the most common messages. |
Codes are matched without regard to case, with _ and - alike. Words without a translation show in English. The built-in words apply when the player's language is one of these, and the language picker lists the languages the story translates, so a game gets them by translating its own text into that language (or by setting config.default_language).
A story's own string translations come first, so a game can replace any engine word, in any language, including one Novella has no table for:
translate zh-Hans strings:
old "Start"
new "开篇"
old "Quit"
new "归去"
translate ko strings:
old "Start"
new "시작"
The language picker shows a language by its own name for the codes it knows (简体中文, 繁體中文, 日本語, 한국어, English, Français, Deutsch, Español, Русский) and by its code otherwise.