Patch File Unification

Unifying (merging) patch files into one patch file for each .uasset using .toml or .toml.all

The .toml and .toml.all commands combine all of your patch files into a single, self-contained TOML patch file per .uasset. This is useful for sharing, inspecting, or further editing your patches as one file.

The .toml command merges all patch files in the patches directory and creates “unified” TOML patch files. The unification creates a single patch file for all the merged patch files for each .uasset, with all data table regular expressions and patchlets applied, producing plain TOML patches. Non-data table .@: patches are copied as they are, but with their load order embedded between .@: and / or $ to preserve their original sequence of applications.

For example, for the following patches structure:

patches-structure2

where:

  • patches\beta-burst-recovery-scan\EffectTable.toml:

    # based on https://www.nexusmods.com/stellarblade/mods/802
    [N_Drone_Scan]
    LifeTime = 30.0
    
    # based on https://www.nexusmods.com/stellarblade/mods/897
    ['.*: ^P_Eve_SkillTree_Just(.*BetaGauge|.*BurstGauge).*$']   
    CalculationMultipleValue = '''=> v.objName match {
    case "P_Eve_SkillTree_JustParry_BetaGauge1" => 8
    case "P_Eve_SkillTree_JustParry_BetaGauge2" => 6
    case "P_Eve_SkillTree_JustEvade_BurstGauge1" => 4
    case "P_Eve_SkillTree_JustEvade_BurstGauge2" => 3
    }'''
    OverlapCount = 1
    LifeTime = 11
    StartDelayTime = 1
    LoopIntervalTime = 1
    ActiveTargetFilterAlias = 'Self'
    LoopTargetFilterAlias = 'Self'
    
  • patches\EffectTable.toml:

    ['.*: ^Gear_BetaCrystalAdditiveRate_.*$']
    StatType = "ActorStatType_DrainHpByAttackPowerRate"
    StatCalculationType = "EffectCalculationType_Static"
    CalculationValue = '''=> {
    val prefix = "Gear_BetaCrystalAdditiveRate_"
    var n = 0.003 * v.objName(prefix.length).toDouble
    if (v.objName.contains("MK2")) n = n * 2
    n
    }'''
    LifeType = "EffectLifeType_Infinite"
    LifeTime = 0
    
  • patches\RecoveryCacheZone.toml:

    ['.@: $..[?(@.Name == "ZoneBoundPadOverrides" && @.StructType == "Vector")]']
    ZoneBoundPadOverrides = '''=> {
    val orig = v.orig[Map[String, Any]]
    orig + ("Z" -> -1999d)
    }'''
    
  • patches\TargetFilterTable.toml:

    ['.@: $..[?(@.Name =~ /N_Drone_Normal_Scan1_1_Target.*/)]']
    FarDistance = 30000
    TargetCheckValue1 = 3000
    

.toml produces 3 patch files:

  • EffectTable.toml:

    # ...                                                        # Game Original Value
    [Gear_BetaCrystalAdditiveRate_1]
    CalculationValue = 0.147                                     # 0.4000000059604645
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [Gear_BetaCrystalAdditiveRate_1_MK2]
    CalculationValue = 0.294                                     # 0.6000000238418579
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [Gear_BetaCrystalAdditiveRate_2]
    CalculationValue = 0.15                                      # 0.699999988079071
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [Gear_BetaCrystalAdditiveRate_2_MK2]
    CalculationValue = 0.3                                       # 1.0499999523162842
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [Gear_BetaCrystalAdditiveRate_3]
    CalculationValue = 0.153                                     # 1.0
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [Gear_BetaCrystalAdditiveRate_3_MK2]
    CalculationValue = 0.306                                     # 1.5
    LifeTime = 0                                                 # "+0"
    LifeType = "EffectLifeType_Infinite"                         # "EffectLifeType_Infinite"
    StatCalculationType = "EffectCalculationType_Static"         # "EffectCalculationType_Static"
    StatType = "ActorStatType_DrainHpByAttackPowerRate"          # "ActorStatType_BetaCrystalAdditiveRate"
    
    [N_Drone_Scan]
    LifeTime = 30.0                                              # 3.5
    
    [P_Eve_SkillTree_JustEvade_BurstGauge1]
    ActiveTargetFilterAlias = "Self"                             # "Self"
    CalculationMultipleValue = 4                                 # 1.0
    LifeTime = 11                                                # 1.0
    LoopIntervalTime = 1                                         # "+0"
    LoopTargetFilterAlias = "Self"                               # 'null'
    OverlapCount = 1                                             # 0
    StartDelayTime = 1                                           # "+0"
    
    [P_Eve_SkillTree_JustEvade_BurstGauge2]
    ActiveTargetFilterAlias = "Self"                             # "Self"
    CalculationMultipleValue = 3                                 # 1.0
    LifeTime = 11                                                # 1.0
    LoopIntervalTime = 1                                         # "+0"
    LoopTargetFilterAlias = "Self"                               # 'null'
    OverlapCount = 1                                             # 0
    StartDelayTime = 1                                           # "+0"
    
    [P_Eve_SkillTree_JustParry_BetaGauge1]
    ActiveTargetFilterAlias = "Self"                             # "Self"
    CalculationMultipleValue = 8                                 # 1.0
    LifeTime = 11                                                # 1.0
    LoopIntervalTime = 1                                         # "+0"
    LoopTargetFilterAlias = "Self"                               # 'null'
    OverlapCount = 1                                             # 0
    StartDelayTime = 1                                           # "+0"
    
    [P_Eve_SkillTree_JustParry_BetaGauge2]
    ActiveTargetFilterAlias = "Self"                             # "Self"
    CalculationMultipleValue = 6                                 # 1.0
    LifeTime = 11                                                # 1.0
    LoopIntervalTime = 1                                         # "+0"
    LoopTargetFilterAlias = "Self"                               # 'null'
    OverlapCount = 1                                             # 0
    StartDelayTime = 1                                           # "+0"
    
  • RecoveryCacheZone.toml:

    # ...                                                        # Game Original Value
    ['.@: #2 $..[?(@.Name == "ZoneBoundPadOverrides" && @.StructType == "Vector")]']
    ZoneBoundPadOverrides = '''=> {
      val orig = v.orig[Map[String, Any]]
      orig + ("Z" -> -1999d)
    }'''
    
  • TargetFilterTable.toml:

    # ...                                                        # Game Original Value
    [N_Drone_Normal_Scan1_1_Target_Enemy]
    FarDistance = 30000                                          # 10000.0
    TargetCheckValue1 = 3000                                     # 1000.0
    
    [N_Drone_Normal_Scan1_1_Target_Owner]
    FarDistance = 30000                                          # 10000.0
    TargetCheckValue1 = 3000                                     # 500.0
    

Code patches are automod’s custom scripting support: every .uasset name listed in uassetNames is delegated to your custom patch method in patchCustom.sc instead of being processed from TOML patch files (see Advanced Script Customizations). .toml merges the TOML patch files before these code patches are applied, while .toml.all is similar to .toml, except that it merges the TOML patch files after the code patches have been applied, so the unified output also reflects the changes made by your code patches.