You can now override specific lines in a ConfigNode (what the game's code calls the format inside a .cfg file), on a line-by-line basis, and you don't have to specify lines that you don't want to change. The new format looks like this:
Single Part and editing format
@PART[SomePart] // change Some PART
{
@mass = 0.625 // change SomePart's mass to 0.625
@description = SomePart: now uses Xenon!@MODULE[ModuleEngines] // change SomePart's ModuleEngines MODULE
{
@maxThrust = 2.25 // change maxThrust to 225@PROPELLANT[LiquidFuel] // change its LiquidFuel propellant
{
@name = XenonGas // use XenonGas instead
@ratio = 1.0 // change the ratio
}
!PROPELLANT[Oxidizer] {} // remove the Oxidizer propellant completely.
}RESOURCE // add a new resource to this part
{
name = ElectricCharge
amount = 100
maxAmount = 100
}
}
Let's break this down.
If you make a .cfg file with an entry that looks like this:
PART
{
name = myPart
...(stuff)
}
you're defining a new part. Then, if another .cfg file somewhere does this:
@PART[myPart]
{
...(stuff)
}
That says "at the PART named myPart, add the following additional stuff..."
There's two ways you can do this:
1. @PART[myPart] {...} is for plugin developers, who need to add new modules and resources to stock parts.
2. @PART[myPart]:Final {...} is for "tweakers", who want to release a set of reconfigurations of other peoples' parts that changes gameplay, or personal patch. The change with :Final are applied after all others are done.
inside the curly brackets, you have several options:
@foo = <new value> changes foo to <new value>.
!foo = DELETE deletes foo completely.
foo = <value> creates a new variable called 'foo'; if there was already a variable called 'foo', now there's two of them.
if there are two or more variables called foo, you can refer to them like this:
@foo,0 = <...> finds the first one (this is the same as @foo = <...>)
@foo,1 = <...> finds the second one, @foo,2 = <...> finds the third, and so on.
The same thing works for !foo,0 etc.
@NODE[foo] {...} modifies the node which has type 'NODE' and name = foo. 'NODE' is a MODULE {} or a RESOURCE {} or a PROP {} or something like that.
!NODE[foo] {} deletes node foo completely.
NODE {
name = foo
...(stuff)
} creates a new node of type NODE. If there was already a node of type 'NODE', now there's two of them.
for nodes, instead of using a numeric index to identify between multiple nodes, you search by its <tag = ...> line. So if you do
@NODE[foo,bar] {...}
that will find and modify the first node that was defined like this:
NODE
{
name = foo
tag = bar
}
Right now 'tag' isn't being used by anything, but in the future if you need to add multiple nodes, adding a 'tag' field isn't a bad idea. It'd look something like this:
PART
{
name = EngineSABRE
module = Part
...
MODULE
{
name = ModuleEngines
tag = Atmosphere
...
}
MODULE
{
name = ModuleEngines
tag = Vacuum
}
}
and then someone could access the second node by going:
@PART[EngineSABRE]
{
@MODULE[ModuleEngines,Vacuum]
{
...
}
}
Multiple Parts
You can also apply a change to multiple part.
Use a wildcard search for all "name" inside brackets :
All PART whose name start with "B9_"
@PART[B9_*]
{
...(stuff)
}
Search for specific module
All PART who have a ModuleEngines
@PART[*]:HAS[@MODULE[ModuleEngines]]
{
...(stuff)
}
will search for part that looks like :
PART
{
MODULE
{
name = ModuleEngines
}
}
Or the absence of a module
All PART who have NO ModuleCommand
@PART[*]:HAS[!MODULE[ModuleCommand]]
{
...(stuff)
}
Search for properties
All PART whith "category = Utility"
@PART[*]:HAS[#category[Utility]]
{
...(stuff)
}
will search for part that looks like :
PART
{
category = Utility
}
Or the absence of a properties
All PART whithout TechRequired
@PART[*]:HAS[~TechRequired[]]
{
...(stuff)
}
Search for module with a specific configuration
All PART who have a ModuleEngines using XenonGas as a propelant (space for clarity)
@PART[*]:HAS[ @MODULE[ModuleEngines]:HAS[ @PROPELLANT[XenonGas] ] ]
{
...(stuff)
}
Combine search
All PART who have a ModuleEngines and have a SolidFuel ressource (space added for clarity)
@PART[*]:HAS[ @MODULE[ModuleEngines] , @RESOURCE[SolidFuel] ]
{
...(stuff)
}
It works at lower level too :
All PART who have a ModuleEngines using XenonGas and ElectricCharge as a propelant (space added for clarity)
@PART[*]:HAS[ @MODULE[ModuleEngines]:HAS[ @PROPELLANT[XenonGas] , @PROPELLANT[ElectricCharge] ] ]
{
...(stuff)
}
Use wildcard search at lower level
All PART without ElectricCharge as a ressource but with any other.
@PART[*]:HAS[!RESOURCE[ElectricCharge],@RESOURCE[*]]
{
...(stuff)
}
Some usefull exemples
Add a tech level to all PART who don't have any
@PART[*]:HAS[~TechRequired[]]:Final
{
TechRequired=advScienceTech
}
Add the Mechjeb module to all command pods who don't have it
@PART[*]:HAS[@MODULE[ModuleCommand],!MODULE[MechJebCore]]:Final
{
MODULE
{
name = MechJebCore
}
}
All the exemple use PART but it works on other nodes too :
@EXPERIMENT_DEFINITION[*]:HAS[#id[gravityScan]]
{
@baseValue = 5
@scienceCap = 10
}
You can also search the old threads for use case :
http://forum.kerbalspaceprogram.com/threads/31342-0-20-ModuleManager-1-3-for-all-your-stock-modding-needs
http://forum.kerbalspaceprogram.com/showthread.php/41616-Extension-for-ModuleManager-with-wildcard-and-conditional-v0-2-24-july-2013
Change for v2.x
New features:
MATH!
@PART[*]:FOR[Realism] {
@mass *= 0.25
@maxTemp -= 500
@scale += 2
}
PROPER INDEXING!
@PART[*]:HAS[MODULE[MultiModeEngine]]:FOR[Pony] {
@MODULE[ModuleEngineFX],1 {
@PROPELLANT[Oxidizer]
{
@name = LiquidOxygen
}
}
}
PER-MOD PARSE PASSES!
@PART[fuelTank]:BEFORE[RealFuels]
{
@RESOURCE[LiquidFuel] {
@amount *= 5
@maxAmount *= 5
}
}
@PART[fuelTank]:AFTER[RealFuels]
{
!RESOURCE[LiquidFuel] {}
!RESOURCE[Oxidizer] {}
}
DEPENDENCY CHECKING!
@PART[fuelTank]:AFTER[RealFuels]:NEEDS[RealSolarSystem,!RealKerbalSystem]
{
@scale *= 4;
}
If it detects a loaded DLL, it assumes it's a mod and creates a :BEFORE, :FOR and :AFTER pass for it.
If you use underscores to specify your mod's version in the filename, ModuleManager will regrettably think that these are part of the filename, because I can't tell the difference between "MyMod_1_3_6" and "Mod_1337s_Cool_Stuff". On the other hand, if you use periods to specify your mod's version, then "MyMod.1.3.6" will correctly be identified as "MyMod".
This means that there will always be the following passes:
:FIRST
:BEFORE[Assembly-CSharp]
:FOR[Assembly-CSharp]
:AFTER[Assembly-CSharp]
:BEFORE[ModuleManager]
:FOR[ModuleManager]
:AFTER[ModuleManager]
:FINAL
Specifying ':FIRST' is optional; I just named the 'main' pass so that the log file is clearer.
If your mod includes a DLL put all your MM patch nodes in the :FOR[yourMod] pass.
If your mod does not include a DLL, then pick a name for your mod THAT DOES NOT CONFLICT WITH ANY OTHER MOD'S DLL, and then put all your MM patch nodes in the :FOR[yourMod] pass.
If you do this, other mods can use :BEFORE[yourMod] and :AFTER[yourMod] to politely modify things furthr at the correct sequence.
The following parameters are currently implemented:
:BEFORE[ModName] - execute this patch BEFORE ModName executes its patches.
:FOR[ModName] - I am ModName, and these are my patches.
:AFTER[ModName] - execute this patch AFTER ModName executes its patches.
:NEEDS[ModName1] - execute this patch only if ModName1 is installed.
:NEEDS[!ModName2] - do not execute this patch if ModName2 is installed.
You can combine NEEDS nodes like this:
:NEEDS[ModName1, !ModName2]
You can match subnodes in one of seven ways:
@NODE[name] // will wildcard match name (so you can do ModuleEnginesFX or ModuleEngines*), and apply itself to the first NODE it finds.
@NODE[name],index // will wildcard match name, and apply itself to the indexth NODE it finds.
@NODE[name],* // will wildcard match name, and apply itself to ALL matching NODEs.
@NODE[name]:HAS[criteria] // will wildcard match name and apply itself to all matching NODEs which also meet the :HAS criteria
@NODE:HAS[criteria] // will apply itself to all matching NODEs which also meet the :HAS criteria
@NODE,index // will apply itself to the indexth NODE it finds
@NODE,* // will apply itself to ALL NODEs
These apply to @, ! and % nodes. $ nodes are necessarily single-application, and thus will always apply to the first node they find.
Edited by sarbian