Skip to content

YAML Tweaks

Pavel Siberx edited this page Aug 9, 2023 · 9 revisions

Introduction

TweakXL supports YAML based format to define TweakDB data.

Tweak files must have a .yaml or .yml extension, be placed in the <Cyberpunk 2077>/r6/tweaks directory, and can be organized using subdirectories.

At the top level, a tweak file defines flats and records.

# Flats
FreeCameraParams.baseSpeed: 10.0
timeSystem.netrunnerBreach.easeInCurve: DiveEaseIn

# Record
Items.Preset_Katana_Default:
  quality: Quality.Random
  mass: 20.0

Flats

Flats can be scalars, structures or arrays.

Editing flats

Assigning the value to flat is trivial, you just need its name and a compatible value:

PreventionSystem.setup.totalEntitiesLimit: 40

Creating flats

Using a name that doesn't exist, you can create new flats:

MySystem.triggerState: n"Combat"
MySystem.probabilities: [ 0.2, 0.5, 0.3 ]

You must use an explicit value format to specify the type of the new flat.

Records

Editing records

To change the properties of a record, you need to use record name to refer it, and assign values to the individual properties:

Vehicle.v_standard2_archer_bandit_player:
  trafficSuspension: Vehicle.TrafficSuspension_Sport
  enableDestruction: false

To change a single property of a record you can use a full property name:

Items.ReversePowerInductor.mass: 1.5

Creating records

To create a record you need to choose a non-existent name and use $type attribute to specify the type name:

Items.MyClothingItem:
  $type: Clothing
  entityName: my_item
  appearanceName: my_item_
  displayName: My-Item-Name

Unspecified properties will be filled with default values.

The type name can be in any of the following forms:

Form Value Comment
Base name Clothing Used in WolvenKit and REDmod.
Script name Clothing_Record Used in redscript.
Native name gamedataClothing_Record Used in CET and RED4ext.

Cloning records

For complex records, such as items or vehicles with hundreds of properties and relations, it can be very difficult to set up a record from scratch. In this case, it's more convenient to clone an already existing record.

To clone a record, you must use $base attribute to specify the name of the source record:

Items.MyClothingItem:
  $base: Items.GenericFaceClothing
  entityName: my_item
  appearanceName: my_item_
  displayName: My-Item-Name

Inline records

One way to work with related records is to define all the records separately and use record names to define relationships:

# Create UI data
Vehicle.v_my_archer_quartz_data:
  $type: VehicleUIData
  driveLayout: LocKey#45371
  horsepower: 250.0
  info: LocKey#53579
  mass: 1070.0
  productionYear: 2041-2077

# Create vehicle and associate UI data
Vehicle.v_my_archer_quartz:
  $base: Vehicle.v_standard2_archer_quartz_player
  vehicleUIData: Vehicle.v_my_archer_quartz_data

With inline syntax, you can combine both definitions into one:

Vehicle.v_my_archer_quartz:
  $base: Vehicle.v_standard2_archer_quartz_player
  vehicleUIData:
    driveLayout: LocKey#45371
    horsepower: 250.0
    info: LocKey#53579
    mass: 1070.0
    productionYear: 2041-2077

You can also use $base for inline records:

Vehicle.v_my_archer_quartz:
  $base: Vehicle.v_standard2_archer_quartz_player
  vehicleUIData:
    $base: Vehicle.v_standard2_archer_quartz_gt_inline0
    horsepower: 250.0

Usually you don't need to specify the type of the inline record with $type, because it's already known from the property type of the parent record. But you have to use it for polymorphic relationships:

Items.MyWeapon:
  $type: WeaponItem
  statModifiers:
    - $type: ConstantStatModifier
      statType: BaseStats.BaseDamage
      value: 50.0
    - $type: RandomStatModifier
      statType: BaseStats.BaseDamage
      min: 0.0
      max: 10.0

Explicit and implicit formats

Most types allow multiple formats for defining values, which can be divided into explicit and implicit formats.

The explicit format unambiguously defines the type of a value. It may be used in any context, and must be used in contexts where the expected value type cannot be determined.

This is an example of defining a flat of CName type using the explicit format:

Example.variable: CName("Value")

The result of this statement depends on what Example.variable is:

  • If it exists and is of CName type, then new value will be assigned.
  • If it exists and is of different type, an error will be thrown that value is not compatible.
  • If it doesn't exist, then new flat of type CName will be created.

The implicit format doesn't imply a value type. It can only be used when the expected type is known.

This is a similar example using the implicit format:

Example.variable: Value

This statement also depends on what Example.variable is but the result is different:

  • If it exists and is of CName type, then Value will be treated as a name CName("Value").
  • If it exists and is of String type, then Value will be treated as a string "Value".
  • If it doesn't exist, an error will be thrown that Value is ambiguous and the type cannot be resolved.

The benefit of the implicit format is simplicity. It's easier to read and edit when working with tweak files by hand.

Record properties are always implicit
When working with records you can always use the implicit format for properties, because all properties are predefined and the type of any property is known.

Scalars

Integer

Native Type: Int32

Value Mode Comment
220 Explicit Positive decimal integer.
-1 Explicit Negative decimal integer.
0xC0DE Explicit Hexadecimal integer.

Float

Native Type: Float

Value Mode Comment
50.0 Explicit Positive float.
-7.25 Explicit Negative float.
123 Implicit No decimal point: can be a float or an integer depending on the context.

Boolean

Native Type: Bool

Value Mode
true Explicit
false Explicit

String

Native Type: String

Value Mode Comment
"This is a string" Explicit Regular string.
"Quoted \\ backslash" Explicit Quoted strings require escaping.
This can be a string Implicit Unquoted text can be a string depending on the context.
Unquoted \ backslash Implicit Unquoted strings don't support escape sequences.

CName

Native Type: CName

Value Mode Comment
n"Thing" Explicit Redscript style.
CName("Thing") Explicit CET style.
None Explicit Special value to define empty name.
Thing Implicit Unquoted text can be a name depending on the context.

Localization Key

Native Type: gamedataLocKeyWrapper

Value Mode Comment
l"Secondary-Loc-Key" Explicit Redscript style (not supported by redscript).
LocKey("Secondary-Loc-Key") Explicit CET style using secondary key.
LocKey(12345) Explicit CET style using primary key.
LocKey#Secondary-Loc-Key Explicit REDengine style using secondary key.
LocKey#12345 Explicit REDengine style using primary key.
Secondary-Loc-Key Implicit Unquoted text can be a key depending on the context.
12345 Implicit Unquoted integer can be a key depending on the context.

Resource Reference

Native Type: raRef:CResource

Value Mode Comment
r"path\to\resource.ext" Explicit Redscript style.
ResRef("path\to\resource.ext") Explicit CET style using path.
ResRef(123456789) Explicit CET style using hash.
path\to\resource.ext Implicit Unquoted text can be a path depending on the context.
123456789 Implicit Unquoted integer can be a hash depending on the context.

Resource paths
The path can use backslashes \, double backslashes \\, or slashes /.
For example, all of these paths are valid and equal:

  • path\to\resource.ext
  • path\\to\\resource.ext
  • path/to/resource.ext

Foreign Key

Native Type: TweakDBID

Value Mode Comment
t"Package.Item" Explicit Redscript style.
TweakDBID("Package.Item") Explicit CET style.
<TDBID:12AB56CD:1F> Explicit Debug style using hash value.
None Explicit Special value to define empty ID.
Package.Item Implicit Unquoted text can be an ID depending on the context.

Structures

Structures can be defined in one line using curly braces:

Example.struct: { foo: 1, bar: 2 }

Or multiline:

Example.struct:
  foo: 1
  bar: 2

In an implicit mode, when the type is known, you can omit selected or all fields. Omitted fields will have default values.

Quaternion

Native Type: Quaternion
Field Type: Float

Value Mode Comment
{ i: 0.0, j: 0.0, k: -0.9, r: 0.4 } Explicit Allowed in any context.
{ k: 0.1, r: 0.2 } Implicit Allowed when the value type is known.

Euler Angles

Native Type: EulerAngles
Field Type: Float

Value Mode Comment
{ roll: 30.0, pitch: 90.0, yaw: 0.0 } Explicit Allowed in any context.
{ yaw: 150.0 } Implicit Allowed when the value type is known.

Vector3

Native Type: Vector3
Field Type: Float

Value Mode Comment
{ x: 0.5, y: -0.8, z: 0.0 } Explicit Allowed in any context.
{ z: 2.0 } Implicit Allowed when the value type is known..

Vector2

Native Type: Vector2
Field Type: Float

Value Mode Comment
{ x: 20.0, y: 40.0 } Explicit Allowed in any context.
{ x: 250.0 } Implicit Allowed when the value type is known.

Color

Native Type: Color
Field Type: Uint8

Value Mode Comment
{ red: 51, green: 102, blue: 0, alpha: 255 } Explicit Allowed in any context.
{ red: 255, alpha: 127 } Implicit Allowed when the value type is known.

Arrays

Arrays can be defined in one line using brackets:

Example.array: [ First, Second, Third ]

Or multiline:

Example.array:
  - First 
  - Second
  - Third

Element types

Array can be of any scalar or structure type. Arrays are homogeneous, and the element type is defined by the first element.

Value Mode Comment
[ 1.0, 2.0, 3.0 ] Explicit Array of floats.
[ { x: 0.5, y: 0.5 } ] Explicit Array of Vector2 structures.
[ n"First", n"Second" ] Explicit Array of names.
[ First, Second, Third ] Implicit Can be array of strings, names, lockeys depending on the context.
[] Implicit An empty array with the actual type determined from the context.

Array operations

Arrays can be mutated instead of replaced.

Adding elements

Syntax: !append {value} !prepend {value}

Adds an element to the end or beginning of target array.

Vehicle.vehicle_list.list:
  - !prepend Vehicle.v_012_corpo_suv
  - !append Vehicle.v_standard25_villefort_columbus_corpo

Adding unique elements

Syntax: !append-once {value} !prepend-once {value}

Adds an element to the end or beginning of target array if it doesn't exist.

Vehicle.vehicle_list.list:
  - !append-once Vehicle.v_standard2_villefort_cortes_police

Merging arrays

Syntax: !append-from {array} !prepend-from {array}

Add all elements from another array to the end or beginning of target array, discarding duplicates. The source array can be any flat or record property.

Items.RiflePossibleScopesList.itemPartList:
  - !append-from Items.SniperPossibleScopesList.itemPartList

Removing elements

Syntax: !remove {value}

Remove an element from target array.

Vehicle.vehicle_list.list:
  - !remove Vehicle.v_sport1_herrera_outlaw_player

Replacing elements

By combining removals and additions, you can effectively replace values:

Items.w_silencer_01:
  statModifiers:
    # Remove original modifier for StealthHitDamageMultiplier
    - !remove Items.w_silencer_01_inline0
    # Add new modifier with desired values
    - !append
      $type: ConstantStatModifier
      statType: BaseStats.StealthHitDamageMultiplier
      modifierType: Additive
      value: 3.0

Inheritance

The new record inherits properties with already filled arrays. Array operations can be used to modify inherited arrays. These modifications can also be inherited:

# Get stats from base ashura with extra iconic modifer
Items.Weapon_A:
  $base: Items.Base_Ashura
  statModifiers:
    - !append Quality.IconicItem

# Get stats from Weapon_A with extra recovery modifer
Items.Weapon_B:
  $base: Items.Weapon_A
  statModifiers: 
    - !append
      $type: ConstantStatModifier
      statType: BaseStats.RecoilRecoveryTime
      modifierType: Additive
      value: -0.15

# Get stats from Weapon_B but without iconic modifier from Weapon_A
Items.Weapon_C:
  $base: Items.Weapon_B
  statModifiers:
    - !remove Quality.IconicItem

Anchors and aliases

You can use anchors to repeat some definitions without the need for copy-paste:

# Define item addition for one vendor
Vendors.wat_lch_clothingshop_01: &AddToVendor
  itemStock:
    - !append
      item: Items.MyClothing
      quantity: [ Vendors.IsPresent ]

# Repeat for other vendors
Vendors.wat_kab_clothingshop_01: *AddToVendor
Vendors.wat_nid_clothingshop_01: *AddToVendor
Vendors.wbr_jpn_clothingshop_01: *AddToVendor

Templates

To repeat one definition multiple times you must set $instances attribute to an array of data for each copy and use placeholders for data substitutions:

Items.my_item_$(color):
  $instances:
    - { color: black, icon: slot_1 }
    - { color: white, icon: slot_2 }
    - { color: red, icon: slot_3 }
  $base: Items.GenericHeadClothing
  entityName: my_item
  appearanceName: my_item!$(color)
  displayName: my_item_$(color)_name
  icon:
    atlasResourcePath: mod\icons\my_item.inkatlas
    atlasPartName: $(icon)

Templates can be used only at the top level of tweak file.

Magic tweaks

Localization keys

While using a specialized type for one half of the data, the game also uses plain strings like LocKey#1234 for the other half. Originally you have to keep in mind the type of property to select the appropriate format, and can only use numeric primary keys, which are very difficult to maintain and error prone.

TweakXL allows you to use any localization key explicit format regardless of property type.

Item icons

Item records use an iconPath string property to refer to the icon. This is an example of how icon must be defined originally:

# Icon must have UIIcon. prefix
UIIcon.MyItemIcon:
  atlasResourcePath: mod\icons.inkatlas
  atlasPartName: icon_my_item

Items.MyItem:
  # Must use only the part after UIIcon. prefix
  iconPath: MyItemIcon 

TweakXL simplifies this using inline syntax:

Items.MyItem:
  icon: # This property is not used by the game originally
    atlasResourcePath: mod\icons.inkatlas
    atlasPartName: icon_my_item

TweakXL will create a UIIcon record, generate a correct name, and fill iconPath property. So you don't have to define an extra record for each item and maintain an extra set of names.