Scpui

From FreeSpace Wiki
Jump to: navigation, search
SCPUI-trim.png

SCPUI is a term that refers to both the LUA Script implementation and the user interface LUA API in the FreespaceOpen engine available from versions 23.2 onwards. It is intended to be used with the librocket libraries to replace the base Freespace Open interface in an easily customizable way through LUA scripts.

The original implementation of SCPUI was started by m!m and finished by MjnMixael. This specific implementation attempts to replicate the original Freespace UI experience while offering enhancements such as in-game options not normally available.

For documentation on the Librocket API, go here.

The GitHub source files for SCPUI are available here. However, it is highly recommended that you have your game or mod rely on the core SCPUI mod on Knossos.NET here.

Overview

SCPUI works by completely overriding a given game state's internal code. Everything that interface should be expected to do must be handled in the Lua scripts. Methods to get and set appropriate game data are supplied by FSO's UI API. The core file that handles this is ui_system-sct.tbl. This file is SCPUI's state management controller. If it detects a valid SCPUI interface for a given game state, it will load a librocket file and begin execution of the appropriate scripts.

An SCPUI game state definition has three main files; RML, RCSS, and LUA. More information is available on those specific pages, but for now it's enough to know that RML is a kind of base HTML, RCSS is a base CSS, and LUA is an FSO compatible LUA file.

The lua file for that state will be run until an FSO game state change is detected. When that happens, the ui_system-sct will clean up the librocket elements and then check if a new set of SCPUI files should be loaded.

The FSO Engine's Lua API provides a library of methods for interfacing with UI code. Please consult the scripting.html file and the UserInterface (ui) library for details.

Scpui.tbl

SCPUI's global settings can be modified with scpui.tbl or xxx-ui.tbm files. Documentation on scpui.tbl is available here.

Visual Customization

Unlike FSO's built-in User Interface, SCPUI liberally re-uses visual assets wherever possible. Because of this, there are only about 225 image files compared to retail's thousands. This makes changing the look and feel of the UI incredibly easy even for those who are not interested in modifying the underlying Lua code.

Custom.RCSS

Every UI screen loads custom.rcss as its primary style sheet in addition to the rest of the style sheets for that UI screen. The idea here is that you can list all of your custom styles in that file stored in your own mod's game data. The file should be data/interface/css/custom.rcss and you can start by copying the file directly out of SCPUI's base install. This is also where you'll set your mod's primary font file.

Fonts

Fonts are defined in SCPUI using an .rcss style sheet. In the base install of SCPUI, the primary font is defined in custom.rcss like this

body
{
	font-family: BankGothic Md BT;
	font-weight: normal;
	font-style: normal;
}

Note that the font-family is the font's name as defined in the font and not the filename. In some fonts that may strictly be the font family name only, but not always. For example, Noto Sans Medium actually needs to be listed as Noto Sans.

Background

You can change the main background image for all SCPUI screens by simply replacing bg.png. Other background features are covered in the scpui.tbl documentation.

There are several UI screens that use a logo image. It comes in three main variants...

  • Standard
    • Replace logo.png to replace the standard logo throughout all of SCPUI.
  • Wide
    • For FS2, this logo is the version with the "wings". Replace logo-wide.png to change this one.
  • Loop
    • In FS2 this is the SOC logo used in the Loop Brief UI. For this one replace loop-logo.png

Loading Screen Backgrounds

Loading screen backgrounds are handled a little differently than in base FreespaceOpen. You must create or modify the loadscreenbgs.rcss file to add the custom backgrounds you wish to use. In that file create a css class with the mission filename that is to have a custom screen and then define the file like the example below.

.bta1_m1_01 {
	background-decorator: image;
	background-image: 3_Load_1M1_01.png;
}

Mission bta_m1_01.fs2 will now have the image 3_Load_1M1_01.png used as it's background.

To modify the loading bar see UI Topics.

Keywords

SCPUI has a built in feature to allow coloring keywords automatically for UI the various briefing screens. It can also, optionally, add tooltips to these keywords. This feature works in Briefing, Command Briefing, Debriefing, Fiction Viewer, Loop Briefing, and Red Alert. Definitions for keywords are tabled in a special table. See keywords.tbl documentation for more information.

Elements

The main UI elements (window boxes, button panels, etc) are all re-used liberally throughout SCPUI. At the time of writing this article, there are 53 of these files in total. You can modify them however you like.

  • It is highly recommended you do not change the aspect ratio of these images if you do not want to have to modify RML and RCSS files.

Buttons

There are a number of button styles that SCPUI uses to match FS2's retail UI. Each button has at least three images: Main, Hover, and On. Generally they are designated like the following example:

arrow-up.png ;;Main
arrow-up-h.png ;;Hover
arrow-up-o.png ;;On

In some cases there are Positive and Negative versions. These are named like the following example:

select.png ;;Main
select-pos-h.png ;;Positive Hover
select-pos-o.png ;;Positive On
select-neg-h.png ;;Negative Hover
select-neg-o.png ;;Negative On

Briefing Color Tags

SCPUI supports two methods for coloring text in briefings. It supports the retail style color tag system out of the box. However, SCPUI also has its own HTML-based approach to styling text. It works everywhere that classic color tags do - briefings (including command, loop, and red alert briefings), debriefings, and the fiction viewer - but also the tech room, campaign descriptions, and nearly everywhere that long-form text is supported. For it to work in briefings, the text must be put in "HTML mode" by putting !html at the start, followed by a space. Everywhere else will work without that tag.

Text in HTML mode may contain HTML markup. Text may be styled using <span> tags with appropriate classes drawn from common.rcss; inline styles are also supported. FSO and HTML both make use of double quotes and slashes; to minimize conflicts, using single quotes and writing slashes as $slash is recommended. A complete example might look something like this: !html <span class='white'>Pilot attend!<$slashspan> Today, we'll be escorting the <span class='green italic'>GTC Examplar<$slashspan> as she raids <span class='red'>Shivan<$slashspan> positions throughout <span class='yellow'>Gamma Draconis<$slashspan>.

It is, however, rarely necessary to perform simple styling like this manually: SCPUI's Keywords.tbl may be used to automate common styling rules.

Loadout Select Icons

SCPUI supports both 2D pre-drawn icons as well as generated 3D icons. However, they are handled a little differently than inside base FSO. SCPUI pre-generates icon images on game start and displays those in place of the actual icons. Icons are generated during SCPUI's splash screens and cached to the appdata directory to speed up load times on subsequent gameplay instances.

To use 3D icons generated from ship models you must have 3D icons enabled in your game_settings.tbl and for weapons you must have a Tech Model defined.

To use 2D icons you must have a Select Icon defined for the ship or weapon.

You can set the icon dimensions in scpui.tbl.

Ribbons

SCPUI comes with a brand new award system called Ribbons. This is meant as a way for players to show off how many FreespaceOpen Engine games they have completed. Ribbons are granted via the grant-scpui-ribbon sexp and once granted cannot be modified. Ribbons are saved to global data that spans all Freespace campaigns, mods, and other games. An example of what ribbons look like is below.

Scpui ribbons.jpg

Loadout Handling

SCPUI includes an entirely custom loadout handler that bypasses some of FSO's built-in methods. The biggest change is that each loadout is saved to a json file in the player's appdata directory as scpui_loadouts.cfg. Note this is saved in the mod's appdata directory and not the players directory.

The past loadout will be reloaded across game instances and much more reliably across gameplay entirely. Loadout saves differentiate between missions played in Campaign and missions played via Simulator room.

Librocket

Librocket is a pseudo html/css rendering library included with FSO. It is designed as a complete solution for any project's interface needs.SCPUI uses this library to create and manage each UI context.

LibRocket uses the time-tested open standards XHTML1.0 and CSS2.0 (while borrowing features from HTML5 and CSS3), and extends them with features suited towards real-time applications.

Documentation on how to interface with the librocket API can be found here.

Game State Management

SCPUI's game state management is handled by the scpui.tbl file that simply lists the game state from FSO's game states and matches it with an RML file that will be associated. One notable exception is GS_STATE_SCRIPTING which allows for substates. This feature means that you can effectively create an unlimited amount of custom UIs that all exist within GS_STATE_SCRIPTING.

  • To call a built-in game state use ba.postGameEvent("gamestate").
  • To call a scripting substate use ScpuiSystem:beginSubstate("substate")

UI Topics

SCPUI implements a way to essentially do modular scripting which allows downstream games to modify how SCPUI works without having to carry a complete copy of SCPUI itself. This system is called UI Topics. These work very similar to FSO engine hooks. There are listeners throughout the SCPUI script base that can be attached to in order to modify behavior of various screens, buttons, or other elements. Every screen contains an initialize topic which is the last code run when a game state is started and a screen is created. As a basic example, Between the Ashes uses this to modify the background image that is used throughout. Here is the Topic code that is used to change the Fiction Viewer background.

topics.fictionviewer.initialize:bind(1, function(message, context)
  campaignfilename = ba.getCurrentPlayer():getCampaignFilename()
  local main = message.document:GetElementById("main_background")
  if useSubspaceBG(main) then return end
  if utils.isOneOf(campaignfilename, "bta1_m", "bta2_m", "bta-tem_m") then
    main:SetClass("fiction_viewer_bg", true)
  end
end)

Topics sometimes include relevant data in the parameters called message and can change specific values by settings context.value. The initialize topics, like the example above, always send the entire UI document reference. There are others, like the loading screen loading bar that sends the loading bar filename only and expects a filename return. Here is an example that changes the default loading bar image to something else.

topics.loadscreen.load_bar:bind(9999, function(message, context)
  context.value = "fancyloadingbar"
end)

A full list of available topics can be found in SCPUI's ui_topics.lua file.

Data Saver

SCPUI comes with a set of Lua functions to handle saving and loading custom data quickly and easily for any kind of custom data you need. The data is saved to a json file using a hashed version of the mod's mod title and the hash can be further randomized by setting a custom seed in scpui.tbl.

Data can be saved as 'persistent' or 'not persistent'. Persistent data is saved to

data/players

, meaning it will be available to your mod even across mod versions. Non persistent data is saved to

data/config

instead.

All data is saved on a per-pilot basis.

Saving Arbitrary Data

Make sure the data you intend to save can be converted to JSON. Basically don't send any userdata. Access datasaver by calling

local datasaver = require("datasaver")

. You can then save arbitrary data with a call like this

datasaver:saveDataToFile("data title", saveData, true)

.

Saving data takes three arguments. First is the name of the data. It should be a string. Second is the data to save. The last argument defines if the data is saved as 'persistent' (true) or 'not persistent' (false) which defines where the data is saved to.

Loading Arbitrary Data

Loading arbitrary data is just as easy. Set up datasaver with a call like this

local datasaver = require("datasaver")

. Then simply ask for the data you want to load like this

local saveData = datasaver:loadDataFromFile("data title", true) or {}

.

Loading data takes two arguments. First is the name of the data and should match the name you used to save with. It should be a string. The second argument defines if the data is loaded as 'persistent' (true) or 'not persistent' (false) which defines where the data is loaded from.

Custom Mod Options

The Custom Options in the Between the Ashes mod.

SCPUI comes with a built-in feature to allow for custom in-game options specific to a particular mod. These options are defined by options.tbl and saved to the player folder as scpui_options.cfg. Scripts can take advantage of this feature to allow players to customize how scripts run, or if they even run at all.

For example, you could take the HUD Icons script and create a global toggle. This allows players to turn HUD Icons on or off entirely. Furthermore you could allow customizing the max opacity of the icons, the distance at which the icons become fully transparent, and the distance at which the icons become fully opaque. These options will all within the in-game Options UI.

  • 'Note that $Mod_title: in game_settings.tbl is required for this feature to work!'
  • It's a good idea to make sure your mod title is unique to avoid mod options collisions.

Options.tbl

This table file is used to define the Options buttons within the Options UI.

Save Structure

The player's options are saved to scpui_options.cfg in data/players. This makes the options available across mod versions. The save structure within the file follows a player->mod->option hierarchy. (See example below.) This allows for each player to have their own preferences for each mod they play without interfering with other players' preferences.

"John": {
	"Between The Ashes": {
		"Alarm_Voice": "Vasudan",
		"Verbose_Debugging": "Off",
		"Font_Multiplier": 2,
		"Icon_Far": 2,
		"Icon_Near": 5,
		"Brief_Render_Option": "Texture",
		"Icon_Opacity": "1",
		"Cockpit_Alarms": "On",
		"HUD_Icons": "Off",
		"Cockpit_Rendering": "Off",
		"Easter_Eggs": "On"
	}
},
"Mike": {
	"Between The Ashes": {
		"Cockpit_Rendering": "Off",
		"Easter_Eggs": "On",
		"Font_Multiplier": 5,
		"Icon_Far": 2,
		"Icon_Near": 5,
		"Brief_Render_Option": "Texture",
		"Icon_Opacity": "1",
		"Cockpit_Alarms": "On",
		"HUD_Icons": "Off",
		"Verbose_Debugging": "On",
		"Alarm_Voice": "Female"
	}
}

Accessing Save Data

Modifying Options within the in-game UI and saving those values to a file is just a start. In order for these options to do anything, you'll need to hook them into your scripts. After the Pilot Select screen and a player is chosen, the current Options are available in a global variable called ScpuiOptionValues with direct access to a specific option by specifying the option key.

  • It may be recommended to create a script-tbl.tbm that runs $On State End and checks if hv.OldState is either "GS_STATE_INITIAL_PLAYER_SELECT" or "GS_STATE_OPTIONS_MENU". If true, then forward the option preferences to your script's local values.
#Conditional Hooks
$Application: FS2_Open
$On State End:
[
if hv.OldState.Name == "GS_STATE_INITIAL_PLAYER_SELECT" or hv.OldState.Name == "GS_STATE_OPTIONS_MENU" then
	if ScpuiOptionValues.HUD_Icons == "On" then
		ba.print("MOD OPTION: Player enabled HUD Icons")
		RadarIcon.PlayerEnabled = true
	else
		ba.print("MOD OPTION: Player disabled HUD Icons")
		RadarIcon.PlayerEnabled = false
	end
end
]
#End

Pilot Select

The pilot select UI has special handling of custom mod options, including Font Size Multiplier because player preferences cannot be loaded until a player is selected. For this specific use case, SCPUI creates a scpui_options_global.cfg whenever user preferences are saved. This file contains no mod or player specific information in the save structure. It is just a list of key/value pairs for the most recently used pilot.

  • This setup was created specifically to handle the font size option during player selection, but it can be used to perform script actions before a player is selected if necessary.
  • In this case, options are still available as ScpuiOptionValues' and accessed the same way, but there is no guarantee that the option will exist depending on the last mod the player played.

Dialog Popups

Freespace Open has a number of built-in dialog popups throughout the user interface. SCPUI automatically intercepts those popups and displays them as a librocket styled popup window.

You can create your own custom dialog popups within the UI scripts. To do this you must include local dialogs = require("dialogs") within the scope of your script. The dialogs function has several parameters for you to define.

Title

  • Used to define the title of the dialog window.
  • dialog:title("yourtitle")

Text

  • Used to define the text or description your popup will contain.
  • dialog:text("yourtext")

Input

  • Defines if the user can type a string as input
  • Useful for allowing the user to name pilots, saves, etc
  • User must press ENTER to allow the code to execute on the provided string
  • True to allow input, false to disallow. Defaults to false if this parameter is not provided.
    • dialog:input(boolean)

Buttons

  • Defines the user-clickable buttons that will be included in the dialog. It has the following parameters
  • Type can be one of the following:
    • dialogs.BUTTON_TYPE_POSITIVE
    • dialogs.BUTTON_TYPE_NEGATIVE
    • dialogs.BUTTON_TYPE_NEUTRAL
  • Text is the text for the button, such as "Okay".
  • Value is the data that will be returned when the button is clicked.
  • Keypress is the keyboard shortcut for this button.
  • dialog:buttons(type, text, value, keypress)

Escape

  • Used to define if the user should be able to cancel the dialog popup with the ESC key.
  • A non nil value enables ESC and will be the return value if ESC is pressed
  • dialog:escape(value)

Show

  • This is the final step that displays the popup and defines how to handle the return values
  • return values come back as the parameter response
  • The ui.enableInput() here specifically enables input on the dialog popup context. The dialog will automatically return input to the SCPUI context when it's closed.
dialog:show(self.document.context)
   :continueWith(function(response)
      "Execute code here"
   end)
ui.enableInput(self.document.context)

Individual UIs

All game stages are now supported with the following exceptions:

  • Cutscenes
    • Because of FSO/Librocket limitations, viewing a cutscene actually sets SCPUI to a Scripting Substate for the duration of the cutscene itself.
  • F3 Lab is not supported as an SCPUI style UI. It will use the Base FSO UI
  • F3 In-game options is not supported as an SCPUI style UI.
    • This is because SCPUI integrates in-game options directly into the main options UI.
  • Multi UIs have not been thoroughly tested and may be buggy

Development and Debugging

There are three main things to know when developing and debugging SCPUI.

  1. You can view the librocket debugger by pressing ctrl-shift-d
  2. You can live-reload RML and RCSS files by pressing ctrl-shift-r
    • It's worth noting that Librocket will drop the existing elements and lose them in memory. This will cause a warning on game-exit.
  1. Librocket API warnings and errors are not currently forwarded to FSO's error handling. Because of this, simple bugs in your Lua code that uses the API can cause FSO to crash.
  2. You can force regenerating all loadout icons by pressing F12 during FSO's splash screen.
  3. You can force deleting the current mission's loadout by pressing F12 during mission loading.

Tips

  • In general, the ui system will handle passing control back and forth between the SCPUI context and FSO. In rare cases you may need to handle this manually. The FSO API methods ui.enableInput() and ui.disableInput() are provided to handle this case.
  • For 3D weapon selection, SCPUI can only use Tech models and you need to make sure you have Closeup_Pos and Closeup_Zoom set. If you don't have a tech model, just set the regular model file as the tech model in your table.

Tutorials

  • TODO