SkyFall2489 Posted June 4, 2022 Share Posted June 4, 2022 (edited) IMPORTANT: PAGE UNDER CONSTRUCTION, NOT FINAL Before I begin: 0. I'm not really planning to finish this OP here. I got my information primarily from the official documentation here, this was primarily an attempt to provide examples and better explanations. It's actually been a year or two since I actually wrote an MM patch myself. 1. This is my own idea, it is in no way official, nor affiliated with any developer of ModuleManager. 2. I thought it would be helpful. 3. No one told me not to. 4. This is for you, @Ooglak Kerman. And all the others who wished to manage their modules, but could not. So, I figured that many KSP players who use a lot of mods may not exactly have the mods just the way the want. Or maybe a budding modder wants to learn some stuff about CFG files. Part 1: What is Module Manager? ModuleManager is a mod created by @ialdabaoth and currently maintained by @sarbian. A large portion of KSP's data are stored in text files with the .cfg extension. This includes resource definitions, part functionality, science flavor text and gains, and much, much, more. Sometimes, a mod may need to change this existing data as well as adding it's own. That is where Module Manager comes in to help. With ModuleManager, the text in CFG files can change what is actually loaded into the game from what is in the other files. For example, Snacks, a life-support mods, adds some life support supplies to all crewed pods using ModuleManager. THIS GUIDE WILL NOT TELL YOU HOW TO INSTALL MODULE MANAGER. Part 2: The Node Structure The CFG files are set up into a hierarchy of nodes. These nodes contain fields, where data is, and other nodes. Let's take a look at the definition of the Liquid Fuel resource. Spoiler RESOURCE_DEFINITION { name = LiquidFuel displayName = #autoLOC_500999 //#autoLOC_500999 = Liquid Fuel abbreviation = #autoLOC_6002095 //#autoLOC_6002095 = LF density = 0.005 unitCost = 0.8 hsp = 2010 flowMode = STACK_PRIORITY_SEARCH transfer = PUMP isTweakable = true volume = 5 RESOURCE_DRAIN_DEFINITION { isDrainable = true showDrainFX = true drainFXPriority = 7 drainForceISP = 5 drainFXDefinition = gasDraining } } At the top, in caps, we see "RESOURCE_DEFINITION". This is the type of node. As it does not exist within another node, we call it a top-level node. Within it, are some other nodes and fields. First, is the "name" field. This is not what is displayed, it is an internal name. It is very important, however, for selecting that node for later editing. It also has some other functions for things like functionality. You may notice, that in the displayName and abbreviation fields, there is some #autoLOC_<numbers>. This uses the localization system, displaying a different value based on what language is selected for the game. This will be later discussed further. So, we have those fields, but we also have the "RESOURCE_DRAIN_DEFINITION" node, with its own fields inside. There could be other nodes inside that, and you can go on forever. Part 3: Selecting a Node for Editing At the top of the previous example, we saw the line "RESOURCE_DEFINITION". In front of that, we could have put a few thing, and behind it, we could have put a lot more things. If, at the front, we had an "@" character, we could select a RESOURCE_DEFINITION for editing. if we had a "+" character, it would make a copy and let us edit that. the "!" and "-" characters delete the selected node, but you still have to specify a little something. More detail on this later. Finally, the "%" character edits something if it already exists, and if not, creates it. At the back, we could have put some square brackets. Inside those brackets, we could select a name of the specific node to edit, as there are lots of nodes of the same type. The name is the same as the "name" field specified in the initial creation of the node. You can put the "*" character inside the name, and it will represent any number of any characters. So, "abc*" would select "abc1", "abc2", "abcde", but not "ab3". Additionally, the "?" character can be any one random character. If you want to select every part, you can put just the "*" character into the square brackets. After the square brackets, we can put a comma and then a number, or some other selectors. The numbers indicate which node to edit, if the node they are in have multiple with the same name. The selectors are: HAS, NEEDS, FOR, BEFORE, AFTER, FIRST, and FINAL. They will be discussed in more detail later. After each of these words, there is some stuff in square brackets, and then a colon between everything. Here's an example, from the Blueshift thread: Spoiler @PART[wbiS3WarpCore,wbiS3WarpEngine,wbiMk2WarpCore,wbiS2WarpCore] { @MODULE[WBIWarpEngine] { @warpSpeedSkillMultiplier = 0.6 } } Let's look at each line. First, it edits the parts with a specific set of nodes. Curly braces indicate that we are going a level in. Then, we edit a module node with the name "WBIWarpEngine". Again, curly braces. Finally, we edit the warpSpeedSkillMultiplier field to be 0.6. Remember to make sure that each opening curly brace is paired with a closed curly brace. This is one of the most common mistakes! Part 4: Selecting Fields Fields also need to have the selection character at the front, and can have the other selectors or numbers applied to them. Oh, and the number can be the star symbol to select all of them. If there is no number, the first one is used. These go before the new value, like this: @WhatEverThisIs, 3:NEEDS[xyz] = abc Part 5: Other Selectors There are a few things you can put after each selected node or field: 1. HAS HAS will only make the patch apply to parts with some condition. After the word goes a set of square brackets. Inside, you can put: <character><node/fieldType>[<name>]. The character can be @, # or !. If the character is @, then the patch will only run on parts with that node. If the character is !, then the patch will only run on parts without that node. The # character will be covered later, when we get to variables. The node type should be self-explanatory. is it a PART? MODULE? RESOURCE? The name is just the same as the name field when selecting a part. 2. FOR This one's simple, a mod name goes in the box. This lets Module Manager know that the patch is for that mod. 3. NEEDS For this selector, inside the box goes the name of a mod. The patch will only run if that mod is installed. The ! character can be used to make the patch only run if the mod is not installed, and the | character acts like a logical OR gate, so either of two mods can be installed and the patch will run. To determine whether a mod exists: A. if there is a patch, that runs at all, with the FOR selector carrying a name that is the same as what is in the NEEDS box B. Or, if there is a folder in GameData with the name you are looking for. 4-5. BEFORE and AFTER Mod names go in the box. These make the patch run before or after patches with FOR selectors of the mod name. Useful to avoid pouring the drink before getting out a cup, so to speak. 6-7. FIRST and FINAL Simply put, these just make the patches run before or after everything else. No square brackets needed. COMMUNITY UPDATING As it seems others have begun to add things to this, I will quote them here in the OP. Thanks, everyone! On 6/5/2022 at 7:06 AM, Aelfhe1m said: Contributions for this thread: Selecting Nodes - or how to use :HAS Consider the following nodes partially extracted from the stock files: Reveal hidden contents PART { name = cupola CrewCapacity = 1 TechRequired = commandModules // snip extra fields MODULE { name = ModuleCommand minimumCrew = 1 } RESOURCE { name = ElectricCharge amount = 200 maxAmount = 200 } MODULE { name = ModuleScienceExperiment experimentID = crewReport interactionRange = 2 // snip } // snip } EXPERIMENT_DEFINITION { id = crewReport title = #autoLOC_501009 //#autoLOC_501009 = Crew Report baseValue = 5 scienceCap = 5 dataScale = 1 requireAtmosphere = False situationMask = 63 biomeMask = 7 RESULTS { default = #autoLOC_501258 //#autoLOC_501258 = You record the crew's assessment of the situation. KerbinSrfLandedLaunchpad = #autoLOC_501259 //#autoLOC_501259 = We don't seem to be moving very fast right now. // many more results } } How would we select the cupola part to change it? The most common way you will see this done in MM patches is @PART[cupola] This is actually a special shortcut for @PART:HAS[#name[cupola]] Because it is a shortcut for selecting nodes based on their name fields it won't work for nodes that don't have a name field like the EXPERIMENT_DEFINITION node in the above example. To select those you need to identify a field with a useful value and then explicitly refer to it in the HAS clause: @EXPERIMENT_DEFINITION:HAS[#experiment_id[crewReport]] You can also select nodes based on whether or not they contain a certain subnode. For example the cupola PART node above has several MODULE subnodes (as do almost all PART nodes), so the following patch would affect it: @PART:HAS[@MODULE[ModuleCommand]] This selector will affect every PART node that has at least one MODULE subnode that has a name field with the vale "ModuleCommand" (remember [X] is a shortcut for :HAS[#name[X]]) We can also multiple conditions. e.g. @PART[*]:HAS[@MODULE[ModuleCommand]],RESOURCE[ElectricCharge]) This breaks down as - select all PART nodes that have a name field with any value (* is a wildcard), and a MODULE subnode with a name field containing the value "ModuleCommand" and a RESOURCE subnode with a name field with the value "ElectricCharge". In this example the "," represents AND. MM also allows you to use "&" for AND so this could also be written as @PART[*]:HAS[@MODULE[ModuleCommand]]&RESOURCE[ElectricCharge]) Which you choose to use is a matter of personal preference as they will behave identically. Unfortunately MM only recognises OR in a limited number of places. You can use it inside the name shortcut or in a :NEEDS clause but it won't work inside of a :HAS clause. So the following will work (MM uses "|" to mean OR) @PART[cupola|Mark1Cockpit] @PART:NEEDS[modA|modB] but this WON'T work: @PART:HAS[@RESOURCE[ElectricCharge|MonoPropellant]] You would need to write separate patches for each resource type @PART:HAS[@RESOURCE[ElectricCharge]] {} @PART:HAS[@RESOURCE[MonoPropellant]] {} b. Choosing which subnodes to edit :HAS is also used within the contents of a patch filter which subnodes are affected by a given change. Suppose for example we wanted to changed the minimum number of crew required to operate a cupola in the above PART example. First you would use a selector to choose the cupola PART node, then inside the contents of the patch you would use another selector to choose the ModuleCommand MODULE subnode: @PART[cupola] { @MODULE[ModuleCommand] { @minimumCrew = 0 } } Because both PART and MODULE have name fields we can use the shorthand [X] form here. We could also have written out the patch in the full form: @PART:HAS[#name[cupola]] { @MODULE:HAS[#name[ModuleCommand]] { @minimumCrew = 0 } } but that takes more typing. If a subnode does not have a name field or there are multiple subnodes with the same value in the name field then you will need to use a HAS to identify the specific subnode based on some other unique value. For example suppose we have a part with two experiments - a temperatureScan and a barometerScan. This would look like: Reveal hidden contents PART { name = foo ... MODULE { name = ModuleScienceExperiment experimentID = temperatureScan experimentActionName = Log Temperature } MODULE { name = ModuleScienceExperiment experimentID = barometerScan experimentActionName = Log Pressure Data } } To change the temperatureScan's action name we would use: @PART[foo] { @MODULE[ModuleScieneExperiment]:HAS[#experimentID[temperatureScan]] { @experimentActionName = Take the temperature } } Notice how we first use :HAS[@MODULE[ModuleScienceExperiment] to identify that we want to affect ModuleScienceExperiment subnodes then we use an extra :HAS[#experimentID[temperatureScan]] sub-clause to narrow this down to just the MODULES with an experimentID field with the value "temperatureScan". NOTE we use @ when selecting based on subnodes and # to select based on fields If instead of only modifying the foo PART, we wanted to modify all PARTs that had a temperatureScan experiment MODULE then we would repeat the selector on the main node as well: @PART:HAS[@MODULE[ModuleScienceExperiment]:HAS[#experimentID[temperatureScan]]] { @MODULE[ModuleScieneExperiment]:HAS[#experimentID[temperatureScan]] { @experimentActionName = Take the temperature } } c. Selecting nodes that do not match a specific criterium Suppose we want to select all PART nodes that do not have a "bulkheadProfile" field: @PART:HAS[~bulkheadProfile[]] or all PART nodes that do not contain a MODULE subnode with name "ModuleCommand" @PART:HAS[!MODULE[ModuleCommand]] Notice that we use ~ when comparing fields and ! when comparing subnodes. d. Lists of fields Consider the following node RESULTS { message = One message = Two message = Three message = four } Suppose we wanted to change "Two" to "2". How would we do that? MM provides indexers to distinguish between identical options @RESULTS { @message,1 = 2 } Here the ",1" means the second message field (indexes count up from 0). You can also count from the end using negative indexes (",-1" = last value, ",-2" the second last and so on). The wildcard index ",*" means select ALL. We can also modify specific values within fields that have a list of values. For example: PART { name = foo node_stack_top = 0.0, .9, 0.0, 0.0, 1.0, 0.0, 2 } Suppose we want to change the .9 in the second value to 1.0 @PART[foo] { @node_stack_top[1] = 1.0 } The use of the index here will work for any field where the values are separated by commas. But what if the mod author had used a different separator? PART { name = foo otherfield = one|two|three|four } then we specify both the index and the separator @PART[foo] { @otherfield[2,|] = 3 } // result PART { name = foo otherfield = one|two|3|four } On 6/7/2022 at 9:02 AM, Aelfhe1m said: Making a patch only apply if a given mod is installed MM uses :NEEDS to mark that a patch should only be processed if the mods it needs are present. Each patch may only have a single NEEDS statement, but more than one mod can be specified within the needs. @PART:NEEDS[Tweakscale,ModularFuelTanks] The above example is a patch that would only be applied if BOTH Tweakscale and ModularFuelTanks were found in your GameData folder. Patches can also specify that they need a given mod NOT to be installed. @PART:NEEDS[!Tweakscale,!ModularFuelTanks] In this case the "!" character stands for NOT and is specifying that this patch should only be applied if Tweakscale and ModularFuelTanks are NOT present. You can of course mix and match both required and not required @PART:NEEDS[ModularFuelTanks,!Tweakscale] NEEDS can also recognise the "|" character to mean OR @PART:NEEDS[Tweakscale|ModularFuelTanks] This specifies that the patch should be applied if EITHER Tweakscale or ModularFuelTanks are installed (or both it's not exclusive) MM "knows" which mods are installed based on several tests: there is a DLL with the specified name somewhere inside the game's folder there is a folder with that name inside GameData there is another patch that has a :FOR[] statement containing the mod name there is special code inside the mod's DLL (assembly) to tell MM to add a name to its list of recognised mods So for example MM would believe that Tweakscale was installed if: it found a file called Tweakscale.dll it found a folder called GameData/Tweakscale it found another patch with :FOR[Tweakscale] if another mod had code inside its DLL that told MM Tweakscale existed MM includes a full list of all the mods it has found near the start of its logfile (KSP/Logs/ModuleManager/ModuleManager.log) NEEDS can also be used to check for subfolders within GameData @PART:NEEDS[TriggerTech/KerbalAlarmClock] Because of the way Module Manager detects mods it is VERY important to make sure that you completely delete a mods folder and all its related files when removing a mod. An empty folder will still cause MM to treat a mod as being present and it will apply any patches related to that mod which can lead to errors or unexpected behaviours. Also all patch authors need to be careful to use FOR correctly, since Module Manager uses this to detect the presence of a mod. ONLY the original mod should use FOR[Modname], ALL other mods or personal patches should use BEFORE, AFTER or NEEDS when referring to the name of another mod. Controlling the order in which patches are applied - FIRST, BEFORE, FOR, AFTER, LAST, FINAL When doing patching Module Manager starts by scanning through the GameData folder to find all the contained patches. Then: Nodes with no operator are loaded by the KSP GameDatabase first. Patches for modname values in NEEDS, BEFORE, AFTER that don't exist are removed. All patches with :FIRST are applied. All patches without an ordering directive (:FIRST, :BEFORE, :FOR, :AFTER, :LAST, :FINAL) are applied. For each recognised modname: All patches with :BEFORE are applied All patches with :FOR are applied All patches with :AFTER are applied All patches with :LAST are applied in order All patches with :FINAL are applied If two patches have the same priority in the above list then they will be sorted based on the filename of the files containing the patch or if they are in the same file then the order with which they appear in that file. Consider the following (very contrived) example: Module Manager will produce the following patch log for this KSP install: Reveal hidden contents [LOG 16:42:11.513] Log started at 2022-06-07 16:42:11.513 [LOG 16:42:12.729] Checking Cache [LOG 16:42:12.971] SHA generated in 0.236s [LOG 16:42:12.971] SHA = 31-D6-78-79-56-5F-51-6E-3C-70-41-04-16-B4-39-AC-47-A0-3B-5D-38-B9-8E-2F-56-CC-07-52-46-6D-76-49 [LOG 16:42:12.972] Pre patch init [LOG 16:42:13.173] compiling list of loaded mods... Mod DLLs found: Name Assembly Version Assembly File Version KSPAssembly Version SHA256 Assembly-CSharp 0.0.0.0 0.0.0.0 1.12 9be4953fe5b14018d2f62ce040c188da90f05d1e1fbaf1a2262775a63cd5607e ModuleManager 4.2.1.0 4.2.1.0 2.5 2eb4963f73a5255163953a270026f50a8f1a7dd27f4bb0dd69dd4699d0f2268b KSPSteamCtrlr 0.0.1.35 0.0.1.35 1675fa4fcb61d014eb1babe7eed703e7f76a1008537ee57ca7cec65cd9ff94ac Non-DLL mods added (:FOR[xxx]): ModA Handwavium Mods by directory (sub directories of GameData): ModB Squad SquadExpansion Mods added by assemblies: [LOG 16:42:13.174] Loading Physics.cfg [LOG 16:42:13.176] Extracting patches [LOG 16:42:13.198] Deleting root node in file ModB/MMPatches node: !RESOURCE_DEFINITION[newResource]:NEEDS[WarpDrive] as it can't satisfy its NEEDS [LOG 16:42:13.293] Applying patches [LOG 16:42:13.295] :INSERT (initial) pass [LOG 16:42:13.367] :FIRST pass [LOG 16:42:13.370] Applying update ModA/morepatches/@PART[HandwaviumTank]:FIRST to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.377] :LEGACY (default) pass [LOG 16:42:13.380] Applying copy ModA/morepatches/+PART[HandwaviumTank]:NEEDS[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.383] :BEFORE[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :FOR[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :AFTER[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :BEFORE[HANDWAVIUM] pass [LOG 16:42:13.383] Applying copy ModA/patches/+PART[HandwaviumTank]:BEFORE[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.384] :FOR[HANDWAVIUM] pass [LOG 16:42:13.384] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to ModB/Resrources/res.cfg/RESOURCE_DEFINITION[newResource] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[LiquidFuel] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Oxidizer] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[SolidFuel] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[MonoPropellant] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[XenonGas] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[ElectricCharge] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[IntakeAir] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[EVA Propellant] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Ore] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Ablator] [LOG 16:42:13.385] :AFTER[HANDWAVIUM] pass [LOG 16:42:13.385] :BEFORE[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :FOR[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :AFTER[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :BEFORE[MODA] pass [LOG 16:42:13.385] :FOR[MODA] pass [LOG 16:42:13.386] Applying update ModA/morepatches/@PART[fuelTankSmallFlat]:FOR[ModA] to Squad/Parts/FuelTank/Size1_Tanks/fuelTankT100.cfg/PART[fuelTankSmallFlat] [LOG 16:42:13.387] :AFTER[MODA] pass [LOG 16:42:13.387] :BEFORE[MODB] pass [LOG 16:42:13.387] :FOR[MODB] pass [LOG 16:42:13.387] :AFTER[MODB] pass [LOG 16:42:13.387] Applying copy ModA/patches/+PART[HandwaviumTank]:AFTER[ModB] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.388] :BEFORE[MODULEMANAGER] pass [LOG 16:42:13.388] :FOR[MODULEMANAGER] pass [LOG 16:42:13.388] :AFTER[MODULEMANAGER] pass [LOG 16:42:13.388] :BEFORE[SQUAD] pass [LOG 16:42:13.388] :FOR[SQUAD] pass [LOG 16:42:13.388] :AFTER[SQUAD] pass [LOG 16:42:13.388] Applying update ModB/MMPatches/@RESOURCE_DEFINITION[newResource]:AFTER[Squad] to ModB/Resrources/res.cfg/RESOURCE_DEFINITION[newResource] [LOG 16:42:13.388] :BEFORE[SQUADEXPANSION] pass [LOG 16:42:13.388] :FOR[SQUADEXPANSION] pass [LOG 16:42:13.388] :AFTER[SQUADEXPANSION] pass [LOG 16:42:13.389] :LAST[HANDWAVIUM] pass [LOG 16:42:13.389] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[LargeHandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[LargeHandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[SmallHandwaviumTank] [LOG 16:42:13.396] :FINAL pass [LOG 16:42:13.396] Done patching [LOG 16:42:13.397] Saving Cache [LOG 16:42:13.552] Saving cache [LOG 16:42:13.874] ModuleManager: 21 patches applied [LOG 16:42:13.874] Ran in 1.145s [LOG 16:42:13.907] Done! Notice for example that BEFORE[Handwavium],FOR[Handwavium] and AFTER[Handwavium] all occurred before FOR{ModA] but that LAST[Handwavium] occurred after all the BEFORE/FOR/AFTER passes (and before FINAL) Edited January 31 by SkyFall2489 Quote Link to comment Share on other sites More sharing options...
Ooglak Kerman Posted June 4, 2022 Share Posted June 4, 2022 Thanks for this @SkyFall2489. I'm working on a persistent patch of the settings.cfg for Blueshift that will have my preferred settings. When I get it sorted, I'll post it as a basic example. Quote Link to comment Share on other sites More sharing options...
Ooglak Kerman Posted June 4, 2022 Share Posted June 4, 2022 (edited) Here's one of the changes that I have been previously been doing by hand for each release of Blueshift. The file in question is Blueshift/Patches/StockParts.cfg Change from: @PART[ISRU]:NEEDS[!WildBlueTools,!FarFutureTechnologies] To: @PART[ISRU]:NEEDS[!FarFutureTechnologies] To effect this, do I need to include the entire module definition? Or can I modify just the NEEDS selector. Edited June 4, 2022 by Ooglak Kerman Quote Link to comment Share on other sites More sharing options...
Aelfhe1m Posted June 5, 2022 Share Posted June 5, 2022 2 hours ago, Ooglak Kerman said: To effect this, do I need to include the entire module definition? Yes. 2 hours ago, Ooglak Kerman said: Or can I modify just the NEEDS selector. You can't use MM to change the NEEDS of another patch. Quote Link to comment Share on other sites More sharing options...
Ooglak Kerman Posted June 5, 2022 Share Posted June 5, 2022 2 hours ago, Aelfhe1m said: Yes. You can't use MM to change the NEEDS of another patch. Thank you for the response on this. I'll have to do some trials and testing of what I've got in mind. Next question: Changing of templates. Specifically WildBlueIndustires/000WildBlueTools/Templates/ClassicStock/Production/OmniConverters.cfg If you go down to the OMNICONVERTER --> ConverterName = FusionPellets Processor INPUT_RESOURCE { ResourceName = Ore Ratio = 15 FlowMode = ALL_VESSEL } If you do the math, the amount of ore required for a single fusion pellet is quite high. I am looking to use MM to modify this. Can MM do this? Quote Link to comment Share on other sites More sharing options...
Aelfhe1m Posted June 5, 2022 Share Posted June 5, 2022 29 minutes ago, Ooglak Kerman said: If you do the math, the amount of ore required for a single fusion pellet is quite high. I am looking to use MM to modify this. Can MM do this? In principle to patch that template would be something like: // There is no name field on the OMNICONVERTER templates so use ConverterName field which looks unique // and replace space in value with wildcard @OMNICONVERTER:HAS[#ConverterName[FusionPellets?Processor]] { // again no name field so use ResourceName field instead @INPUT_RESOURCE:HAS[#ResourceName[Ore]] { @Ratio = 15 // new number here } } Although how WBT uses the templates may affect the end result. Quote Link to comment Share on other sites More sharing options...
Ooglak Kerman Posted June 5, 2022 Share Posted June 5, 2022 @Aelfhe1m Thank you for this. This gives me a starting point. The ease of making a complete clone of the game makes this sort of testing rather trivial - if not tedious. If it works, I'll be posting the results and the patch. I hope this topic will benefit others as well as myself. As I start to gain more insight into how the game works and can be modified, it has given new depth and enjoyment to it. Quote Link to comment Share on other sites More sharing options...
Aelfhe1m Posted June 5, 2022 Share Posted June 5, 2022 (edited) Contributions for this thread: Selecting Nodes - or how to use :HAS Consider the following nodes partially extracted from the stock files: Spoiler PART { name = cupola CrewCapacity = 1 TechRequired = commandModules // snip extra fields MODULE { name = ModuleCommand minimumCrew = 1 } RESOURCE { name = ElectricCharge amount = 200 maxAmount = 200 } MODULE { name = ModuleScienceExperiment experimentID = crewReport interactionRange = 2 // snip } // snip } EXPERIMENT_DEFINITION { id = crewReport title = #autoLOC_501009 //#autoLOC_501009 = Crew Report baseValue = 5 scienceCap = 5 dataScale = 1 requireAtmosphere = False situationMask = 63 biomeMask = 7 RESULTS { default = #autoLOC_501258 //#autoLOC_501258 = You record the crew's assessment of the situation. KerbinSrfLandedLaunchpad = #autoLOC_501259 //#autoLOC_501259 = We don't seem to be moving very fast right now. // many more results } } How would we select the cupola part to change it? The most common way you will see this done in MM patches is @PART[cupola] This is actually a special shortcut for @PART:HAS[#name[cupola]] Because it is a shortcut for selecting nodes based on their name fields it won't work for nodes that don't have a name field like the EXPERIMENT_DEFINITION node in the above example. To select those you need to identify a field with a useful value and then explicitly refer to it in the HAS clause: @EXPERIMENT_DEFINITION:HAS[#experiment_id[crewReport]] You can also select nodes based on whether or not they contain a certain subnode. For example the cupola PART node above has several MODULE subnodes (as do almost all PART nodes), so the following patch would affect it: @PART:HAS[@MODULE[ModuleCommand]] This selector will affect every PART node that has at least one MODULE subnode that has a name field with the vale "ModuleCommand" (remember [X] is a shortcut for :HAS[#name[X]]) We can also multiple conditions. e.g. @PART[*]:HAS[@MODULE[ModuleCommand]],RESOURCE[ElectricCharge]) This breaks down as - select all PART nodes that have a name field with any value (* is a wildcard), and a MODULE subnode with a name field containing the value "ModuleCommand" and a RESOURCE subnode with a name field with the value "ElectricCharge". In this example the "," represents AND. MM also allows you to use "&" for AND so this could also be written as @PART[*]:HAS[@MODULE[ModuleCommand]]&RESOURCE[ElectricCharge]) Which you choose to use is a matter of personal preference as they will behave identically. Unfortunately MM only recognises OR in a limited number of places. You can use it inside the name shortcut or in a :NEEDS clause but it won't work inside of a :HAS clause. So the following will work (MM uses "|" to mean OR) @PART[cupola|Mark1Cockpit] @PART:NEEDS[modA|modB] but this WON'T work: @PART:HAS[@RESOURCE[ElectricCharge|MonoPropellant]] You would need to write separate patches for each resource type @PART:HAS[@RESOURCE[ElectricCharge]] {} @PART:HAS[@RESOURCE[MonoPropellant]] {} b. Choosing which subnodes to edit :HAS is also used within the contents of a patch filter which subnodes are affected by a given change. Suppose for example we wanted to changed the minimum number of crew required to operate a cupola in the above PART example. First you would use a selector to choose the cupola PART node, then inside the contents of the patch you would use another selector to choose the ModuleCommand MODULE subnode: @PART[cupola] { @MODULE[ModuleCommand] { @minimumCrew = 0 } } Because both PART and MODULE have name fields we can use the shorthand [X] form here. We could also have written out the patch in the full form: @PART:HAS[#name[cupola]] { @MODULE:HAS[#name[ModuleCommand]] { @minimumCrew = 0 } } but that takes more typing. If a subnode does not have a name field or there are multiple subnodes with the same value in the name field then you will need to use a HAS to identify the specific subnode based on some other unique value. For example suppose we have a part with two experiments - a temperatureScan and a barometerScan. This would look like: Spoiler PART { name = foo ... MODULE { name = ModuleScienceExperiment experimentID = temperatureScan experimentActionName = Log Temperature } MODULE { name = ModuleScienceExperiment experimentID = barometerScan experimentActionName = Log Pressure Data } } To change the temperatureScan's action name we would use: @PART[foo] { @MODULE[ModuleScieneExperiment]:HAS[#experimentID[temperatureScan]] { @experimentActionName = Take the temperature } } Notice how we first use :HAS[@MODULE[ModuleScienceExperiment] to identify that we want to affect ModuleScienceExperiment subnodes then we use an extra :HAS[#experimentID[temperatureScan]] sub-clause to narrow this down to just the MODULES with an experimentID field with the value "temperatureScan". NOTE we use @ when selecting based on subnodes and # to select based on fields If instead of only modifying the foo PART, we wanted to modify all PARTs that had a temperatureScan experiment MODULE then we would repeat the selector on the main node as well: @PART:HAS[@MODULE[ModuleScienceExperiment]:HAS[#experimentID[temperatureScan]]] { @MODULE[ModuleScieneExperiment]:HAS[#experimentID[temperatureScan]] { @experimentActionName = Take the temperature } } c. Selecting nodes that do not match a specific criterium Suppose we want to select all PART nodes that do not have a "bulkheadProfile" field: @PART:HAS[~bulkheadProfile[]] or all PART nodes that do not contain a MODULE subnode with name "ModuleCommand" @PART:HAS[!MODULE[ModuleCommand]] Notice that we use ~ when comparing fields and ! when comparing subnodes. d. Lists of fields Consider the following node RESULTS { message = One message = Two message = Three message = four } Suppose we wanted to change "Two" to "2". How would we do that? MM provides indexers to distinguish between identical options @RESULTS { @message,1 = 2 } Here the ",1" means the second message field (indexes count up from 0). You can also count from the end using negative indexes (",-1" = last value, ",-2" the second last and so on). The wildcard index ",*" means select ALL. We can also modify specific values within fields that have a list of values. For example: PART { name = foo node_stack_top = 0.0, .9, 0.0, 0.0, 1.0, 0.0, 2 } Suppose we want to change the .9 in the second value to 1.0 @PART[foo] { @node_stack_top[1] = 1.0 } The use of the index here will work for any field where the values are separated by commas. But what if the mod author had used a different separator? PART { name = foo otherfield = one|two|three|four } then we specify both the index and the separator @PART[foo] { @otherfield[2,|] = 3 } // result PART { name = foo otherfield = one|two|3|four } Edited June 5, 2022 by Aelfhe1m Fixed example for | separator Quote Link to comment Share on other sites More sharing options...
Aelfhe1m Posted June 7, 2022 Share Posted June 7, 2022 Making a patch only apply if a given mod is installed MM uses :NEEDS to mark that a patch should only be processed if the mods it needs are present. Each patch may only have a single NEEDS statement, but more than one mod can be specified within the needs. @PART:NEEDS[Tweakscale,ModularFuelTanks] The above example is a patch that would only be applied if BOTH Tweakscale and ModularFuelTanks were found in your GameData folder. Patches can also specify that they need a given mod NOT to be installed. @PART:NEEDS[!Tweakscale,!ModularFuelTanks] In this case the "!" character stands for NOT and is specifying that this patch should only be applied if Tweakscale and ModularFuelTanks are NOT present. You can of course mix and match both required and not required @PART:NEEDS[ModularFuelTanks,!Tweakscale] NEEDS can also recognise the "|" character to mean OR @PART:NEEDS[Tweakscale|ModularFuelTanks] This specifies that the patch should be applied if EITHER Tweakscale or ModularFuelTanks are installed (or both it's not exclusive) MM "knows" which mods are installed based on several tests: there is a DLL with the specified name somewhere inside the game's folder there is a folder with that name inside GameData there is another patch that has a :FOR[] statement containing the mod name there is special code inside the mod's DLL (assembly) to tell MM to add a name to its list of recognised mods So for example MM would believe that Tweakscale was installed if: it found a file called Tweakscale.dll it found a folder called GameData/Tweakscale it found another patch with :FOR[Tweakscale] if another mod had code inside its DLL that told MM Tweakscale existed MM includes a full list of all the mods it has found near the start of its logfile (KSP/Logs/ModuleManager/ModuleManager.log) NEEDS can also be used to check for subfolders within GameData @PART:NEEDS[TriggerTech/KerbalAlarmClock] Because of the way Module Manager detects mods it is VERY important to make sure that you completely delete a mods folder and all its related files when removing a mod. An empty folder will still cause MM to treat a mod as being present and it will apply any patches related to that mod which can lead to errors or unexpected behaviours. Also all patch authors need to be careful to use FOR correctly, since Module Manager uses this to detect the presence of a mod. ONLY the original mod should use FOR[Modname], ALL other mods or personal patches should use BEFORE, AFTER or NEEDS when referring to the name of another mod. Controlling the order in which patches are applied - FIRST, BEFORE, FOR, AFTER, LAST, FINAL When doing patching Module Manager starts by scanning through the GameData folder to find all the contained patches. Then: Nodes with no operator are loaded by the KSP GameDatabase first. Patches for modname values in NEEDS, BEFORE, AFTER that don't exist are removed. All patches with :FIRST are applied. All patches without an ordering directive (:FIRST, :BEFORE, :FOR, :AFTER, :LAST, :FINAL) are applied. For each recognised modname: All patches with :BEFORE are applied All patches with :FOR are applied All patches with :AFTER are applied All patches with :LAST are applied in order All patches with :FINAL are applied If two patches have the same priority in the above list then they will be sorted based on the filename of the files containing the patch or if they are in the same file then the order with which they appear in that file. Consider the following (very contrived) example: Module Manager will produce the following patch log for this KSP install: Spoiler [LOG 16:42:11.513] Log started at 2022-06-07 16:42:11.513 [LOG 16:42:12.729] Checking Cache [LOG 16:42:12.971] SHA generated in 0.236s [LOG 16:42:12.971] SHA = 31-D6-78-79-56-5F-51-6E-3C-70-41-04-16-B4-39-AC-47-A0-3B-5D-38-B9-8E-2F-56-CC-07-52-46-6D-76-49 [LOG 16:42:12.972] Pre patch init [LOG 16:42:13.173] compiling list of loaded mods... Mod DLLs found: Name Assembly Version Assembly File Version KSPAssembly Version SHA256 Assembly-CSharp 0.0.0.0 0.0.0.0 1.12 9be4953fe5b14018d2f62ce040c188da90f05d1e1fbaf1a2262775a63cd5607e ModuleManager 4.2.1.0 4.2.1.0 2.5 2eb4963f73a5255163953a270026f50a8f1a7dd27f4bb0dd69dd4699d0f2268b KSPSteamCtrlr 0.0.1.35 0.0.1.35 1675fa4fcb61d014eb1babe7eed703e7f76a1008537ee57ca7cec65cd9ff94ac Non-DLL mods added (:FOR[xxx]): ModA Handwavium Mods by directory (sub directories of GameData): ModB Squad SquadExpansion Mods added by assemblies: [LOG 16:42:13.174] Loading Physics.cfg [LOG 16:42:13.176] Extracting patches [LOG 16:42:13.198] Deleting root node in file ModB/MMPatches node: !RESOURCE_DEFINITION[newResource]:NEEDS[WarpDrive] as it can't satisfy its NEEDS [LOG 16:42:13.293] Applying patches [LOG 16:42:13.295] :INSERT (initial) pass [LOG 16:42:13.367] :FIRST pass [LOG 16:42:13.370] Applying update ModA/morepatches/@PART[HandwaviumTank]:FIRST to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.377] :LEGACY (default) pass [LOG 16:42:13.380] Applying copy ModA/morepatches/+PART[HandwaviumTank]:NEEDS[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.383] :BEFORE[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :FOR[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :AFTER[ASSEMBLY-CSHARP] pass [LOG 16:42:13.383] :BEFORE[HANDWAVIUM] pass [LOG 16:42:13.383] Applying copy ModA/patches/+PART[HandwaviumTank]:BEFORE[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.384] :FOR[HANDWAVIUM] pass [LOG 16:42:13.384] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to ModB/Resrources/res.cfg/RESOURCE_DEFINITION[newResource] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[LiquidFuel] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Oxidizer] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[SolidFuel] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[MonoPropellant] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[XenonGas] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[ElectricCharge] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[IntakeAir] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[EVA Propellant] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Ore] [LOG 16:42:13.385] Applying update ModB/Resrources/res/@RESOURCE_DEFINITION:FOR[Handwavium] to Squad/Resources/ResourcesGeneric.cfg/RESOURCE_DEFINITION[Ablator] [LOG 16:42:13.385] :AFTER[HANDWAVIUM] pass [LOG 16:42:13.385] :BEFORE[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :FOR[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :AFTER[KSPSTEAMCTRLR] pass [LOG 16:42:13.385] :BEFORE[MODA] pass [LOG 16:42:13.385] :FOR[MODA] pass [LOG 16:42:13.386] Applying update ModA/morepatches/@PART[fuelTankSmallFlat]:FOR[ModA] to Squad/Parts/FuelTank/Size1_Tanks/fuelTankT100.cfg/PART[fuelTankSmallFlat] [LOG 16:42:13.387] :AFTER[MODA] pass [LOG 16:42:13.387] :BEFORE[MODB] pass [LOG 16:42:13.387] :FOR[MODB] pass [LOG 16:42:13.387] :AFTER[MODB] pass [LOG 16:42:13.387] Applying copy ModA/patches/+PART[HandwaviumTank]:AFTER[ModB] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.388] :BEFORE[MODULEMANAGER] pass [LOG 16:42:13.388] :FOR[MODULEMANAGER] pass [LOG 16:42:13.388] :AFTER[MODULEMANAGER] pass [LOG 16:42:13.388] :BEFORE[SQUAD] pass [LOG 16:42:13.388] :FOR[SQUAD] pass [LOG 16:42:13.388] :AFTER[SQUAD] pass [LOG 16:42:13.388] Applying update ModB/MMPatches/@RESOURCE_DEFINITION[newResource]:AFTER[Squad] to ModB/Resrources/res.cfg/RESOURCE_DEFINITION[newResource] [LOG 16:42:13.388] :BEFORE[SQUADEXPANSION] pass [LOG 16:42:13.388] :FOR[SQUADEXPANSION] pass [LOG 16:42:13.388] :AFTER[SQUADEXPANSION] pass [LOG 16:42:13.389] :LAST[HANDWAVIUM] pass [LOG 16:42:13.389] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[HandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[LargeHandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[LargeHandwaviumTank] [LOG 16:42:13.392] Applying update ModA/morepatches/@PART:HAS[@RESOURCE[newResource]]:LAST[Handwavium] to ModB/Resrources/res.cfg/PART[SmallHandwaviumTank] [LOG 16:42:13.396] :FINAL pass [LOG 16:42:13.396] Done patching [LOG 16:42:13.397] Saving Cache [LOG 16:42:13.552] Saving cache [LOG 16:42:13.874] ModuleManager: 21 patches applied [LOG 16:42:13.874] Ran in 1.145s [LOG 16:42:13.907] Done! Notice for example that BEFORE[Handwavium],FOR[Handwavium] and AFTER[Handwavium] all occurred before FOR{ModA] but that LAST[Handwavium] occurred after all the BEFORE/FOR/AFTER passes (and before FINAL) Quote Link to comment Share on other sites More sharing options...
SkyFall2489 Posted June 17, 2022 Author Share Posted June 17, 2022 Thanks for that, put it in the OP. Quote Link to comment Share on other sites More sharing options...
linuxgurugamer Posted July 26, 2022 Share Posted July 26, 2022 Hope someone can help with this. One of my mods has the following code: @PART[*]:HAS[!MODULE[ModuleCargoPart],@MODULE[ModuleInventoryPart],!MODULE[KerbalEVA]]:FINAL { MODULE { name = ModuleCargoPart packedVolume = -1 } MODULE { name = ModuleInventoryPart InventorySlots = #$/MODULE[ModuleInventoryPart]/InventorySlots$ packedVolumeLimit = #$/MODULE[ModuleInventoryPart]/packedVolumeLimit$ } MODULE { name = KSPPartVolumeModule } !MODULE[ModuleInventoryPart] {} } Turns out that at least one (and probably more) parts mods don't bother to put in the "packedVolumeLimit" value, so MM borks on that line since there is nothing to reference. Not being a MM guru, and busy with other stuff, I was hoping that someone could provide some code which would work if the packedVolumeLimit is missing Quote Link to comment Share on other sites More sharing options...
Aelfhe1m Posted July 26, 2022 Share Posted July 26, 2022 @linuxgurugamer You could pre-patch with a default value where an existing value is missing: @PART[*]:HAS[!MODULE[ModuleCargoPart],@MODULE[ModuleInventoryPart],!MODULE[KerbalEVA]]:FINAL { @MODULE[ModuleInventoryPart] { &packedVolumeLimit = 1 // & means add value only if not already present - you can use a different default if 1 isn't appropriate } } // rest of patch as before @PART[*]:HAS[!MODULE[ModuleCargoPart],@MODULE[ModuleInventoryPart],!MODULE[KerbalEVA]]:FINAL { MODULE { name = ModuleCargoPart packedVolume = -1 } MODULE { name = ModuleInventoryPart InventorySlots = #$/MODULE[ModuleInventoryPart]/InventorySlots$ packedVolumeLimit = #$/MODULE[ModuleInventoryPart]/packedVolumeLimit$ } MODULE { name = KSPPartVolumeModule } !MODULE[ModuleInventoryPart] {} } You could also use @MODULE[ModuleInventoryPart]:HAS[~packedVolumeLimit[]] on the first @PART clause to filter only for ModuleInventoryPart modules without packedVolumeLimit values but using the & makes that mostly redundant (might save a little processing time and reduce total patch count) Quote Link to comment Share on other sites More sharing options...
linuxgurugamer Posted July 26, 2022 Share Posted July 26, 2022 (edited) 39 minutes ago, Aelfhe1m said: @linuxgurugamer You could pre-patch with a default value where an existing value is missing: @PART[*]:HAS[!MODULE[ModuleCargoPart],@MODULE[ModuleInventoryPart],!MODULE[KerbalEVA]]:FINAL { @MODULE[ModuleInventoryPart] { &packedVolumeLimit = 1 // & means add value only if not already present - you can use a different default if 1 isn't appropriate } } // rest of patch as before @PART[*]:HAS[!MODULE[ModuleCargoPart],@MODULE[ModuleInventoryPart],!MODULE[KerbalEVA]]:FINAL { MODULE { name = ModuleCargoPart packedVolume = -1 } MODULE { name = ModuleInventoryPart InventorySlots = #$/MODULE[ModuleInventoryPart]/InventorySlots$ packedVolumeLimit = #$/MODULE[ModuleInventoryPart]/packedVolumeLimit$ } MODULE { name = KSPPartVolumeModule } !MODULE[ModuleInventoryPart] {} } You could also use @MODULE[ModuleInventoryPart]:HAS[~packedVolumeLimit[]] on the first @PART clause to filter only for ModuleInventoryPart modules without packedVolumeLimit values but using the & makes that mostly redundant (might save a little processing time and reduce total patch count) Thanks. The prepatch is what I need, just didn' t know how to do it. Edited July 26, 2022 by linuxgurugamer Quote Link to comment Share on other sites More sharing options...
HafCoJoe Posted July 26, 2022 Share Posted July 26, 2022 (edited) This guide is incredible! Especially useful to me is the directions on how to target nodes which don't have a name field. @Scatterer_planetsList:FOR[Spectra] { @scattererCelestialBodies { !Item:HAS[#celestialBodyName[Duna]] { } } } ^ This is the code I set up to delete the node with CelestialBodyName = Duna Edited July 26, 2022 by HafCoJoe Quote Link to comment Share on other sites More sharing options...
spaded1 Posted August 15, 2022 Share Posted August 15, 2022 Hey guys I'm trying to make a mod to make the cost of everything more real. So falcon 9 should cost 100mil to fly. Satellite contracts should pay 25mil not 25k. Extracting ore and landing should reward with millions in dollars. Here is my code - but the contracts don't seem to reflect the changes at all.... Can anyone help me out? ``` @CONTRACT_TYPE[*]:HAS[@rewardFunds]:FINAL { cash= $rewardFunds @rewardFunds = 200*cash } @Contracts:HAS[@Funds]:FINAL { cash= $rewardFunds @rewardFunds = 70*cash @Funds { @BaseReward *= 70 @BaseAdvance *= 70 } @BaseReward *= 70 @BaseAdvance *= 70 } @Contracts:HAS[*,@Funds]:FINAL { cash= $rewardFunds @rewardFunds = 200*cash @Funds { @BaseReward *= 70 @BaseAdvance *= 70 } @BaseReward *= 70 @BaseAdvance *= 70 } @PART[*] { @cost *= 150.0 } @RESOURCE_DEFINITION[*] { @unitCost *= 150.0 } Quote Link to comment Share on other sites More sharing options...
shoe7ess Posted October 4, 2022 Share Posted October 4, 2022 (edited) Hey all, I'm having a hell of a time with a specific part using an MM patch I've used for awhile that changes part attach rules so that all parts can be attached in any way (mostly to make parts have surface attach added to them). I have an MM patch that works on pretty much every part I can find except for the Davon Supply Mod refill part. I physically changed the part.cfg itself, as well as tried a plethora of different versions of the attach rules in using MM patches, but it still will not set the stack attach to "1". I have a workaround with NodeHelper, but since that's only temporary I I'm trying to find out why it's getting reverted back to have stack attach turned off (though it does have surface attach). Spoiler @PART[DavonStationLogisticsHub]:NEEDS[DavonSupplyMod]:AFTER[DavonSupplyMod] { @attachRules = 1,1,1,1,0 } I've tried using ":FINAL" and ":AFTER[zzzDavonPatches]" (my MM patch is in its' own folder), and it just keeps reverting the stack attach node to "0". I've also tried quite a few combos not listed here as well (I create a part off this mod that doubles the size but even there it brings over the attach rules regardless if I am over-writing them with the part patch or not). Anyone have an idea of why could be the cause? I'm about to clear my MM cache and run it again and see if that fixes it, otherwise I can't think of one mod that would over-ride the attach rules, especially when the patches are being loaded as ":FINAL"... *Edit* I think I figured it out, the patch I was using was using some regex expression that was supposed to double the 6th node attachment value (rescale factor was doubled for this part), but it seemed to leave an extra "1" at the end, so instead of x,y,z,angx,angy,angz,scale it added an extra integer after the scale which may have messed with it. *Edit 2* Turns out I was wrong. I was able to get rid of that extra number, but now I have edited it so much I can't even get the nodes to scale correctly anymore. I have a working rescaled patch for some hub connectors that I have no issue with, but it looks like setting the rescale factor seems to also scale the other nodes on this part (after changing around some MM patch variables it started doubling the doubled attachment node parts), but it still just will not allow stacking on the bottom (for whatever reason it will stack on the top node no problem, but I have to use NodeHelper to enable stacking in the VAB to get it to stack on the bottom node). I just created another testbench folder to bypass the 15 minute load so I'll be testing it there but if anyone has any ideas I'd appreciate it... Edited October 5, 2022 by shoe7ess Quote Link to comment Share on other sites More sharing options...
bigyihsuan Posted October 11, 2022 Share Posted October 11, 2022 I'm looking to scale the min and max dimensions of all Procedural Parts by some factor MIN_SCALE and MAX_SCALE (for a 10x rescale system I'm playing). I want this to work on all parts' starting size limits, as well as all part upgrades. Here is what I have so far. Does this look correct (enough)? @PART[*]:FINAL // all parts... { @MODULE[ProceduralPart] // with the procedural parts module... { // set constants %MIN_SCALE = 2 %MAX_SCALE = 5 // change the mins and maxes @diameterMin /= #$MIN_SCALE$ @diameterMax *= #$MAX_SCALE$ @lengthMin /= #$MIN_SCALE$ @lengthMax *= #$MAX_SCALE$ @volumeMin /= #$MIN_SCALE$ @volumeMax *= #$MAX_SCALE$ @UPGRADES // pick the upgrades... { @UPGRADE:HAS[#diameterMax] // that has this field { // modify the value @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#diameterMin] { @diameterMin /= #$../MIN_SCALE$ } @UPGRADE:HAS[#lengthMax] { @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#lengthMin] { @diameterMin /= #$../MIN_SCALE$ } @UPGRADE:HAS[#volumeMax] { @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#volumeMin] { @diameterMin /= #$../MIN_SCALE$ } } } } Quote Link to comment Share on other sites More sharing options...
SkyFall2489 Posted October 11, 2022 Author Share Posted October 11, 2022 46 minutes ago, bigyihsuan said: I'm looking to scale the min and max dimensions of all Procedural Parts by some factor MIN_SCALE and MAX_SCALE (for a 10x rescale system I'm playing). I want this to work on all parts' starting size limits, as well as all part upgrades. Here is what I have so far. Does this look correct (enough)? @PART[*]:FINAL // all parts... { @MODULE[ProceduralPart] // with the procedural parts module... { // set constants %MIN_SCALE = 2 %MAX_SCALE = 5 // change the mins and maxes @diameterMin /= #$MIN_SCALE$ @diameterMax *= #$MAX_SCALE$ @lengthMin /= #$MIN_SCALE$ @lengthMax *= #$MAX_SCALE$ @volumeMin /= #$MIN_SCALE$ @volumeMax *= #$MAX_SCALE$ @UPGRADES // pick the upgrades... { @UPGRADE:HAS[#diameterMax] // that has this field { // modify the value @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#diameterMin] { @diameterMin /= #$../MIN_SCALE$ } @UPGRADE:HAS[#lengthMax] { @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#lengthMin] { @diameterMin /= #$../MIN_SCALE$ } @UPGRADE:HAS[#volumeMax] { @diameterMax *= #$../MAX_SCALE$ } @UPGRADE:HAS[#volumeMin] { @diameterMin /= #$../MIN_SCALE$ } } } } There might be multiple upgrades with the fields you are looking for. This will only select the first of those. Just put a comma, then a space, then an asterisk after the HAS node, like this: @UPRGADE:HAS[#diameterMax], * On 8/15/2022 at 1:04 AM, spaded1 said: Hey guys I'm trying to make a mod to make the cost of everything more real. So falcon 9 should cost 100mil to fly. Satellite contracts should pay 25mil not 25k. Extracting ore and landing should reward with millions in dollars. Here is my code - but the contracts don't seem to reflect the changes at all.... Can anyone help me out? ``` @CONTRACT_TYPE[*]:HAS[@rewardFunds]:FINAL { cash= $rewardFunds @rewardFunds = 200*cash } @Contracts:HAS[@Funds]:FINAL { cash= $rewardFunds @rewardFunds = 70*cash @Funds { @BaseReward *= 70 @BaseAdvance *= 70 } @BaseReward *= 70 @BaseAdvance *= 70 } @Contracts:HAS[*,@Funds]:FINAL { cash= $rewardFunds @rewardFunds = 200*cash @Funds { @BaseReward *= 70 @BaseAdvance *= 70 } @BaseReward *= 70 @BaseAdvance *= 70 } @PART[*] { @cost *= 150.0 } @RESOURCE_DEFINITION[*] { @unitCost *= 150.0 } The issue is how you are selecting the variables. The syntax is kinda overcomplicated, check out some examples. Also, you can just say rewardFunds *= 200 on line 4 Quote Link to comment Share on other sites More sharing options...
Guest Posted October 14, 2022 Share Posted October 14, 2022 Question: Is there a way to mass clone all parts (Or fueltanks, engines, etc) similar to using "+part(...)"? Quote Link to comment Share on other sites More sharing options...
SkyFall2489 Posted October 23, 2022 Author Share Posted October 23, 2022 On 10/14/2022 at 2:25 PM, Forked Camphor said: Question: Is there a way to mass clone all parts (Or fueltanks, engines, etc) similar to using "+part(...)"? Well, thing is, names. It's totally doable if you can figure that out. Every part needs an unique vale in its name field. (NAME, not TITLE, NAME is what the game uses, TITLE is displayed to the player) So, you'd need to figure out that wierd regular expression thingy to modify every part name into a new part name that is not the old name, nor the name of any other part, such as by appending to the string. I'm not entirely sure how to do that, but I think it's possible. Go look at MoarKerbals, it appends a string to the description of several parts using a single patch. Quote Link to comment Share on other sites More sharing options...
Clarathecat Posted October 29, 2022 Share Posted October 29, 2022 I'm trying to make a patch that adds a different amount of usi wolf system crew capacity to each deployable centrifuge part of a given deployed crew capacity, Like, parts with 1 deployed crew capacity get 1 wolf crew capacity, but parts with 2 deployed crew capacity get 2 wolf crew capacity. Basically, I'm trying to select parts that have a specific module that has a specific variable of a specific value. Quote Link to comment Share on other sites More sharing options...
SkyFall2489 Posted October 30, 2022 Author Share Posted October 30, 2022 5 hours ago, Clancythecat said: I'm trying to make a patch that adds a different amount of usi wolf system crew capacity to each deployable centrifuge part of a given deployed crew capacity, Like, parts with 1 deployed crew capacity get 1 wolf crew capacity, but parts with 2 deployed crew capacity get 2 wolf crew capacity. Basically, I'm trying to select parts that have a specific module that has a specific variable of a specific value. Actually, you don't exactly need that. Check out the Variable system. It will let you just set one field to another. What does each part module look like? Then, I can show you how it works. If you do need to select keys of certain values within nodes, you can say @PART[*]:HAS[#MODULE[DeployableCentrifuge]:HAS[#deployedCapacity[xyz]]] Quote Link to comment Share on other sites More sharing options...
Clarathecat Posted October 30, 2022 Share Posted October 30, 2022 1 hour ago, SkyFall2489 said: Actually, you don't exactly need that. Check out the Variable system. It will let you just set one field to another. What does each part module look like? Then, I can show you how it works. If you do need to select keys of certain values within nodes, you can say @PART[*]:HAS[#MODULE[DeployableCentrifuge]:HAS[#deployedCapacity[xyz]]] The module looks like this MODULE { name = WOLF_CrewCargoModule EconomyBerths = 2 LuxuryBerths = 1 ModuleName = #LOC_USI_WOLF_CrewCargoModuleName PartInfo = #LOC_USI_WOLF_CrewCargoCrate_PartInfo_Summary } i'm trying to give them 1 EconomyBerths for every deployedCapacity, and half as many LuxuryBerths Quote Link to comment Share on other sites More sharing options...
SkyFall2489 Posted October 30, 2022 Author Share Posted October 30, 2022 20 minutes ago, Clancythecat said: The module looks like this MODULE { name = WOLF_CrewCargoModule EconomyBerths = 2 LuxuryBerths = 1 ModuleName = #LOC_USI_WOLF_CrewCargoModuleName PartInfo = #LOC_USI_WOLF_CrewCargoCrate_PartInfo_Summary } i'm trying to give them 1 EconomyBerths for every deployedCapacity, and half as many LuxuryBerths What about the deployable centrifuge module? Quote Link to comment Share on other sites More sharing options...
Clarathecat Posted October 30, 2022 Share Posted October 30, 2022 2 hours ago, SkyFall2489 said: What about the deployable centrifuge module? it looks like this MODULE { name = ModuleDeployableCentrifuge DeployAnimationName = CentrifugeCollapse // Speed of the deploy animation AnimationSpeed = 0.025 // Layer of the deploy animation AnimationLayer = 1 // Crew needed to deploy CrewToDeploy = 3 // Skill Required CrewSkillNeeded = #autoLOC_500103 // Skill Display Name CrewSkillNeededName = #autoLOC_500103 // Crew capacity when deployed DeployedCrewCapacity = 16 Deployed = True // Radius, for display purposes only Radius = 15 // Name of the deploy action DeployActionName = #LOC_SSPX_Inflatable_Action_Deploy_Start_Title // Name of the retract action RetractActionName = #LOC_SSPX_Inflatable_Action_Deploy_Stop_Title // Name of the toggle action ToggleActionName = #LOC_SSPX_Inflatable_Action_Deploy_Toggle_Title // Name of the start action StartSpinActionName = #LOC_SSPX_Deployable_Action_Spin_Start_Title // Name of the stop action StopSpinActionName = #LOC_SSPX_Deployable_Action_Spin_Stop_Title // Name of the toggle action ToggleSpinActionName = #LOC_SSPX_Deployable_Action_Spin_Toggle_Title // Speed of the centrifuge rotation in deg/s SpinRate = -35 // Rate at which the SpinRate accelerates (deg/s/s) SpinAccelerationRate = 1.0 // Rate at which the counterweight spins in deg/s, typically faster than SpinRate and reversed CounterweightSpinRate = 70.0 // Rate at which the counterweight accelerates (deg/s/s) CounterweightSpinAccelerationRate = 2.0 // Transform to rotate for the centrifuge SpinTransformName = B_SpinCore // Transform to rotate for the counterweight CounterweightTransformName = B_Counterweight InternalSpinMapping = 1 } Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.