Tutorial 2 Action Group Button - MOARdV/AvionicsSystems GitHub Wiki

This example will walk through the config file for MAS_pb_AG1, a push-button style toggle for action group 1. This example is based on MOARdV/MAS_ASET/Push_Button_Module/MAS_pb_AG1.cfg. The reader should already be familiar with prop config files, so this example focuses on the MAS configuration.

MODULE
{
  name = MASComponent

MASComponent is the Prop Module for anything that is not a monitor.

Collider

  COLLIDER_EVENT
  {
    name = Collider

The first thing we plug into this MASComponent is a COLLIDER_EVENT. This is the node that handles button clicks, and thus makes the IVA experience interactive. Without the COLLIDER_EVENT, all we'd have is a bunch of data displays and decorations. This prop unimaginatively names the COLLIDER_EVENT "Collider". The name is important when an error occurs: If there is a problem with the COLLIDER_EVENT, the log will name the node that failed to configure, along with the name of the prop it's attached to. Without a name, it would be very difficult to figure out which component failed to configure if there is more than one in the prop.

    collider = pb_Collider
    onClick = fc.ToggleActionGroup(1)
    sound = ASET/ASET_Props/Sounds/pb_Push02
    volume = 1
  }

Here is the core of a COLLIDER_EVENT. It contains the collider parameter, which tells MAS the name of the collider that this event cares about. It also includes the name of a sound effect to play, and the volume that sound should be played at.

The onClick parameter is what makes the prop do something other than play sound effects when it is clicked. In this case, the parameter tells MAS to trigger the action fc.ToggleActionGroup(1). Most MAS actions are prefixed by fc., which means "Flight Computer". MAS actions that are related to mods that MAS supports, or are part of a specialized feature, will have different prefixes, such as mechjeb. or navigation.. If we refer back to the prop maker's page of the wiki, we can see that the fc group's methods are described on several pages. In this case, we want the MASFlightComputerProxy page. The explanation for ToggleActionGroup(1) is here.

We can see that the onClick parameter will toggle Action Group 1 from off to on (or on to off) when it is called. This COLLIDER_EVENT does the actual work of toggling Action Group 1.

Animation

  ANIMATION_PLAYER
  {
      name = Button press animation
      animation = pb_PushAnim
      animationSpeed = 1.0

Next up is the ANIMATION_PLAYER node. An Animation Player plays an animation from beginning to end, or from end to beginning, depending on the state of a variable. When the variable transitions from false to true, the animation is played forwards. When the variable transitions from true to false, the animations is played in reverse.

In this example, the ANIMATION_PLAYER node is named "Button press animation". MAS will find the animation named "pb_PushAnim" in the prop, and it will play that animation at a speed of 1.0, or 100% of the speed stored in the prop.

    variable = fc.GetActionGroup(1)
  }

The variable that controls the animation is fc.GetActionGroup(1). Referring to the MASFlightComputerProxy documentation, we see that this method returns the 1 if the selected action group is "on", and 0 if it is "off". Since there is no range field in this node, the variable operates in Boolean Mode, as opposed to Threshold Mode. Boolean mode is the preferred configuration for variables, since the only thing that a node does when it receives an update is determine if the variable is greater than 0 or not. Any positive value is treated as "true", while any negative value (or zero) is treated as "false".

Threshold Mode works in a similar way, but it tests to see if a variable falls between the two ends of a specified range. Threshold Mode is useful when there's a range of valid values to worry about.

Text

The contents of the text node are rearranged in this example to group similar components together for easier explanation.

  TEXT_LABEL
  {
    name = Top Legend
    transform = Legend_Upper

Here, we see the TEXT_LABEL node is named Top Legend, and the transform in the model where this text will be attached is named Legend_Upper.

    font = Liberation Sans
    style = Bold
    fontSize = 3.9
    lineSpacing  = 0.9
    alignment = Center
    anchor = MiddleCenter

These entries in the config file tell MAS, in part, how to display the text in question. The prop wants to use the Liberation Sans Bold typeface, with a size of 3.9. The ASET Props modular push button documentation provides guidelines on sizes to use, but it's a good idea to see how those sizes look for a particular application.

MAS includes a few Fonts for text, and it can load any OS font installed on the player's computer. If a requested font can not be found, MAS will fall back to using the Liberation Sans regular font.

MAS does not use TMPro, and it does not support TMPro fonts.

The line spacing of 0.9 means that multi-line text will have a slightly smaller row-to-row spacing than what the typeface says it should use. The text is center justified (alignment = Center), and the anchor point for the text is the middle (vertically) center (horizontally) of the text. There are a variety of options for the anchor, as explained in the entry for TEXT_LABEL.

    emissive = active
    activeColor = COLOR_MOARdV_ActiveBacklightText
    passiveColor = COLOR_MOARdV_UnlitText

The emissive field controls whether the displayed text has an emissive color or a diffuse color. Emissive text "glows", allowing it to be read when there is little ambient light. In this case, the emissive field is set to active, which means that the text is emissive any time the controlling variable is greater than minimum value of its range.

The passive color, which is used when the text is not emissive, is the color assigned to COLOR_MOARdV_ActiveBacklightText. This color is a named color defined in a config file (in this case, in MOARdV/Props/MAS_ColorTable.cfg). The advantage of named colors is two-fold: first, it allows props to use a common palette of colors. If the prop author wants to change those colors, only one entry in one file has to change. Second, each command pod config file can override global colors in MASFlightComputer, so if an IVA maker wants to use existing props, but wants different colors, all the IVA maker has to do is add overrides to the command pod's config file.

As should be clear, the active color is the color MAS displays when the controlling variable is in range (active). Speaking of which...

    variable = fc.Conditioned(fc.ActionGroupHasActions(1) * fc.GetPersistentAsNumber("Backlight"))
    blend = true
    range = 0, 1

We'll go over variable in a moment.

range tells MAS that the range of values that we care about falls between 0 and 1. Both ends of the range may be a number, as we have here, or a variable. For instance, if we wanted a variable to be in range between the altitudes of 0 (sea level) and the top of the atmosphere, we could use range = 0, fc.AtmosphereTop().

Because we have blend = true, MAS is configured to handle the variable in Blend Mode, as opposed to Threshold Mode. This means that MAS will clamp the variable to range, and it will smoothly interpolate between the values. A blend can be used for controlling things altimeter hands, or throttle lever position, or, as in this case, lamp brightness.

variable demonstrates a significant improvement over RPM's variable handling. First, here's what the in-game experience is like: If the player has assigned any actions to Action Group 1, then this caption will get brighter and dimmer as the player adjusts the instrument backlight. If the player did not assign actions to AG1, or the parts that the actions were assigned to are no longer on the craft (dropped in a previous stage, for instance), then the caption's backlight never switches on. Even if the backlight is on, it can flicker on and off if the craft is undergoing a high-gee event, provided the IVA maker configured the MASFlightComputer to respond to those events. The backlight could also go out if the batteries were drained.

How does the IVA maker do this?

First, fc.ActionGroupHasActions(1) returns 1 if there are any actions assigned to AG1. It returns 0 otherwise. Second, fc.GetPersistentAsNumber("Backlight") returns whatever is in the persistent variable named "Backlight", converted to a number. When you're making an IVA and configuring a variable entry, always use fc.GetPersistentAsNumber. fc.GetPersistent will return the name of the persistent if it is uninitialized, and it will return a text string instead of a number if the persistent variable was stored as text. Either of those situations will cause an exception (which MAS will trap) that results in parts of the IVA props not working.

By multiplying fc.ActionGroupHasActions(1) by fc.GetPersistentAsNumber("Backlight"), we have a way to evaluate a compound condition: if AG1 has actions AND the backlight is on, then we'll show the backlight.

The expression fc.ActionGroupHasActions(1) * fc.GetPersistentAsNumber("Backlight") is passed to fc.Conditioned(). fc.Conditioned() is an "immersion" function. When the MASFlightComputer is configured to check power levels and g-loads, fc.Conditioned() does two things: First, it returns 0 if the vessels batteries are drained. Second, it will randomly return 0 if the vessel is subjected to excessive g-forces. In flight, this can be used to cause illuminated panels components to go dead if power runs out, and it will cause them to flicker during high acceleration (like when parachutes deploy, or on a hard landing). Obviously, fc.Conditioned() should not be used for every variable - the animation above, for instance, simulated a mechanical button push, and it would look weird if the button flipped repeatedly during parachute deployment.

    text = AG1
    oneshot = true
  }

And finally, we have the text we want to display: "AG1". The text may be plain text, as is here, or Formatted Rich Text, or text with a variable. Since, in this instance, the text is intended to be a label on a control panel, it is simple, static text. The oneshot = true is redundant in this case. It tells MAS that the text being displayed never changes, but MAS detects this automatically. The only time you really need to include oneshot is if the text you are displaying uses a variable, but you do not want the text to update during play. (I don't know why you would want to do that, but I am sure someone, somewhere, can come up with a scenario).

The second text label is nearly identical to the first one. It uses a different activeColor, but it treats its variable differently: there is no blend or range, so the variable is treated as a Boolean Mode variable, like the animations, which means it will either use activeColor if the value is positive, or it will use passiveColor if the value is zero or less. Like the previous text label, this label variable multiplies two values together in the variable - in this case, fc.GetActionGroup(1), which we discussed for the ANIMATION_PLAYER, and fc.ActionGroupHasActions(1). In practical terms, what this variable says is "if AG1 is on, and it has any actions, try to turn on the backlight for this text". If there are no actions in AG1, or AG1 is not switched on, then the light will not illuminate.

Hopefully, this long and drawn out description of a prop helps demonstrate how to configure a simple prop in MAS, and how to implement some interesting features that are not practical in RasterPropMonitor.