-
Notifications
You must be signed in to change notification settings - Fork 27
Prop Config
Prop Config is a tool designed to automate prop generation. It allows props to be collected into arbitrary groups and output into separate folders based on those groups. Within each group, Prop Config allows the definition of one or more Styles. A Style is a collection of MODEL elements, MODULE elements, and MASComponent elements that defines common traits found in props. Each element in a Style may be overridden in the prop config, and additional fields may be added by a prop to further customize it.
Prop Config uses XML files for input. One or more XML files may be provided on the command line, ie. PropConfig.exe myprops.xml otherprops.xml
.
One caveat about Prop Config: it is a fairly dumb tool. You can tell it to create invalid MASComponent nodes, and you can reference textures and models that don't exist. Making it smart enough to ensure the models and textures exist in the KSP installation, and that only valid MASComponent nodes are created would make it run slower, and it would mean that it must be updated at the same time that new features are added.
Starting with Prop Config v1.1.2, config files are written only if they did not already exist, or if the XML file is newer than an existing config file. To force Prop Config to regenerate all config files, regardless of their date stamp, include --force
as the first command-line parameter (eg, propConfig --force myprops.xml
.
NOTE: Capitalization matters. An element named model
is not the same as an element named MODEL
.
An example of the PropConfig XML file can be found in the repo under prop.xml.
The root node must be named PropConfig
:
<PropConfig>
...
</PropConfig>
A prop group is a collection of props that generally share common characteristics, such as ASET modular toggle switches. Each prop group should include a folder
attribute. This folder tells PropConfig where to write the config files for that prop group. If the folder
attribute is missing, PropConfig will write the group's configs in the current directory. PropConfig creates these folders relative to the current working directory if they are not a full path. A prop group may have any name for its element - PropConfig will display that name as part of its output, but the name has no effect on prop generation.
The folder
attribute may consist of a nested folder, eg "Switch_Toggle_Modular/aircraft". UNTESTED
<ToggleSwitch folder="Switch_Toggle_Modular">
...
</ToggleSwitch>
Each prop group contains two categories of child elements: styles and props.
A style consists of a set of configuration values that are shared by multiple props. The style provides a template that is used to build a prop's config file.
A prop defines an actual prop that will be created by PropConfig. It consists of a name, a style, and overrides for nodes in the style. Any field in a style may be overridden, and a prop may add additional fields to any prop. In addition, it may add whole new MASComponent or MODEL nodes to the config file.
A Style defines the common values used in a particular style of prop. It consists of one or more MODEL elements and any number of other elements that correspond to MASComponent nodes. The style defines the default values for all of these common nodes. A Prop may override these defaults. Each style must include a name
attribute so that PropConfig can identify it later. The name
may be any valid string, and it should not be duplicated within a prop group element. Duplicate style names may be used in different prop groups, however.
<style name="MOARdV Power Switch">
<MODEL>
...
</MODEL>
...
</style>
A Prop defines a single prop. It consists of a name element, a style element, zero or more MODEL override elements, and zero or more MASComponent override elements.
<prop>
<name>MAS.toggle_ARRT_Power</name>
<style>MOARdV Power Switch</style>
<startupScript>SwitchInitializer()</startupScript>
<COLLIDER_EVENT id="0">
<onClick>ARRT_TogglePower()</onClick>
</COLLIDER_EVENT>
<ROTATION>
<variable>fc.GetPersistentAsNumber("MAS_ARRT_On")</variable>
</ROTATION>
<TEXT_LABEL id="0">
<text>ON</text>
</TEXT_LABEL>
</prop>
The name
element may contain any name that can be used for both a file name and a KSP prop name. PropConfig will automatically replace any empty space
or period .
characters in the name with an underscore _
. In the example above, the prop is named "MAS.toggle_ARRT_Power". The config file is named "MAS_toggle_ARRT_Power.cfg".
The style
element names the Style that is used as a template for this prop. If an invalid style is selected, PropConfig will skip the part.
The startupScript
element is optional, and it may be omitted. When present, this element defines the startupScript that will be executed by the MASComponent when it loads and initializes upon entering the Flight scene. Refer to MASComponent for more information on the startup script.
Within a prop element, you may add new MODEL, MODULE, or MASComponents by specifying all of the fields that need to be in that config node. Make sure you use a unique id
for the new node. You may also delete nodes from the Style by adding the attribute delete="true"
:
<prop>
... // name, style, overrides omitted
<TEXT_LABEL id="1" delete="true" />
</prop>
In the above example, the TEXT_LABEL with id="1" in the prop's style is deleted. Using this approach may reduce the number of unique styles that need to be created.
Both Styles and Props may include MODEL elements. A MODEL element is converted into a MODEL node in the config file. Each MODEL element should include one model
element, an optional comment
element, and zero or more texture
elements. When more than one MODEL element is required in a prop (such as the ASET modular props), each MODEL element must be identified by an id
attribute, which is an integer number. If the id
attribute is omitted, it defaults to 0.
<MODEL id="1">
<comment>Basic toggle</comment>
<model>ASET/ASET_Props/Control/Switch_Toggle_Modular/models/TgglLever_Type_5</model>
<texture>Switch_TUMBLEDiffuse,ASET/ASET_Props/Control/Switch_Tumble/Switch_TUMBLEDiffuse</texture>
<texture>Tggl_Cap_Diffuse,ASET/ASET_Props/Control/Switch_Toggle_Modular/models/Tggl_Cap_White</texture>
</MODEL>
In this case, the MODEL has an id of 1, so if a Prop needs to edit this MODEL for some reason, it will also need to use <MODEL id="1">
.
The above MODEL element results in the following output in the config file:
// Basic toggle
MODEL
{
model = ASET/ASET_Props/Control/Switch_Toggle_Modular/models/TgglLever_Type_5
texture = Switch_TUMBLEDiffuse,ASET/ASET_Props/Control/Switch_Tumble/Switch_TUMBLEDiffuse
texture = Tggl_Cap_Diffuse,ASET/ASET_Props/Control/Switch_Toggle_Modular/models/Tggl_Cap_White
}
Although they are not included in this example, the MODEL options for scale
and rotation
may also be included in the XML script.
Both Styles and Props may include MODULE elements. A MODULE element is converted into a MODULE node in the config file. This element is useful for adding minor additional functionality to a given prop. It will support a simple prop module; that is, a module that does not contain nested nodes. The original impetus for this element was to support the Flight Director/Attitude Indicator prop, which uses a MASNavBall module in addition to a MASComponent.
Like the MODEL
element, a MODULE element contains multiple child elements. The MODULE element also supports the comment
child element like the MODEL
. Each child element is converted into a ConfigNode name/value pair. For example,
<MODULE>
<name>MASNavBall</name>
<navBallName>NavSphereRotGO</navBallName>
<range>-0.5, 0.5</range>
<maxAngleChange>180</maxAngleChange>
<variable>fdaiOffFlag(1)</variable>
</MODULE>
results in the ConfigNode
MODULE
{
name = MASNavBall
navBallName = NavSphereRotGO
range = -0.5, 0.5
maxAngleChange = 180
variable = fdaiOffFlag(1)
}
Any element in a Style or Prop that is named neither MODEL
nor MODULE
is treated as a MASComponent node. The name of the node in the config file is exactly the same as the name of the element. For instance, if the element is called <ROTATION>
, then the config file will include a node named ROTATION
. If more than one node of the same type is used in the prop, each element should be given a unique id
in the same way described for MODEL
. If an element does not have an id, it defaults to 0.
Like the MODEL
element, a MASComponent element contains multiple child elements. The MASComponent element also supports the comment
child element like the MODEL
. Each child element is converted into a ConfigNode name/value pair. For example,
<ROTATION>
<variable>fc.GetPersistentAsNumber("MAS_IMP_On")</variable>
</ROTATION>
will result in
MODULE
{
name = MASComponent
// Other nodes omitted
ROTATION
{
variable = fc.GetPersistentAsNumber("MAS_IMP_On")
}
}
in the prop config file.
Because the prop configuration script is an XML file, certain characters are illegal. These characters are part of MAS variable text processing, so some workarounds must be applied.
-
<=
and=>
may not be used for string formatting in a<text>
(or any other) element. Instead, use the standard C#{
and}
. Prop Config will convert them for you. -
&
may not be used, so$#$
should be used in prop configuration XML files to separate string formatting information from the variable list (for instance,{0:0.00} $#$ fc.Apoapsis() * 0.001
). -
<
may not be used for comparisons such asfc.Periapsis() < 0
. Use the substitution<
.>
usually works without a problem, but it may be good practice to use>
instead.
These restrictions will primarily affect <text>
and <variable>
. For instance, if the text field is supposed to be:
<=0:METHHH[@x8]mm[@x16]ss=> $&$ fc.OrbitPeriod()
you may use
{0:METHHH[@x8]mm[@x16]ss} $#$ fc.OrbitPeriod()
Future updates will hopefully provide easier substitution options.
Once you create the XML file to create props, run Prop Config from the command line:
PropConfig.exe myprops.xml
- Prop Config reads the XML file. If there are any XML errors, such as mismatched open/close elements, Prop Config will print the error and skip processing that XML file.
- Prop Config will process each prop group.
For each prop group,
- Prop Config reads all of the style elements. Any styles with duplicated names are reported, and only the first style encountered with that name is used.
Within each style,
- All elements with the name
MODEL
are processed as MODEL nodes. All other elements are processed as MASComponent nodes. - If more than one
MODEL
element is found in the style, and none have anid
attribute, or if multipleMODEL
elements are found with the sameid
attribute, Prop Config will report the duplicates. Only the firstMODEL
with a duplicatedid
will be used by the style. - Likewise, if more than one MASComponent element is found with matching ids, Prop Config will print a notice and keep only the first one.
Once the styles are processed, Prop Config will process all of the prop
elements.
- If the
name
contains any whitespace, Prop Config replaces those characters with_
underscore. Prop Config does not test for duplicate prop names. - Prop Config searches for the style named in the
style
element. If a style with that name is not found, Prop Config reports the error and it skips the prop. - Prop Config merges the style and the prop elements.
The merging process is as follows:
- For every
MODEL
in the style, if the prop does not also have an entry for aMODEL
with the sameid
, then the style'sMODEL
is used. If the prop has aMODEL
with the sameid
, then Prop Config will copy each field in theMODEL
from the style, unless the field exists in the prop as well, in which case the prop`s field is used. The same process is applied to MASComponent elements.
Since this is pretty confusing and important, and I did not explain it well, I'll provide an example:
<PropConfig>
<SomeProps folder="LameProps">
<style name="Example Style">
<MODEL id="0">
<comment>Unchanged Model</comment>
<model>Props/Part1</model>
</MODEL>
<MODEL id="1">
<comment>Edit Model</comment>
<model>Props/Part2</model>
</MODEL>
<MODEL id="2">
<comment>Add To Model</comment>
<model>Props/Part3</model>
</MODEL>
<MODEL id="3">
<comment>Multiple texture model</comment>
<model>Props/Part3</model>
<texture>diffuse,Props/otherDiffuse</texture>
<texture>emissive,Props/otherEmissive</texture>
</MODEL>
</style>
<prop>
<name>UselessProp</name>
<style>Example Style</style>
<MODEL id="1">
<model>Props/Part2a</model>
</MODEL>
<MODEL id="2">
<texture>diffuse,Props/differentDiffuse</texture>
</MODEL>
<MODEL id="3">
<comment>Oops - can't edit multiple textures</comment>
<texture>diffuse,Props/differentDiffuse</texture>
</MODEL>
</prop>
</SomeProps>
</PropConfig>
The above XML will create a prop config file LameProps/UselessProp.cfg. The file will look like
PROP
{
name = UselessProp
// Unchanged Model
MODEL
{
model = Props/Part1
}
// Edit Model
MODEL
{
model = Props/Part2a
}
// Add To Model
MODEL
{
model = Props/Part3
texture = diffuse,Props/differentDiffuse
}
// Oops - can't edit multiple textures
MODEL
{
model = Props/Part3
texture = diffuse,Props/differentDiffuse
texture = diffuse,Props/differentDiffuse
}
MODULE
{
name = MASComponent
}
}
It's not an interesting prop, since there are no components under MASComponent. But it illustrates the merging system.
One thing to watch out for, especially for MODEL texture fields: there is no way to distinguish between duplicated fields in a style. If a style has a MODEL with multiple texture
fields, such as the Part3 MODEL node above, and a prop edits the texture
field, all of the style's texture
fields are changed. If you need to have MODEL nodes with multiple, different textures, you will have to put each one in its own, separate style.