-
Notifications
You must be signed in to change notification settings - Fork 16
Configuring a monitor
Monitors are created by adding a RasterPropMonitor into the prop.cfg for the prop you have made.
There's one more module you will need, RasterPropMonitorComputer. Notice that it's a PartModule, not an InternalModule, and requires no configuration. You just need to add it to the pod that will be hosting your IVA. Many other modules in this package also use it to store persistence data and compute all the variables, so you really want it to be there.
WARNING: The configuration relies heavily on direct texture URLs. Remember that in all cases, path names must use "/" slashes, and not the other kind. Forgetting that can produce hard to debug configuration errors.
- screenTransform -- the name of the screen object.
- textureLayerID -- Unity name of a texture ID in the object's material that the screen will be printed on. Defaults to "_MainTex". KSP shaders also have "_BumpMap" and "_Emissive".
- fontTransform -- Where to get the font bitmap. You can either place a texture somewhere in GameData and refer to it exactly like you would in a MODEL configuration node (KSP reads in everything that looks like a texture and is stored outside of a PluginData directory, and assigns it an URL) or put the texture on a model transform and give the name of that transform.
- fontDefinition -- An URL (with extension) pointing to a text file that contains the font definition, if you need one different from the default. For explanations, see Making a prop for RasterPropMonitor.
-
extraFont -- Optional. The definition is the same as in
fontTransform. You require at least onefontTransformoption so that a default font always exists, but you can switch fonts on the fly using the[font<number>]tags. extraFont entries are the fonts you will switch to. The font listed infontTransformis always number 0, while extraFont entries are in order of appearance in the configuration -- 1 is the first extraFont, etc. Extra fonts must have the same dimensions as the default font. - emptyColor -- R,G,B,A of a color that will be used to blank out the screen between refreshes. Everywhere, individual color values range from 0 to 255, i.e. a pure non-transparent red is "255,0,0,255".
- screenWidth/screenHeight -- Number of characters in a line and number of lines.
- screenPixelWidth/screenPixelHeight -- Width and height of the texture to be generated for the screen. You probably want these to be powers of 2, but they don't have to be equal.
- fontLetterWidth/fontLetterHeight -- Width and height of a font cell in pixels. Letters are printed on the screen in pixel-perfect mapping, so one pixel of a font texture will always correspond to one pixel of the generated screen texture -- as a result, you can have less characters in a line than would fit into screenPixelWidth, but can't have more.
- cameraAspect -- Aspect ratio of the camera images when this screen will be used to show them. (See Setting up cameras)
- globalButtons -- A comma-separated list of button transforms. Empty by default, this option is only relevant when you use Page handlers or Background handlers. It can contain empty entries.
- buttonClickSound -- An URL (in the KSP sense) of a sound file that will be played whenever the user clicks a button. Empty by default, optional.
- buttonClickVolume -- Volume of the button click, defaults to 0.5.
- refreshDrawRate,refreshTextRate,refreshDataRate -- The three different refresh rates for the monitor, expressed in number of frames that will be skipped before the specified refresh occurs. Draw is how often the screen will be repainted -- any background, including a camera, will be repainted that often. Text is how often a page definition will be reassembled into actual characters. Data is how often certain more resource-intensive data collection operations will run.
- needsElectricCharge -- Boolean, defaults to true. RasterPropMonitor does not actually consume electric charge (if you feel it is warranted, you can just add the power consumption to ModuleCommand of your pod) but if this variable is true, the monitor will go blank when no power is available.
- defaultFontTint -- Optional. An RGBA color, this tint will be applied to the font when printing unless colortags change it. A normal non-transparent white is the default, if you, e.g. want a transparent font, you can make it something like 255,255,255,128.
- noSignalTextureURL -- Optional. If the page is supposed to display a camera view but can't, because the camera is missing or destroyed, the texture referenced by this URL will be displayed instead.
Multiple screens in the same IVA will share their computing modules, but this also means that the lowest refreshTextRate and refreshDataRate among all those given will be used when computing variables. refreshDrawRate remains individual per monitor.
All the components of RasterPropMonitor use the same button handling routines, and beyond a simple name of a transform (which must be located within the same prop as the module that will consume this button) they can also handle expressions in the form of <transform name>|<prop ID>, (i.e. separated by a pipe character) where "prop ID" is the number of the prop that the button should be located on in the list of props within the internal.cfg, starting with 0. Remember that MODULE{} entries also count for the purposes of computing the prop ID if they are present in the internal.cfg -- if you have an InternalSeat and an InternalCameraTagetHelper in the beginning of the internal.cfg, the first PROP{} in this list actually has prop ID 2. You can use JSIPropIDFinder prop module to help you find the prop ID. If the prop ID you give is negative, the internal model itself will be searched for the transform instead. This also works in lists of globalButtons, for JSIActionGroupSwitch and any other button consumer.
Beware, every time you save the internal from Unity, it will sort the props alphabetically, which may cause prop IDs to change! This may also make configurations which use multiple identical monitors very difficult to maintain, so it's not recommended to use this feature unless you need a unique monitor.
In general, the same button transform can be shared between any number of actions, and they will always trigger simultaneously -- the only exception being page selection buttons, which behave in a somewhat more complicated manner. (see below)
Page definitions are blocks of PAGE within the module configuration:
PAGE
{
name = firstpage
text = JSI/RasterPropMonitor/Example/ExampleProp/page1.txt
button = buttonR1
}
Parameters within the PAGE block are:
- name -- The unique name of the page. Optional, but it's a very good idea to have it, because it permits the users to alter page definitions using ModuleManager patches, and it is required to use context-aware page button features.
- button -- Button transform that will trigger that particular page. Multiple pages can share the same button -- if they do, clicking on this button will cycle between them. Cycling is in order of page definition, and loops. Selected page will persist, but position in the cycle loop will not. If multiple monitors have their pages assigned to the same button, both monitors will cycle the pages simultaneously, i.e. if monitor A has pages 1 and 2 on the same button as monitor B's pages 3 and 4, pressing the button will cause both monitors to change at once -- A will show page 1, and B will show page 3. Pressing it again will cause pages 2 and 4 to show respectively. Obviously, if the number of pages hanging on each button is not the same, the loop will go out of sync fairly soon.
- default -- If this parameter is set to anything, this page will be the one on the screen when the module finishes loading. If you include JSIInternalPersistence module in your prop.cfg for the pod, the page selected afterwards will persist upon reload of the vessel. (see below)
- lockingPage -- If this parameter is set to anything, the page will lock upon being selected -- only triggering a page which is declared as an "unlocker page" will change the page. This is the simple locking mode.
- disableSwitchingTo -- A more complicated version of page locking, this is an optional comma-separated list of page names. Pages with names which are present in this list will not be switched to while this page is active.
-
unlockerPage -- If this parameter is set to anything, this page is an unlocker page and triggering it will allow you to change from a locked page. Selecting an unlocker page will also force the switch if
disableSwitchingTooption is used. - textureOverlayURL -- This is a texture URL. If this parameter is present, this texture will be plastered onto the screen as the last step of screen rendering, overlaying anything and everything you have previously printed on it. Be aware that the same shader as the one used to print letters is used to display this texture, while the color it is printed with is full brightness white -- so everything said about colors and font bitmaps applies to texture overlays as well.
- textureInterlayURL -- Works the same as an overlay URL -- but this texture will be plastered onto the screen once the background, whatever it might be, has finished rendering, but before starting to print the text.
A monitor needs to have at least one page. There is no hard limit to the number of pages. If there is a button option, but neither any text nor a background to in the page block, the screen will be blanked when this button is pressed. A page does not actually require having a button, but if it doesn't, it will be impossible to switch to it again.
Page text can be defined in one of three ways:
- A Page Handler. This is defined by a PAGEHANDLER{} block and if found, takes precedence over interpreting the
textoption. Multiple page handler blocks are allowed per page. If the page handler did not load (either due to not being present in the system or due to linking to a foreign plugin that isn't) the next one will be tried, and if none get loaded, thetextoption will be interpreted. See Page Handlers - A page definition file. The text option in the PAGE block is treated as an URL (in the KSP sense, i.e. that starts just below GameData, like a MODEL texture URL) of a text file that contains a complete screen definition. It must include a file extension if your file has one. Do not use ".cfg", KSP doesn't like it. Example:
text = MyModGamedataDirectory/Screens/ThatPage.txt. See Writing page definition files. - If the above fails, the text option is interpreted directly. In this case, "$$$" will be replaced with a line break. If you wish to use the { and } format string characters in such a screen definition, you need to replace { with <= and } with =>, because KSP mangles them upon reading from prop.cfg files.
A page background is defined in one of three ways:
- A Background Handler. This works just like a Page Handler but the block is named BACKGROUNDHANDLER{}. It takes precedence over other kinds of background. Multiple background handler blocks are allowed per page. If the background handler did not load (either due to not being present in the system or due to linking to a foreign plugin that isn't) the next one will be tried, and if none got loaded, other background options will take effect. See Background Handlers.
- A cameraTransform option, this is the name of a transform marking the location of an external camera. (See Setting up cameras and below) Beyond the normal transform names, there is a special reserved transform name --
CurrentReferenceDockingPortCamera. It's a camera that always corresponds to the current reference docking port, which the user can select using the targeting menu if you configure such a page. Cameras take precedence over a texture URL, which is... - A textureURL option. This is a texture URL in the usual KSP sense that will be used as a background for this page, and this page only. It will be painted onto the screen and stretched across the entirety of it.
Cameras, beyond the primary cameraTransform option which defines where the camera will be and where it will point have extra options that define their field of view -- i.e. zoom level:
- fov -- A fixed field of view in degrees. This takes precedence over a mutable field of view, which is defined by these options:
-
zoomFov --
<minimumFOV>,<maximumFOV>,<number_of_steps>. The first two values are in degrees, the third is the number of button clicks it will take to reach from one to the other, with actual FOV values spread linearly between them. Cameras will start out at maximumFOV, the current zoom level will not persist. -
zoomButtons --
<zoomOut>,<zoomIn>are numbers of globalButtons that will be taken to mean 'zoom out' and 'zoom in' respectively and pressing these buttons will step through the FOV range.
Beware that page handlers will also receive those button presses, which makes zooming cameras incompatible with those of them that require button presses.
If you've been reading the above carefully, you probably noticed the following tricks:
- A page button can coexist with a globalButton on the same transform.
- A page can lock itself from switching to other pages while it's active, and in this case, globalButtons which coexist with the page buttons that otherwise would switch a page will be passed to the page itself.
- A page doesn't actually have to have a button, there just isn't any other way to switch to it.
This permits you to arbitrarily reuse page buttons as globalButtons while a certain page is active, but you can complement this with rearranging the page buttons to call other pages as well, which can even let you switch to pages which don't actually have buttons. For this, the PAGE block must contain a CONTEXTREDIRECT block:
PAGE
{
...
CONTEXTREDIRECT
{
redirect = pageA,pageB
redirect = pageC,pageD
}
}
While this page is active, a button press that would normally result in switching to page named "pageA" will instead switch to "pageB". There are certain things you need to remember:
- Page redirects are processed before page locks. I.e. if you lock out a page in
disableSwitchingToand then redirect a button to it, it just won't switch. - Page redirects are actually a mechanism independent of page locks, and will work even if no page locks are defined.
- Both pages involved in a redirect must have a name for this to work.
- 'pageA' in the above example is not the name of a button -- it is the name of a page. I.e. if you want to recycle a button that is otherwise assigned to two different pages, you will need to add a redirect entry for both of them.
- There's no chaining of redirects. I.e. if 'pageA' redirects to 'pageB', and 'pageB' redirects to 'pageC', pressing the button for 'pageA' will not activate 'pageC'.
RasterPropMonitorComputer has the important job to compute things for all the modules of this package, as well as to store persistence data for InternalModules, which normally can't do it on their own. This is a PartModule. You want to add this module to the capsule that will be hosting the various InternalModules supplied by this package. They will still work if you don't, but certain things will not persist when they would otherwise, and things like AGMEMO variables will not work. There are no parameters to configure:
MODULE
{
name = RasterPropMonitorComputer
}