Difference between revisions of "Mission screen"

From Elite Wiki
m (Fixed an accidentally removed section header)
(Moved Alaric's code for tabular layout to Alignment section)
Line 44: Line 44:
 
== Alignment ==
 
== Alignment ==
 
There is no obvious way to do this, but [[User:Ocz|Ocz]] managed some version of right alignment for his [[First Finance OXP]]
 
There is no obvious way to do this, but [[User:Ocz|Ocz]] managed some version of right alignment for his [[First Finance OXP]]
 +
 +
[[User:Alaric/KV-16 Owners Manual|Alaric's unfinished Torus-field-monitor OXZ]] contains handy functions to help format text in tabular layout for use in mission screens/MFDs, just keep in mind that max row length on mission screens is 32 em.
 +
<div class="mw-collapsible mw-collapsed"  data-expandtext="Show code" data-collapsetext="Hide code" style="max-width:max-content">
 +
See his alaric-oxp-utilities.js file: <span style="visibility: hidden">&nbsp;&nbsp;&nbsp;&nbsp;Show code</span>
 +
<pre style="width:max-content" class="mw-collapsible-content">
 +
"use strict";
 +
 +
this.name        = "alaric-oxp-utilities";
 +
this.author      = "Alaric";
 +
this.copyright  = "2016 Alaric";
 +
this.description = "General helper functions for OXPs";
 +
this.licence    = "CC BY-NC-SA 3.0 AU";
 +
this.version = "1.0";
 +
 +
/**
 +
* Trims a length of text to fit in the available width. If the text is truncated,
 +
* an ellipse (U+2026) will be appended unless <ellipses> is false. If <ellipses>
 +
* is omitted from the call, it defaults to true.
 +
*
 +
* text - The text to trim
 +
* emDisplayWidth - The available width, in Em, to display the text
 +
* ellipses - optional (default: true)
 +
*
 +
* Returns the new, trimmed, string.
 +
*/
 +
this._trimTextToFitWidth = function(text, emDisplayWidth, ellipses)
 +
{
 +
var font = defaultFont;
 +
var chPadding = String.fromCharCode(31);
 +
var ellipsesText = "\u2026";
 +
var emEllipsesText = font.measureString(ellipsesText);
 +
var emPaddingText = font.measureString(chPadding);
 +
 +
var chWidth = [0, text.length];
 +
var emWidth = [font.measureString(text), 0];
 +
 +
// use default for ellipses if not supplied
 +
if (ellipses === null || ellipses === undefined) ellipses = true;
 +
 +
// if the text already fits, just return it.
 +
if (emWidth[0] <= emDisplayWidth) return text;
 +
 +
// if the display width is too short for ellipses, disable ellipses
 +
if (emEllipsesText >= emDisplayWidth) ellipses = false;
 +
 +
// subtract ellipses with from display width if ellipses is true
 +
emDisplayWidth -= (ellipses) ? emEllipsesText : 0;
 +
 +
while (chWidth[0] != chWidth[1])
 +
{
 +
// get Em width of text at length midway between chWidth[0] and chWidth[1]
 +
var chPivot = Math.ceil((chWidth[0] + chWidth[1]) / 2);
 +
var emPivot = font.measureString(text.substring(0, chPivot));
 +
 +
// update for next split point based on the text being too long or too short
 +
var flagDirection = (emPivot <= emDisplayWidth) ? 0 : 1;
 +
 +
chWidth[flagDirection] = chPivot - flagDirection;
 +
emWidth[flagDirection] = emPivot;
 +
}
 +
 +
// At this point, chWidth[0] and emWidth[0] contain the trimmed width in
 +
// characters and Em respectively. Return the text, appending ellipses if
 +
// <ellipses> is true. The space for ellipses has already been accounted
 +
// for.
 +
 +
return (ellipses)
 +
? text.substring(0, chWidth[0]) + ellipsesText
 +
: text.substring(0, chWidth[0])
 +
;
 +
}
 +
 +
 +
/*
 +
* Builds tabular (columns aligned) text for use in mission screens/MFDs.
 +
*
 +
* Input to the function is provided by an array of 'rows'. Each row is, itself, an array of
 +
* objects with the following properties:
 +
*
 +
* Required properties:
 +
* text: The text to display in this column
 +
* width: The width of the column in em. Text will be truncated if too long.
 +
*
 +
* Optional properties:
 +
* alignment: LEFT, RIGHT or CENTER. Default: LEFT
 +
* elipses: Display elipses for truncated text? Default: true.
 +
*  blink:
 +
*
 +
* Multiple rows are deliniated by '\n'. No '\n' is appended to the last row.
 +
*
 +
*/
 +
this._buildTabularText = function(rows)
 +
{
 +
var padCharacter = String.fromCharCode(31);
 +
var padWidth = defaultFont.measureString(padCharacter);
 +
var tabularText = "";
 +
var row;
 +
 +
for (row = 0; row < rows.length; ++row)
 +
{
 +
if (row > 0) tabularText += "\n";
 +
 +
var i;
 +
 +
var currentEm0 = 0;
 +
var currentEm1 = 0;
 +
var columns = rows[row];
 +
var rowText = "";
 +
 +
for (i = 0; i < columns.length; ++i)
 +
{
 +
 +
currentEm0 = defaultFont.measureString(rowText);
 +
var leading = (currentEm1 - currentEm0);
 +
currentEm1 = currentEm1 + columns[i].width;
 +
 +
var text = this._trimTextToFitWidth(columns[i].text, currentEm1 - currentEm0, columns[i].ellipses);
 +
var width = defaultFont.measureString(text);
 +
 +
 +
var padding = (currentEm1 - currentEm0) - width;
 +
 +
switch ((columns[i].alignment !== undefined) ? columns[i].alignment : "LEFT")
 +
{
 +
 +
case "LEFT" : padding = 0; break;
 +
case "RIGHT" : leading = 0; break;
 +
case "CENTER" : padding = padding / 2; break;
 +
 +
default:
 +
log(this.name, "invalid alignment '" + columns[i].alignment + "'");
 +
padding = 0; break;
 +
 +
}
 +
 +
padding = Math.floor((leading + padding) / padWidth);
 +
 +
rowText += (padding >= 1)
 +
? new Array(padding).join(padCharacter) + text
 +
: text
 +
;
 +
}
 +
 +
tabularText += rowText;
 +
}
 +
 +
return tabularText;
 +
}
 +
</pre>
 +
</div>
  
 
== Backdrop ==
 
== Backdrop ==
Line 123: Line 273:
 
*[http://www.aegidian.org/bb/viewtopic.php?f=4&t=18713 New Line in mission.runScreen?] (2017)
 
*[http://www.aegidian.org/bb/viewtopic.php?f=4&t=18713 New Line in mission.runScreen?] (2017)
 
*[http://www.aegidian.org/bb/viewtopic.php?f=4&t=18414 Tabular text output for MFDs/mission screens] (2016)
 
*[http://www.aegidian.org/bb/viewtopic.php?f=4&t=18414 Tabular text output for MFDs/mission screens] (2016)
*[[User:Alaric/KV-16 Owners Manual|Alaric's unfinished Torus-field-monitor OXZ]] contains Javascript for formatting MFD's - and for making text flow. See his alaric-oxp-utilities.js file:
 
  
"use strict";
 
 
this.name        = "alaric-oxp-utilities";
 
this.author      = "Alaric";
 
this.copyright  = "2016 Alaric";
 
this.description = "General helper functions for OXPs";
 
this.licence    = "CC BY-NC-SA 3.0 AU";
 
this.version = "1.0";
 
 
/**
 
* Trims a length of text to fit in the available width. If the text is truncated,
 
* an ellipse (U+2026) will be appended unless <ellipses> is false. If <ellipses>
 
* is omitted from the call, it defaults to true.
 
*
 
* text - The text to trim
 
* emDisplayWidth - The available width, in Em, to display the text
 
* ellipses - optional (default: true)
 
*
 
* Returns the new, trimmed, string.
 
*/
 
this._trimTextToFitWidth = function(text, emDisplayWidth, ellipses)
 
{
 
var font = defaultFont;
 
var chPadding = String.fromCharCode(31);
 
var ellipsesText = "\u2026";
 
var emEllipsesText = font.measureString(ellipsesText);
 
var emPaddingText = font.measureString(chPadding);
 
 
var chWidth = [0, text.length];
 
var emWidth = [font.measureString(text), 0];
 
 
// use default for ellipses if not supplied
 
if (ellipses === null || ellipses === undefined) ellipses = true;
 
 
// if the text already fits, just return it.
 
if (emWidth[0] <= emDisplayWidth) return text;
 
 
// if the display width is too short for ellipses, disable ellipses
 
if (emEllipsesText >= emDisplayWidth) ellipses = false;
 
 
// subtract ellipses with from display width if ellipses is true
 
emDisplayWidth -= (ellipses) ? emEllipsesText : 0;
 
 
while (chWidth[0] != chWidth[1])
 
{
 
// get Em width of text at length midway between chWidth[0] and chWidth[1]
 
var chPivot = Math.ceil((chWidth[0] + chWidth[1]) / 2);
 
var emPivot = font.measureString(text.substring(0, chPivot));
 
 
// update for next split point based on the text being too long or too short
 
var flagDirection = (emPivot <= emDisplayWidth) ? 0 : 1;
 
 
chWidth[flagDirection] = chPivot - flagDirection;
 
emWidth[flagDirection] = emPivot;
 
}
 
 
// At this point, chWidth[0] and emWidth[0] contain the trimmed width in
 
// characters and Em respectively. Return the text, appending ellipses if
 
// <ellipses> is true. The space for ellipses has already been accounted
 
// for.
 
 
return (ellipses)
 
? text.substring(0, chWidth[0]) + ellipsesText
 
: text.substring(0, chWidth[0])
 
;
 
}
 
 
 
/*
 
* Builds tabular (columns aligned) text for use in mission screens/MFDs.
 
*
 
* Input to the function is provided by an array of 'rows'. Each row is, itself, an array of
 
* objects with the following properties:
 
*
 
* Required properties:
 
* text: The text to display in this column
 
* width: The width of the column in em. Text will be truncated if too long.
 
*
 
* Optional properties:
 
* alignment: LEFT, RIGHT or CENTER. Default: LEFT
 
* elipses: Display elipses for truncated text? Default: true.
 
*  blink:
 
*
 
* Multiple rows are deliniated by '\n'. No '\n' is appended to the last row.
 
*
 
*/
 
this._buildTabularText = function(rows)
 
{
 
var padCharacter = String.fromCharCode(31);
 
var padWidth = defaultFont.measureString(padCharacter);
 
var tabularText = "";
 
var row;
 
 
for (row = 0; row < rows.length; ++row)
 
{
 
if (row > 0) tabularText += "\n";
 
 
var i;
 
 
var currentEm0 = 0;
 
var currentEm1 = 0;
 
var columns = rows[row];
 
var rowText = "";
 
 
for (i = 0; i < columns.length; ++i)
 
{
 
 
currentEm0 = defaultFont.measureString(rowText);
 
var leading = (currentEm1 - currentEm0);
 
currentEm1 = currentEm1 + columns[i].width;
 
 
var text = this._trimTextToFitWidth(columns[i].text, currentEm1 - currentEm0, columns[i].ellipses);
 
var width = defaultFont.measureString(text);
 
 
 
var padding = (currentEm1 - currentEm0) - width;
 
 
switch ((columns[i].alignment !== undefined) ? columns[i].alignment : "LEFT")
 
{
 
 
case "LEFT" : padding = 0; break;
 
case "RIGHT" : leading = 0; break;
 
  case "CENTER" : padding = padding / 2; break;
 
 
default:
 
log(this.name, "invalid alignment '" + columns[i].alignment + "'");
 
padding = 0; break;
 
 
}
 
 
padding = Math.floor((leading + padding) / padWidth);
 
 
rowText += (padding >= 1)
 
? new Array(padding).join(padCharacter) + text
 
: text
 
;
 
}
 
 
tabularText += rowText;
 
}
 
 
return tabularText;
 
}
 
  
  
  
 
[[Category:Oolite scripting]]
 
[[Category:Oolite scripting]]

Revision as of 19:58, 8 February 2023

Hints.png

Mission screens started off as screens which allow the player to interface with NPCs in a mission.oxp but also allow other sorts of "communication" as in the conversation overheard in the bar in the Hints OXP.

They can have backdrops, changes in text and allow choices for the player.

The text can be placed in a descriptions.plist, details about it in a script.js

Text

Several spacial characters are used in determining the layout of the text, just as in Wiki.
   \" Enables the use of colons.
   \\n Insert a hard Enter.

In XML:

   \n Inserts a hard Enter.
See Missiontext.plist for more complexity (eg random names, naming current system etc)
Character '31' (hex:1F, octal:037) is a narrow 'hair-space'. Custom font OXPs should ensure that this character is blank and has the same narrow width as the core font definition, as it is used to allow an equivalent to 'tab stops' in mission text.


% codes

Here are the % codes that can be used inside description strings and what each one does:

Code:

  •  %H is replaced with <planetName>. If systemName is nil, a planet name is retrieved through -[Universe getSystemName:], treating <seed> as a system seed.
  •  %I is equivalent to "%H[planetname-derivative-suffix]".
  •  %N is replaced with a random "alien" name using the planet name digraphs. If used more than once in the same string, it will produce the same name on each occurence.
  •  %R is like %N but, due to a bug, misses some possibilities. Deprecated.
  •  %JNNN, where NNN is a three-digit integer, is replaced with the name of system ID NNN in the current galaxy.
  •  %GNNNNNN, where NNNNNN is a six-digit integer, is replaced with the name of system ID NNN (first triplet) in the specified galaxy (second triplet).
  •  %% is replaced with %.
  •  %[ is replaced with [.
  •  %] is replaced with ].
From String expansion

Colour

Choices can be non-yellow (have a look at the various contracts interfaces in 1.77) but there is a more fundamental limitation of the current UI code that all text on a particular line has to be the same colour. Cim (2013)
Code for Colour change
First Finance Loan Management.png

Alignment

There is no obvious way to do this, but Ocz managed some version of right alignment for his First Finance OXP

Alaric's unfinished Torus-field-monitor OXZ contains handy functions to help format text in tabular layout for use in mission screens/MFDs, just keep in mind that max row length on mission screens is 32 em.

See his alaric-oxp-utilities.js file:     Show code

"use strict";

this.name        = "alaric-oxp-utilities";
this.author      = "Alaric";
this.copyright   = "2016 Alaric";
this.description = "General helper functions for OXPs";
this.licence     = "CC BY-NC-SA 3.0 AU";
this.version	 = "1.0";

/**
* Trims a length of text to fit in the available width. If the text is truncated, 
* an ellipse (U+2026) will be appended unless <ellipses> is false. If <ellipses>
* is omitted from the call, it defaults to true.
*
* text				- The text to trim
* emDisplayWidth	- The available width, in Em, to display the text
* ellipses			- optional (default: true)
*
* Returns the new, trimmed, string.
*/
this._trimTextToFitWidth = function(text, emDisplayWidth, ellipses)
{
	var font = defaultFont;
	var chPadding = String.fromCharCode(31);
	var ellipsesText = "\u2026";
	var emEllipsesText = font.measureString(ellipsesText);
	var emPaddingText = font.measureString(chPadding);
		
	var chWidth = [0, text.length];
	var emWidth = [font.measureString(text), 0];

	// use default for ellipses if not supplied
	if (ellipses === null || ellipses === undefined) ellipses = true;
	
	// if the text already fits, just return it.
	if (emWidth[0] <= emDisplayWidth) return text;
	
	// if the display width is too short for ellipses, disable ellipses
	if (emEllipsesText >= emDisplayWidth) ellipses = false;

	// subtract ellipses with from display width if ellipses is true
	emDisplayWidth -= (ellipses) ? emEllipsesText : 0;
		
	while (chWidth[0] != chWidth[1])
	{
		// get Em width of text at length midway between chWidth[0] and chWidth[1]
		var chPivot = Math.ceil((chWidth[0] + chWidth[1]) / 2);
		var emPivot = font.measureString(text.substring(0, chPivot)); 

		// update for next split point based on the text being too long or too short
		var flagDirection = (emPivot <= emDisplayWidth) ? 0 : 1;

		chWidth[flagDirection] = chPivot - flagDirection;
		emWidth[flagDirection] = emPivot;
	}

	// At this point, chWidth[0] and emWidth[0] contain the trimmed width in
	// characters and Em respectively. Return the text, appending ellipses if 
	// <ellipses> is true. The space for ellipses has already been accounted
	// for.

	return (ellipses) 
		? text.substring(0, chWidth[0]) + ellipsesText
		: text.substring(0, chWidth[0])
		;
}


/*
* Builds tabular (columns aligned) text for use in mission screens/MFDs.
*
* Input to the function is provided by an array of 'rows'. Each row is, itself, an array of
* objects with the following properties:
*
* Required properties:
*	text:	The text to display in this column
*	width:	The width of the column in em. Text will be truncated if too long.
*
* Optional properties:
*	alignment:	LEFT, RIGHT or CENTER. Default: LEFT
*	elipses:	Display elipses for truncated text? Default: true.
*  blink:		
*
* Multiple rows are deliniated by '\n'. No '\n' is appended to the last row.
*
*/
this._buildTabularText = function(rows)
{
	var padCharacter = String.fromCharCode(31);
	var padWidth = defaultFont.measureString(padCharacter);
	var tabularText = "";
	var row;
	
	for (row = 0; row < rows.length; ++row)
	{
		if (row > 0) tabularText += "\n";
	
		var i;
		
		var currentEm0 = 0;
		var currentEm1 = 0;
		var columns = rows[row];
		var rowText = "";

		for (i = 0; i < columns.length; ++i)
		{
		
			currentEm0 = defaultFont.measureString(rowText);
			var leading = (currentEm1 - currentEm0);
			currentEm1 = currentEm1 + columns[i].width;
						
			var text = this._trimTextToFitWidth(columns[i].text, currentEm1 - currentEm0, columns[i].ellipses);
			var width = defaultFont.measureString(text);
			
		
			var padding = (currentEm1 - currentEm0) - width;

			switch ((columns[i].alignment !== undefined) ? columns[i].alignment : "LEFT")
			{
				
				case "LEFT" : padding = 0; break;
				case "RIGHT" : leading = 0; break;
 				case "CENTER" : padding = padding / 2; break;					

				default:
					log(this.name, "invalid alignment '" + columns[i].alignment + "'");
					padding = 0; break;

			}

			padding = Math.floor((leading + padding) / padWidth);
			
			rowText += (padding >= 1) 
				? new Array(padding).join(padCharacter) + text 
				: text
				;
		}
	
		tabularText += rowText;
	}

	return tabularText;
}

Backdrop

This is managed from within the script.js file:
background: ""the_file_name_of_the_image_you_want_to_display_must_be_in_the_images_folder.png",

or you can use overlay instead of background (less conflicts with other oxp's such as XenonUI which also tend to specify a background)

Problems with Backdrop

These can be prevented from appearing by other oxp's such as XenonUI which also create backdrops for the docked screens.

Using overlay helps, but also see Phkb's comments here (for Dark Side solutions) and here (using Library config).

Exit Screen

By default, when the mission screen ends, the game returns to the status screen (F5), but this behavior can be changed. In most cases you can just provide the exitScreen parameter to the mission.runScreen call, but if if the exit screen depends on the player's choice, you need change the exit screen in the mission screen callback by setting mission.exitScreen. Outside of a callback function, the value of this is almost meaningless, and setting it has no useful effect.

The example below uses both ways:     Show Example

this._runMissionScreen = function _runMissionScreen() 
{
    mission.runScreen(
        {
            title: "Exit Screen Example",
            exitScreen: "GUI_SCREEN_MARKET", // <-- The first way 
            choices: {
                "01_INTERFACES": "Do nothing",
                "02_CHART": "Set mission.exitScreen to \"GUI_SCREEN_SHORT_RANGE_CHART\"",
                "03_SYSDATA": "Set mission.exitScreen to \"GUI_SCREEN_SYSTEM_DATA\"",
                "04_INVALID": "Set mission.exitScreen to \"INVALID VALUE\"",
            }
        },
        this._missionScreenCallback.bind(this)
    );
    // The mission.exitScreen variable is set to the value 
    // of the exitScreen parameter of the function call above
    mission.addMessageText("Current exit screen is " + mission.exitScreen + ".");
};

this._missionScreenCallback = function _missionScreenCallback(choice) 
{
    // The choice is null when player interrupts the mission screen (e.g., presses F1 )
    if (choice === "01_INTERFACES" || choice === null) {
        // Do nothing - the exit screen will be as specified in runScreen call
    }
    else if (choice === "02_CHART") {
        mission.exitScreen = "GUI_SCREEN_SHORT_RANGE_CHART"; // <-- The second way
    }
    else if (choice === "03_SYSDATA") {
        mission.exitScreen = "GUI_SCREEN_SYSTEM_DATA";
    }
    else if (choice === "04_INVALID") {
        mission.exitScreen = "INVALID VALUE"; // The exit screen will be reset to default
    }
};

// Add interface to F4 page for docked station
this.startUpComplete = function startUpComplete() 
{
    player.ship.dockedStation.setInterface("example_exit_screen",
        {
            title: "Show exitScreen example screen",
            summary: "This is an example interface definition.",
            category: "AAA",
            callback: this._runMissionScreen.bind(this)
        }
    );
};

Svengali's Library & CCL

These two oxp's allow for more complex interractions. See the deprecated CCL's never used Cutscene, as well as the current Library OXP's "Music" and "Demos".

Links